2

考虑以下代码:

require 'active_support/concern'

module Inner
end

module Outer
  extend ActiveSupport::Concern
  included do
    include Inner
  end
end

class FirstClass
  include Outer
end

module SecondInner
end

module SecondOuter
  include SecondInner
end

class SecondClass
  include SecondOuter
end

为什么通过 AS::Concern 与普通 Ruby 包含的模块的祖先顺序不同?

FirstClass.ancestors
# => [FirstClass, Inner, Outer, Object, PP::ObjectMixin, Kernel, BasicObject]

SecondClass.ancestors
# => [SecondClass, SecondOuter, SecondInner, Object, PP::ObjectMixin, Kernel, BasicObject]
4

2 回答 2

5

ActiveSupport::Concern不会更改祖先的查找顺序。

如果您更改module Outer为使用纯 Ruby 来执行相同的操作,那么 AS 会做什么但没有 AS,您会看到,它具有相同的祖先链:

module Outer
  def self.included(base)
    base.send(:include, Inner)
  end
end

SecondClass.ancestors
#=> [SecondClass, SecondOuter, SecondInner, Object, PP::ObjectMixin, Kernel, BasicObject]
于 2016-10-15T18:04:55.647 回答
1

Andrey Deineko 的回答为了解正在发生的事情提供了重要基础。

当模块包含在类或其他模块中时会发生什么?有两件事似乎相关:

  • append_features叫做。

当这个模块包含在另一个模块中时,Ruby 会在这个模块中调用 append_features,并将其传递给 mod 中的接收模块。如果此模块尚未添加到 mod 或其祖先之一,则 Ruby 的默认实现是将此模块的常量、方法和模块变量添加到 mod。另请参阅模块#include。

  • included叫做

每当接收器包含在另一个模块或类中时都会调用回调。如果您的代码想要在模块包含在另一个模块中时执行某些操作,则应优先使用此模块。

我们不能挂钩,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

其次,Outeris 包含在FirstClassbefore Inneris 中,但Inner.included解析 before Outer.includeddoes。这导致

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

于 2016-10-16T14:02:15.957 回答