Ruby 的常量查找路徑問題是一直困擾我的一個問題,在工作中遇到過好幾次,一直沒有徹底弄清楚到底為什么,最近在讀一本書《Ruby 元編程》,對 Ruby 對象模型有了更深入的認識,另外讀了一篇 blog《Everything you ever wanted to know about constant lookup in Ruby》, 讓我總算把 Ruby 常量查找路徑這個問題搞得比較清楚。
第一個遇到的問題,我還曾經在 Ruby-China 上發過帖。
代碼如下:
module M1
CT = "ok"
end
class C1
CK = "ck"
include M1
def self.method1
puts self
puts "#{CK} in method1"
puts "#{CT} in method1"
end
class << self
def method2
puts self
puts "#{CK} in method1"
puts "#{CT} in method2"
end
end
end
C1.method1
C1.method2
輸出結果是
代碼如下:
C1
ck in method1
ok in method1
C1
ck in method2
NameError: uninitialized constant Class::CT
from (irb):16:in `method2'
這是我在重構薄荷網代碼時候遇到的問題,method1 和 method2 都是常見的類方法的定義方面,我向來認為它們是等價可替換的寫法,但是從實際執行的結果看,它們里面的常量查找路徑不一樣。
如果我把 M1 的定義改成下面的樣子:
代碼如下:
module M1
def self.included(base)
base.extend(self)
end
CT = "ok"
end
執行結果是:
代碼如下:
C1
ck in method1
ok in method1
C1
ck in method2
ok in method2
還有一個問題是也是經常遇到的,抽象成問題代碼如下:
代碼如下:
module A
module M
def a_method
#...
end
end
end
class A::B
include M
end
會報異常:
代碼如下:
NameError: uninitialized constant A::B::M
from (irb):10:in `<class:B>'
Ruby 常量查找時依據兩條路徑
A. Module.nesting
B. open class/module 的 ancestors
A 比 B 優先,A 找不到了才到 B 中查找。
A.Module.nesting 的概念比較容易理解,它是指代碼位置的 module 嵌套情況,它是一個數組,從最內層的嵌套一直到最外層的嵌套,如果沒有嵌套,數組為空。任何一處代碼位置都有 Module.nesting 值,可以通過下面的代碼打印出各個位置的 Module.nesting 值。
代碼如下:
p Module.nesting
module A
module B
新聞熱點
疑難解答