为什么Digest gem不能将所有类都显示为常量?

时间:2019-07-31 18:42:37

标签: ruby hash

我正在使用摘要模块执行某些任务。

和往常一样,ruby-doc有一个非常好的文档。

当我想从模块或类中获取类时:

module A
    class B end
    C = Class.new(B)
    D ||= 1
end
A.const_set(:E, Math::E)

p A.constants                                                  # => [:B, :C, :D, :E]
p A.constants.select { |x| A.const_get(x).kind_of?(Class) }    # => [:B, :C]

但是在摘要模块中,发生了一些荒谬的事情:

require 'digest'      # => true

p Digest.constants    # => [:Class, :Base, :REQUIRE_MUTEX, :Instance]
p Digest::SHA1        # => Digest::SHA1
p Digest.constants    # => [:Class, :Base, :SHA1, :REQUIRE_MUTEX, :Instance]
p Digest::SHA2        # => Digest::SHA2
p Digest.constants    # => [:Class, :SHA2, :Base, :SHA1, :SHA256, :REQUIRE_MUTEX, :SHA512, :SHA384, :Instance]

正如您所看到的,常量方法不会返回SHA1类(在第2行)。然后,当我编写Digest::SHA1时,程序将加载SHA1类。然后,当您再次运行调用constants方法时,它将返回数组中的SHA1

这是怎么回事?有没有一种方法可以不以相同的方式加载程序中的所有类?这是一个有效的设计吗?

2 个答案:

答案 0 :(得分:2)

对于Digest,此行为是故意的。根据哈希算法,需要不同的库。这是违反直觉的,但也是预期的行为,如您在此处看到的:https://github.com/ruby/ruby/blob/master/ext/digest/lib/digest.rb#L8-L25

要改变这一点,Ruby Core需要相应地采用。 希望对您有所帮助。

答案 1 :(得分:0)

魔术是摘要模块中的const_missing单例方法。 @knugje在他的答案中指出了一个非常有用的链接,使链接更加清晰。

关于const_missing

ri文档说:

  

在mod中引用未定义的常量时调用

这就是窍门:

module X
    def self.const_missing(name)
        name
    end
end

p X.constants        # => []
p X::Y               # => :Y
p X.constants        # => []

因此,代码未在模块中定义常量。因此,我们可以使用一个非常简单的技巧:

module X
    define_singleton_method(:const_missing) { |n| const_set(n, n.id2name) }
end

p X.constants      # => []
p X::Y             # => "Y"
p X::Z             # => "Z"
p X.constants      # => [:Y, :Z]
p X::Z             # "Z"

这里就是使用const_missing的方式。但是摘要不是const_set,而是使用名称加载文件并进行一些异常处理并检查文件中是否存在常量。

就个人而言,这可能会造成混乱。例如,当您没有离线和在线文档的文档时,就不能简单地运行Digest.constants列出可用的常量。

knugie指出,您可以在Digest模块here中阅读有关const_missing实现的信息。