これは、含まれているメソッド ブロックが実際にはクラスのコンテキストで評価されるためです。つまり、その中で定義されたメソッドは、モジュールが含まれているときにクラスで定義されているため、含まれているモジュールよりも優先されます。
module Child1
extend ActiveSupport::Concern
included do
def foo
end
end
end
module Child2
def bar
end
end
class A
include Child1
include Child2
end
A.new.method(:foo).owner #=> A
A.new.method(:bar).owner #=> Child2
メソッド検索
Ruby では、メソッドを呼び出すたびに、最初にそれを見つけなければなりません (それがメソッドなのか変数なのかはわかりません)。これは、いわゆるメソッド検索で行われます。レシーバーが指定されていない場合 ( のような純粋な呼び出しputs
)、まず現在のスコープで変数を検索します。見つからない場合は、 current でそのメソッドを検索しますself
。受信者が指定されている場合 ( foo.bar
)、指定された受信者のメソッドを自然に検索します。
今ルックアップ-ルビーでは、すべてのメソッドは常にいくつかのモジュール/クラスに属しています。順序の最初は受信者の固有クラスです (存在する場合)。そうでない場合は、通常の受信者のクラスが最初です。
クラスでメソッドが見つからない場合は、指定されたクラスに含まれるすべてのモジュールを逆の順序で検索します。そこに何も見つからない場合は、指定されたクラスのスーパークラスが次です。何かが見つかるまで、プロセス全体が再帰的に進みます。ルックアップが BasicObject に到達し、メソッドが見つからない場合は終了し、BasicObject で定義されたデフォルトの実装で method_missing の検索をトリガーします。
注意すべき重要なことは、クラスに属するメソッドは常にモジュール メソッドよりも優先されるということです。
module M
def foo
:m_foo
end
end
class MyClass
def foo
:class_foo
end
include M
end
MyClass.new.foo #=> :class_foo
約super
スーパー メソッドの検索は非常に似ています。メソッド ルックアップのさらに先にある同じ名前のメソッドを検索しようとしているだけです。
module M1
def foo
"M1-" + super
end
end
module M2
def foo
'M2-' + super
end
end
module M3
def foo
'M3-' + super
end
end
class Object
def foo
'Object'
end
end
class A
include M2
include M3
end
class B < A
def foo
'B-' + super
end
include M1
end
B.new.foo #=> 'B-M1-M3-M2-Object'
ActiveSupport::Concern#included
included
ブロックを取りself.included
、現在のモジュールでメソッドを作成する非常に単純なメソッドです。ブロックは を使用してinstance_eval
実行されます。つまり、そこにあるコードは、指定されたモジュールが含まれているクラスのコンテキストで実際に実行されます。したがって、その中でメソッドを定義すると、このメソッドはモジュールを含むクラスによって所有されます。 、モジュール自体ではありません。
各モジュールは、同じ名前のメソッドを 1 つしか保持できません。同じ名前のメソッドを 2 つ定義しようとすると、以前の定義は完全に消去され、ruby メソッド ルックアップを使用して見つける方法がありません。あなたの例では、インクルード ブロックに同じメソッド定義を持つ 2 つのモジュールを含めたため、2 番目の定義は最初の定義を完全にオーバーライドし、メソッド ルックアップの上位に他の定義がないため、super は失敗するはずです。