Andrey Deineko 的回答为了解正在发生的事情提供了重要基础。
当模块包含在类或其他模块中时会发生什么?有两件事似乎相关:
当这个模块包含在另一个模块中时,Ruby 会在这个模块中调用 append_features,并将其传递给 mod 中的接收模块。如果此模块尚未添加到 mod 或其祖先之一,则 Ruby 的默认实现是将此模块的常量、方法和模块变量添加到 mod。另请参阅模块#include。
每当接收器包含在另一个模块或类中时都会调用回调。如果您的代码想要在模块包含在另一个模块中时执行某些操作,则应优先使用此模块。
我们不能挂钩,append_features
但我们可以included
在我们的模块中定义。
module Inner
def self.included(base)
puts "including #{self} in #{base}"
end
end
module Outer
def self.included(base)
puts "including #{self} in #{base}"
base.send(:include, Inner)
end
end
module SecondOuter
include Inner
def self.included(base)
puts "including #{self} in #{base}"
end
end
class FirstClass
include Outer
end
class SecondClass
include SecondOuter
end
Outer 和 SecondOuter 的区别在于Inner
使用方式。在Outer
,Inner
中不包含,而仅包含在其他模块包含的任何内容Outer
中。然而SecondOuter
,包括在内Inner
。
将上述代码粘贴到控制台时,屏幕上会打印以下语句:
including Inner in SecondOuter
including Outer in FirstClass
including Inner in FirstClass
including SecondOuter in SecondClass
第一个和第四个语句解释了SecondClass
's 祖先的顺序。 Inner
是 的祖先,SecondOuter
而 又是 的祖先SecondClass
。因此我们有
SecondOuter.ancestors
=> [SecondOuter, Inner]
SecondClass.ancestors
=> [SecondClass, SecondOuter, Inner, Object, PP::ObjectMixin, Kernel, BasicObject]
第三和第四个语句显示了为什么外部和内部模块顺序对于FirstClass
' 的祖先是相反的:
首先,Inner
不是的祖先Outer
。
其次,Outer
is 包含在FirstClass
before Inner
is 中,但Inner.included
解析 before Outer.included
does。这导致
Outer.ancestors
=> [Outer]
FirstClass.ancestors
=> [FirstClass, Inner, Outer, Object, PP::ObjectMixin, Kernel, BasicObject]
当我们扩展 AS::Concern 并将include SomeModule
语句放入included do
块中时,我们实际上是在包含SomeModule
类似于Outer
上面的方法。
Ruby 2.3.1 模块文档
ActiveSupport::关注included