2011-01-19 58 views
5

我試圖通過包含一個模塊來覆蓋動態生成的方法。爲什麼包含此模塊不覆蓋動態生成的方法?

在下面的示例中,紋波關聯在表格中添加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是前A的祖先鏈(你可以認爲這是BA)。因此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

我正要打起精神來,但是後來你們落後了,讓所有人都掛了。 :-) – 2011-01-19 22:46:00

+0

謝謝!我應該知道這一點:我在這裏寫出了繼承鏈... http://stackoverflow.com/questions/3492679/ruby-determining-method-origins :) – 2011-01-20 14:25:35

2

爲什麼我的模塊方法不是覆蓋動態生成的方法?

因爲這不是繼承的工作原理。類中定義的方法覆蓋從其他類/模塊繼承的方法,而不是相反。

在Ruby 2.0,有Module#prepend,其工作方式一樣Module#include,除了它插入模塊作爲子類,而不是在繼承鏈的超類。

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' 
相關問題