2012-09-25 184 views
9

是否有任何干淨的方法來初始化模塊中的實例變量,以用作Mixin?例如,我有以下幾點:初始化Mixins中的實例變量

module Example 

    def on(...) 
    @handlers ||= {} 
    # do something with @handlers 
    end 

    def all(...) 
    @all_handlers ||= [] 
    # do something with @all_handlers 
    end 

    def unhandled(...) 
    @unhandled ||= [] 
    # do something with unhandled 
    end 

    def do_something(..) 
    @handlers  ||= {} 
    @unhandled ||= [] 
    @all_handlers ||= [] 

    # potentially do something with any of the 3 above 
    end 

end 

請注意,我不得不一次又一次地檢查如果每個@member,已在每個功能是否正確初始化 - 這輕微刺激。我寧願寫:

module Example 

    def initialize 
    @handlers  = {} 
    @unhandled = [] 
    @all_handlers = [] 
    end 

    # or 
    @handlers = {} 
    @unhandled = [] 
    # ... 
end 

而不是必須重複確保東西正確初始化。但是,從我可以告訴這是不可能的。除了在Example中添加initialize_me方法並從擴展類中調用initialize_me之外,有什麼方法可以解決這個問題嗎?我確實看到了this example,但是我沒有辦法只爲了達到這個目的而將它們修改成Class

回答

12
module Example 
    def self.included(base) 
    base.instance_variable_set :@example_ivar, :foo 
    end 
end 

編輯:請注意,這是設置一個類的實例變量。當模塊混入類中時,不能創建實例上的實例變量,因爲這些實例尚未創建。你可以,不過,在混入創建初始化方法,例如:

module Example 
    def self.included(base) 
    base.class_exec do 
     def initialize 
     @example_ivar = :foo 
     end 
    end 
    end 
end 

可能有辦法做到這一點,同時調用包括類的初始化方法(人?)。不確定。但這裏的一種替代方案:

class Foo 
    include Example 

    def initialize 
    @foo = :bar 
    after_initialize 
    end 
end 

module Example 
    def after_initialize 
    @example_ivar = :foo 
    end 
end 
+0

非常好,謝謝 - 我想知道爲什麼我沒有看到這個方法提到任何地方,即使有一百萬篇關於它的文章。 –

+0

不幸的是,這看起來不起作用 - 引用'@ example_ivar'然後返回爲'nil'。 –

+0

啊,我明白了,謝謝 - 但是如果這個類已經指定了一個'initialize'方法就會引發問題,對吧? –

2

modules提供了鉤子,作爲Module#included。我建議你查看關於該主題的ruby文檔,或使用ActiveSupport::Concern,它提供了一些模塊上的幫助程序。

+0

我沒有使用'ActiveSupport',但是感謝我指出了正確的方向。 –

+0

不客氣。 – ksol

1

也許這是一個小哈克,但您可以使用prepend來獲得所需的行爲:

module Foo 
    def initialize(*args) 
    @instance_var = [] 
    super 
    end 
end 

class A 
    prepend Foo 
end 

下面是從控制檯輸出:

2.1.1 :011 > A.new 
=> #<A:0x00000101131788 @instance_var=[]> 
+0

使用prepend而不是include有什麼缺點? –

+0

我能想到的一個缺點是模塊被插入層次結構中類的下面。通常,我們的類型層次結構看起來像'A Max

+0

好吧:) 那麼「prepend」對我來說是相當不言自明的,所以我喜歡(和使用)你的解決方案,謝謝! (+1) –

0

我認爲有可能是一個更簡單的答案。模塊應該有一個初始化器,像通常那樣初始化變量。在包含模塊的類的初始化器中,調用super()來調用包含模塊中的初始化器。這只是遵循Ruby中的方法調度規則。

經過反思,如果包含該模塊的類也有一個需要初始化的超類,那麼這將無法很好地工作。模塊中的初始化器需要接受一個可變參數列表並將其傳遞給超類。它看起來是一個很好的探索途徑。