2012-09-25 14 views
9

Mixinとして使用するモジュールでインスタンス変数を初期化するためのクリーンな方法はありますか? - これは軽度に刺激する私は、各@memberが適切に各関数内で初期化されている場合、何度も何度も確認する必要がありMixinでインスタンス変数を初期化する

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 

お知らせ:たとえば、私は次のよう持っています。

module Example 

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

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

を繰り返してください物事を正しく初期化されていることを確認する必要はありません:私はむしろ書くでしょう。しかし、私が言うことから、これは不可能です。 initialize_meメソッドをExampleに追加し、拡張クラスからinitialize_meを呼び出す以外に、これを回避する方法はありますか?私はthis exampleを見ましたが、これを達成するために私が猿のパッチを当てているのはClassにはありません。

答えて

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

編集:これはクラスのインスタンス変数を設定していることに注意してください。これらのインスタンスはまだ作成されていないため、モジュールがクラスに混在しているときにインスタンスのインスタンス変数を作成することはできません。することができます、しかし、例えば、ミックスインでinitializeメソッドを作成します。:

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

を含むクラスのinitializeメソッド(誰に?)呼び出し中にこれを行う方法があるかもしれません。わからない。

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

modulesModule#includedとして、フックが用意されています。しかし、ここでの代替です。トピックに関するruby docをチェックするか、モジュールに関するいくつかのヘルパーを提供する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

インクルードの代わりにプリペンドを使用する際の短所はありますか? –

+0

私が考えることができる1つの欠点は、モジュールが階層のクラスの下に挿入されることです。通常、タイプ階層は 'A Max

+0

大丈夫です:) まあ "前置き"は、私が推測するように、それは非常に自己説明的なので、私はあなたのソリューションが好きです、そしてありがとう! (+1) –

0

私はそこかもしれないと思いますこれに対する簡単な答え。モジュールには、通常どおりに変数を初期化するイニシャライザが必要です。モジュールを含むクラスのイニシャライザで、インクルードされたモジュールの初期化子を呼び出すためにsuper()を呼び出します。これは単にRubyのメソッドディスパッチルールに従います。

リフレクションでは、モジュールを含むクラスにも初期化が必要なスーパークラスがある場合、これはうまく動作しません。モジュールの初期化子は、可変パラメータリストを受け入れ、これをスーパークラスに渡す必要があります。それは、しかし、探検するために良い道のように見えます。

関連する問題