这是一个迷惑你的面条的问题。
class_eval
在使用类作为接收器进行评估的块中,方法声明和常量声明之间似乎存在差异。这是一些演示差异的代码:
module X
def save_block(&block)
@block = block
end
end
module Y
extend X
save_block do
SOME_CONSTANT = 1
def meth
"a method"
end
end
def self.included(m)
m.class_eval &@block
end
end
class Z
include Y
end
class W
include Y
end
至少在 Ruby 1.9.3 中执行此代码会导致以下错误:
warning: already initialized constant SOME_CONSTANT
这是我没想到的。首先考虑meth
位于哪里:
Z.instance_methods(false)
=> [:meth]
W.instance_methods(false)
=> [:meth]
并且Y
没有任何实例方法:
Y.instance_methods(false)
=> []
这是有道理的,因为该块是使用Z
并W
作为class_eval
. 但是,常量是不同的,因此会出现错误消息,如下所示:
Y.constants(false)
=> [:SOME_CONSTANT]
Z.constants(false)
=> []
W.constants(false)
=> []
在这里,常量最终被定义在Y
(出于某种奇怪的原因),因此当第二次执行块时,常量SOME_CONSTANT
已经定义了。
我非常期待一些启示。
更新(2012/11/19):
正如@phoet 在下面指出的(对我对他的回答的评论的回应),常量是在块的词法范围内定义的,也就是说,在最初定义块的范围内。在我的示例中,这将是 Y。