如何通过反射获取Ruby的Module类定义的常量?

45
我试图理解Matz和Flanagan的“Ruby编程语言”元编程章节,但是我无法理解我构想的以下代码片段的输出:
p Module.constants.length           # => 88
$snapshot1 = Module.constants       
class A
  NAME=:abc

  $snapshot2 = Module.constants
  p $snapshot2.length               # => 90
  p $snapshot2 - $snapshot1         # => ["A", "NAME"]

end
p Module.constants.length           # => 89
p Module.constants - $snapshot1     # => ["A"]
p A.constants                       # => ["NAME"]

这本书说明了类方法constants会返回该类的常量列表(就像你可以在A.constants的输出中看到的那样)。

当我试图获取Module类定义的常量列表时,遇到了上述奇怪的行为。

A的常量出现在Module.constants中。如何获取由Module类定义的常量列表?

文档中指出:

Module.constants返回系统中所有定义的常量,包括所有类名和方法名

由于A继承自Module.constants的实现方式,所以在基类和派生类型中如何表现不同?

p A.class               # => Class
p A.class.ancestors       # => [Class, Module, Object, Kernel]

注意:如果您正在使用Ruby 1.9,constants会返回一个由符号组成的数组而不是字符串。


注意:若使用 Ruby 1.9,则 constants 回传之为符号(Symbol)组成的阵列而非字串(String)。

1
我回答了你的问题吗?我问这个是因为我的答案没有被“接受”,但是我并没有要求任何额外的信息... - Marc-André Lafortune
@Marc - 你的回答引发了我更多的问题和更多的涂改。这一周我都在努力理解方法解析顺序,虽然我依然不太清楚可能性是什么,但我认为我已经90%确定它的工作方式了。请看下面的我的帖子。 - Gishu
2个回答

64

好问题!

您的困惑是因为类方法Module.constants隐藏了实例方法Module#constants,而它们都属于Module类。

在Ruby 1.9中,通过添加可选参数来解决了这个问题:

# No argument: same class method as in 1.8:
Module.constants         # ==> All constants
# One argument: uses the instance method:
Module.constants(true)   # ==> Constants of Module (and included modules)
Module.constants(false)  # ==> Constants of Module (only).

在你上面的示例中,A.constants 调用了 Module#constants(实例方法),而 Module.constants 则调用了 Module.constants

因此,在 Ruby 1.9 中,您需要调用 Module.constants(true)

在 Ruby 1.8 中,则可以调用 Module 上的实例方法 #constants。您需要获取实例方法并将其绑定为类方法(使用不同的名称):

class << Module
  define_method :constants_of_module, Module.instance_method(:constants)
end

# Now use this new class method:
class Module
   COOL = 42
end
Module.constants.include?("COOL")  # ==> false, as you mention
Module.constants_of_module         # ==> ["COOL"], the result you want

我希望我能够将1.9的功能完整地向后移植到我的backports gem中的1.8版本,但是在Ruby 1.8中,我无法想到一种只获取模块常量而排除继承常量的方法。

编辑:刚刚更正了官方文档以正确反映这一点...


@Marc - 非常感谢您能够审查一下我的方法解析理解,并确认一下它的正确性。请查看我下面的帖子中的博客文章链接。感谢并已接受 :) - Gishu

3
我在Marc的回复后又花了一些时间琢磨,修改了更多代码片段。最终,当Ruby的方法解析看起来有意义时,我将其写成了博客文章,以便我不会忘记。
注释:如果“A”A的特殊类
当调用A.constants时,方法解析(参见我的博客文章中的图像以获得视觉辅助)按顺序查找以下位置:
  • MyClass"Object"BasicObject"(单例方法)
  • Class(实例方法)
  • Module(实例方法)
  • Object(实例方法)和Kernel
  • BasicObject(实例方法)
Ruby找到了实例方法Module#constants 当调用Module.constants时,Ruby查看:
  • Module"Object"BasicObject"(单例方法)
  • Class(实例方法)
  • Module(实例方法)
  • Object(实例方法)和Kernel
  • BasicObject(实例方法)
这次,Ruby在Module".constants找到了单例/类方法,正如Marc所说。
Module定义了一个单例方法,它遮盖了实例方法。单例方法返回所有已知的常量,而实例方法返回当前类及其祖先中定义的常量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接