2013-07-05 12 views
8

私は、initializeの動作をモジュールからどのように拡張するかを考えようとしています。混じっているクラスのinitializeにsuperを呼ぶことなくやりたい。私は呼び出しの通常のパターンをサポートしたいinclude私はそれを把握することはできません。私はこの問題について見つけ出すことのできるものすべてを読んだことがあります。人々には提案がありますが、実際には(少なくとも私の手の中では)どちらも動作していないようです。ここでRubyモジュールで#initializeをオーバーライドすることはできますか?

は私が私が知っている(と思う)です:

  • をそれがすべてで行うことができれば、それはinclude(すなわちModule.included(base))のフックを使用して行う必要があります。
  • includeフックは、包含クラスがinitializeを定義する前に実行されるため、initializebase.instance_evalで定義しようとするだけで上書きされることはありません。
  • method_addedフックを使用し、そこで対処することを提案しました。これは私が今試していることですが、フックがメソッド定義の始めに実行されるように見えますので、以下で見ているものになります。

    module Mo 
        def self.included(klass) 
        klass.instance_eval do 
         def method_added(method) 
         puts "Starting creation of #{method} for #{self.name}" 
         case method 
         when :initialize 
          alias_method :original_initialize, :initialize 
          puts "About to define initialize in Mo" 
          def initialize 
          original_initialize 
          puts "Hello from Mo#initialize" 
          end 
          puts "Finished defining initialize in Mo" 
         end 
         puts "Finishing creation of #{method} for #{self.name}" 
         end 
        end 
        end 
    end 
    
    class Foo 
        include Mo 
        def initialize 
        puts "Hello from Foo#initialize" 
        end 
    end 
    
    foo = Foo.new 
    

これは、次の出力が生成されます。

Starting creation of initialize for Foo 
    Starting creation of original_initialize for Foo 
    Finishing creation of original_initialize for Foo 
    About to define initialize in Mo 
    Finished defining initialize in Mo 
    Finishing creation of initialize for Foo 
    Hello from Foo#initialize 

Fooクラスからinitializeはまだモジュールからの定義を上書きしているようですが、私には見えます。私は、これは、定義がまだ開いていることを推測しています。最後にどのブロックが開始され、最後に終了するかは問題ではないことを示唆しています。

誰かが実際にこれを行う方法を知っていて、それを働かせてくれたら、私を啓発してください。

FWIW、はい、私はこれをやりたがっている理由があると思います。

答えて

24

あなたはルビー2.0以降を使っているのであれば、あなただけprependを使用することができます。どちらかがprependではなくincludeに、ユーザーが必要な、または実行します。

だけでなくRuby 1.9の中に、[OK]を
module Mo 
    module Initializer 
    def initialize 
     puts "Hello from Mo#initialize" 
     super 
    end 
    end 

    def self.included(klass) 
    klass.send :prepend, Initializer 
    end 
end 
+0

質問をした直後に 'prepend'を発見しました。私はそれが好きです! 1.9でそれを行う方法があるかどうか不思議です* – Huliax

+0

単純に 'klass.prepend'の代わりに' klass.send:prepend'を使用することで何かが分かりませんか? – NobodysNightmare

0

Fooの初期化で条件付き呼び出しを行うと、インクルードされているメソッドが存在する場合にのみ呼び出されます。

module Mo 
    def initialize_extra 
    puts "Hello from Mo" 
    end 
end 

class Foo 
    include Mo 
    def initialize 
    if defined? initialize_extra 
     initialize_extra 
    else 
     puts "Hello from Foo" 
    end 
    end 
end 

x = Foo.new 
+1

私の目標は、モジュールを含める必要以外のクラスに負担をかけないことです。 – Huliax

6

あなたはnewクラスメソッドに機能を追加することができます...

module Mo 
    def new(*var) 
    additional_initialize(*var) 
    super(*var) 
    end 
    def additional_initialize(*var) 
    puts "Hello from Mo" 
    end 
end 

class Foo 
    extend Mo 
    def initialize 
    puts "Hello from Foo" 
    end 
end 

foo = Foo.new 

返すことに...

Hello from Mo 
Hello from Foo