我有一些基于这个答案的代码: https: //stackoverflow.com/a/2136117/2158544。本质上它看起来像这样(注意:在实际代码中,我不控制模块A):
module A
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def singleton_test
end
end
end
module B
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def self.extended(base)
puts base.method(:singleton_test).owner
define_method(:singleton_test) do |*args, &blk|
super(*args, &blk)
end
end
end
end
第一个包含:
class C
include A
include B
end
A::ClassMethods # <- output
第二个包含:
class D
include A
include B
end
B::ClassMethods # <- output
尽管对 的调用super
仍然被正确路由到module A
,但我很困惑为什么singleton_test
当它被包含到class D
(owner is B::ClassMethods
) 中时已经“包装”了。我的理论是,这是因为当module B
重新定义时singleton_test
,它是在包含的模块级别(module A
)上重新定义它,因此每次module A
随后包含时,该方法已经被“包装”。
当你调用一个方法时,Ruby会沿着祖先链寻找该方法,如果它到达顶部(BasicObject)并且找不到匹配的方法,它将抛出一个错误(除非已定义
method_missing
)这在您的示例中如何工作
首次纳入
当你查找时,
singleton_test
它会检查祖先链singleton_test
现在在
B::ClassMethods::extended
钩子中,您正在singleton_test
使用进行定义define_method
。由于您在没有接收器的情况下调用了此方法,因此隐式接收器是self
并且self
在此方法的上下文中,B::ClassMethods
本质上您正在调用你可以更清楚地看到这一点
输出:
第二次纳入
我想你可以看到事情的发展方向
当你查找时,
singleton_test
它会检查祖先链singleton_test
因此,并不是说该方法已“已包装”,也不是该方法正在重新定义,而是
A
您已在 中定义了一个新方法B::ClassMethods
,因为在其定义优先B
后包含该方法(覆盖此上下文中的方法)。A
A