为什么Ruby class_eval从范围中删除常量?

时间:2016-05-10 14:51:17

标签: ruby

我用一个常数&方法:

class A
  FOO = 'hello'
  def bar
    puts FOO
  end
end

A.new.bar
=> 'hello'

一切都按预期工作。但是,当我这样做时:

A.class_eval do
  def bar
    puts FOO
  end
end

A.new.bar
NameError: uninitialized constant FOO

奇怪......为了解决这个问题,我正在做:

A.class_eval do
  def bar
    puts self.class::FOO
  end
end

为什么会出现这种情况有什么好的解释?

1 个答案:

答案 0 :(得分:5)

查找常量

    在词汇封闭模块声明中
  1. 向外
  2. 向上在当前模块声明的继承链中
  3. 所以,让我们做Ruby做的事情:

    1. 在词汇封闭的模块声明中向外看:容易 - 没有模块声明
    2. 从当前的模块声明向上看:再次,没有当前的模块声明......或者是否存在?好吧,如果没有模块声明,则隐含地假定它是class Object - 但Object没有名为FOO的常量,并且它的祖先KernelBasicObject
    3. Ergo:Ruby是对的。常量查找路径中没有名为FOO的常量。它没有从范围中删除FOO,它从来没有开始。

      [Ruby的反射API实际上可以让您访问所需的任何内容:#1与Module.nesting完全相同,而#2(几乎)与Module.nesting.first.ancestors相同。]

      您可能会想:等待,不是module_eval模块声明吗?不,不是!它就像任何其他方法一样,采用与任何其他块一样的块。它不会以任何方式改变常量查找规则。

      请注意,这并不完全正确:毕竟,例如,例如,instance_eval 会更改方法查找规则,因此module_eval更改常量查找规则是不可想象的。更令人困惑的是,当用String而不是调用时,它实际上 更改了Module.nesting,从而改变了常量查找规则!< / p>