14

ルビーで次の構造を持っているとしましょう(レールなし)

module Parent
    def f
        puts "in parent"
    end
end

module Child
    def f
        super
        puts "in child"
    end
end

class A
    include Parent
    include Child
end

A.new.f # prints =>
#in parent
#in child

Railsの懸念事項を使用する場合

module Parent
  extend ActiveSupport::Concern

  included do

    def f
      puts "In Parent"
    end


  end

end

module Child
  extend ActiveSupport::Concern

  included do

    def f
      super
      puts "In Child"
    end


  end

end

class A < ActiveRecord::Base
  include Parent
  include Child
end

A.new.f #exception
NoMethodError: super: no superclass method `f' for #<A:0x00000002244490>

それで、私はここで何が欠けていますか?通常のモジュールのように、懸念事項で super を使用する必要があります。検索しましたが、このトピックに関するヘルプが見つかりませんでした

4

1 に答える 1

24

これは、含まれているメソッド ブロックが実際にはクラスのコンテキストで評価されるためです。つまり、その中で定義されたメソッドは、モジュールが含まれているときにクラスで定義されているため、含まれているモジュールよりも優先されます。

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 は失敗するはずです。

于 2016-02-25T19:27:51.967 に答える