2008-08-14 121 views
34

好的,所以我一直在我的小Rails應用程序中重構我的代碼,以消除重複,並且通常讓我的生活更輕鬆(因爲我喜歡輕鬆的生活)。這個重構的一部分,一直是將我的兩個模型共同的代碼移動到一個模塊中,我可以將其包含在我需要的模塊中。Ruby mixins和調用超級方法

到目前爲止,這麼好。看起來它會解決問題,但我剛剛遇到一個問題,我不知道如何解決。該模塊(我稱之爲「可發送」)只是處理傳真,電子郵件或打印文檔PDF的代碼。所以,例如,我有一個採購訂單,並且我有內部銷售訂單(想象力縮寫爲ISO)。

我打的問題,是我想要一些變量初始化(初始化的人誰不正確拼寫:P)的對象加載後,所以我一直在使用after_initialize掛鉤。沒問題...直到我開始添加更多mixin。

我的問題是,我可以在我的混入的任何一個的after_initialize,所以我需要包括在開始一個超級通話,以確保其他混入after_initialize調用被調用。這真是太棒了,直到我最終打電話給超級電話,並且因爲沒有超級電話而出現錯誤。

這裏有一個小例子,如果我沒有被混淆不夠:

class Iso < ActiveRecord::Base 
    include Shared::TracksSerialNumberExtension 
    include Shared::OrderLines 
    extend Shared::Filtered 
    include Sendable::Model 

    validates_presence_of :customer 
    validates_associated :lines 

    owned_by    :customer 
    order_lines    :despatched # Mixin 

    tracks_serial_numbers :items # Mixin 

    sendable :customer      # Mixin 

    attr_accessor :address 

    def initialize(params = nil) 
    super 
    self.created_at ||= Time.now.to_date 
    end 
end 

所以,如果混入的每一個有after_initialize調用,具有超級電話,我怎麼能阻止那最後超級打電話提高錯誤?在我叫它之前,如何測試超級方法的存在?

回答

40

您可以使用此:

super if defined?(super) 

下面是一個例子:

class A 
end 

class B < A 
    def t 
    super if defined?(super) 
    puts "Hi from B" 
    end 
end 

B.new.t 
0

而不是檢查,如果超級方法存在,你可以把它定義

class ActiveRecord::Base 
    def after_initialize 
    end 
end 

這工作在我的測試,並且不應該破壞任何現有的代碼,因爲你的所有其他類,其定義它將無聲無息地默默覆蓋這種方法

+1

Downvoted,因爲它不是一個通用的解決方案。關鍵是你不知道超級是否存在,所以如果將ActiveRecord :: Base#after_initialize存在,你可以創建一個點,當你的代碼在/當ActiveRecord添加一個Base#after_initialize或者它的arity被改變時;如果它被定義,那麼有條件地調用它是遠遠不夠的。 – yaauie 2012-04-03 00:15:10

+0

@yaauie - 當然,我可以在monkeypatch之前加一個`raise'oh no',如果方法.include?(:after_initialize)`,但這會讓這個例子更難理解......很容易陷入困境在詳細說明所有邊界情況下,這裏的實際課程(只是修補一個基本方法)將會在噪聲中迷失方向。 – 2012-04-11 00:29:33

3

你試過alias_method_chain?你基本上可以連接你所有的after_initialize電話。它的作用就像一個裝飾器:每個新方法都增加了一個新的功能層,並將控制權交給「重寫」方法來完成剩下的工作。

3

的包括類可以定義自己after_initialize,所以比alias_method_chain(或其他別名,節省了原來的)以外的任何解決方案(從ActiveRecord::Base繼承,其中,在這種情況下是Iso的東西)風險覆蓋代碼。 @Orion Edwards的解決方案是我能想到的最好的解決方案。還有其他的,但他們是更駭人。

alias_method_chain也有創建after_initialize方法的命名版本的好處,這意味着您可以在極少數情況下自定義呼叫順序。否則,你將受到包括類包含mixin的任何順序的擺佈。

我已經發布了一個問題有關創建的所有回調的默認空實現紅寶石上軌核心郵件列表。無論如何,保存過程會檢查它們,所以我不明白他們爲什麼不應該在那裏。唯一的缺點是創建額外的空棧幀,但在每個已知的實現上都相當便宜。

2

你可以隨便扔一個快速的條件中有:

super if respond_to?('super') 

,你應該罰款 - 不增加無用的方法;很好,很乾淨。