2016-05-10 55 views
4

我做了一類具有恆定&的方法:爲什麼Ruby class_eval從範圍中刪除常量?

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 

爲什麼會這樣有什麼好解釋?

回答

5

常量在詞法封裝模塊的聲明在當前模塊聲明

這樣的繼承鏈擡頭

  1. 向外
  2. 向上,就讓我們做什麼Ruby確實:

    1. 向外看在詞法封裝模塊聲明:易 - 有沒有模塊聲明
    2. 從目前的模塊聲明看起來向上:再次,沒有電流模塊聲明...還是有?那麼,如果沒有模塊聲明,則隱含地假定爲class Object - 但是Object不具有名爲FOO的常數,其祖先KernelBasicObject也不是。

    Ergo:Ruby是對的。在常量查找路徑中沒有常數名爲FOO。它並沒有從範圍中刪除FOO,它從來沒有在範圍內開始。

    [Ruby的反射API實際上,您可以訪問任何你需要:#1是完全一樣Module.nesting和#2是(幾乎)一樣Module.nesting.first.ancestors]

    你可能會想:等待,ISN」 t module_eval模塊聲明?不,它不是!這只是一種像其他任何方法一樣的方法,就像任何其他塊一樣。它不會以任何方式改變常量查找規則。

    請注意,這並非完全正確:畢竟, instance_eval確實是更改方法查找規則,例如,所以module_eval更改常數查找規則並不是不可思議的。而更令人困惑的是,當用String而不是調用時,它實際上是確實更改爲Module.nesting,因此不斷查找規則!