2011-01-19 3 views
5

モジュールを組み込むことで、動的に生成されるメソッドをオーバーライドしようとしています。このモジュールを組み込むことで、動的に生成されるメソッドがオーバーライドされないのはなぜですか?

以下の例では、Rippleアソシエーションがrows=メソッドを表に追加します。私はそのメソッドを呼び出すだけでなく、後でいくつか追加のものを行います。

私はモジュールのrow=が既存のメソッドを使用するためにsuperを呼び出すことができると考えて、メソッドをオーバーライドするモジュールを作成しました。

class Table 

    # Ripple association - creates rows= method 
    many :rows, :class_name => Table::Row 

    # Hacky first attempt to use the dynamically-created 
    # method and also do additional stuff - I would actually 
    # move this code elsewhere if it worked 
    module RowNormalizer 
    def rows=(*args) 
     rows = super 
     rows.map!(&:normalize_prior_year) 
    end 
    end 
    include RowNormalizer 

end 

私はその中に例外を発生させた場合、何も起こらないという事実によって証明されるようにしかし、私の新しいrows=は、呼び出されることはありません。

私はモジュールが含まれていることを知っています。なぜなら、これを入れれば例外が発生するからです。

 included do 
     raise 'I got included, woo!' 
     end 

また、代わりrows=の場合、モジュールは、そのメソッドが呼び出し可能であり、somethingelse=定義します。

モジュールメソッドが動的に生成されたメソッドをオーバーライドしないのはなぜですか?

答えて

10

はの実験をやってみましょう:

class A; def x; 'hi' end end 
module B; def x; super + ' john' end end 
A.class_eval { include B } 

A.new.x 
=> "hi" # oops 

それはなぜですか?答えは簡単です:

A.ancestors 
=> [A, B, Object, Kernel, BasicObject] 

B(あなたはBA内部ていると考えることができます)先祖チェーンのA前です。そのため、A.xは常にB.xより優先されます。

しかし、これは回避することができます。

class A 
    def x 
    'hi' 
    end 
end 

module B 
    # Define a method with a different name 
    def x_after 
    x_before + ' john' 
    end 

    # And set up aliases on the inclusion :) 
    # We can use `alias new_name old_name` 
    def self.included(klass) 
    klass.class_eval { 
     alias :x_before :x 
     alias :x :x_after 
    } 
    end 
end 

A.class_eval { include B } 

A.new.x #=> "hi john" 

activesupportの(したがって、Railsの)を使用すると、alias_method_chain(target, feature)http://apidock.com/rails/Module/alias_method_chainとして実装され、このパターンがあります。

module B 
    def self.included(base) 
    base.alias_method_chain :x, :feature 
    end 

    def x_with_feature 
    x_without_feature + " John" 
    end 
end 

を更新ルビー2はModule#prependが付属していますこれはAのメソッドをオーバーライドします。ほとんどの使用例ではこのaliasハックは不要です。

+0

私はアップアップしようとしていましたが、その後、あなたは立ち去って、誰もぶら下げていました。 :-) –

+0

ありがとう!私はこれを知っている必要があります:私はここに継承チェーンを書きました... http://stackoverflow.com/questions/3492679/ruby-determining-method-origins :) –

2

私のモジュールメソッドが動的に生成されたものを上書きしないのはなぜですか?

これは継承の仕組みではないためです。クラスで定義されたメソッドは、他のクラス/モジュールから継承されたメソッドを上書きします。それは代わりに継承チェーンにおけるスーパークラスのサブクラスとしてモジュールを挿入以外のRuby 2.0で

は、ちょうどModule#includeように動作Module#prependは、あります。

+0

素晴らしい、私は本当にモジュール#を参照してくださいに興奮していますRuby 2のために(少なくとも一時的に)計画されています! (http://redmine.ruby-lang.org/issues/1102を参照) –

0

extendクラスのインスタンスを作成することができます。

class A 
    def initialize 
    extend(B) 
    end 
    def hi 
    'hi' 
    end 
end 
module B 
    def hi 
    super[0,1] + 'ello' 
    end 
end 

obj = A.new 
obj.hi #=> 'hello' 
関連する問題