2012-02-21 14 views
5

少し "Deprecate-It" libを書きたいと思い、 "method_added"コールバックをたくさん使いました。 しかし、私は、このコールバックがモジュールを組み込むときにトリガされないことに気付きました。Rubyのmethod_addedコールバックはモジュールを含むトリガーではありません

何らかのコールバックまたは回避策があり、クラス「Foobar」に自分自身にsomewhingが含まれていることが通知されるようにしますか?

小さなデモが実証する:

InstanceMethod: 'visible' added to 'Foobar' 
invisible: true 
visible: true 
wont_exist: false 

追加情報:結果だ

# Including Moduls won't trigger method_added callback 

module InvisibleMethod 
    def invisible 
    "You won't get a callback from me" 
    end 
end 

class Foobar 
    def self.method_added(m) 
    puts "InstanceMethod: '#{m}' added to '#{self}'" 
    end 

    def visible 
    "You will get a callback from me" 
    end 

    include InvisibleMethod 
end 

[:invisible, :visible, :wont_exist].each do |meth| 
    puts "#{meth}: #{Foobar.public_method_defined? meth}" 
end 

を私は本当にmethod_addedのようなフックを使用する必要があります。

ActiveModelは、匿名モジュールを使用して実行時にクラスにpublic_instance_methodsを追加しています。

+1

残念なことに私は良い答えがあるとは思わないが、これは動作するいくつかのハックの方向にあなたを指すことができます.. http:// stackoverflow。com/questions/4191214 /コールバック - for-classes-defined-inside-a-module – David

答えて

8

問題は、モジュールを含めてもクラスにメソッドが追加されず、メソッド呼び出しチェーンだけが変更されるということです。このチェーンは、どのクラス/モジュールがメソッドのために検索されるかを定義します。これは問題のクラスに対して定義されていません。モジュールを含めると、そのチェーンにエントリが追加されます。

これは、スーパークラスにメソッドを追加したときとまったく同じです。スーパークラスで定義されていないため、method_addedを呼び出すことはありません。サブクラスがスーパークラスの振る舞いを変更する可能性があるのは非常に奇妙です。

は手動でメソッドを呼び出すことによって、あなたのクラスのincludeを再定義することによって、含まれるモジュールのために追加したことを修正することができます:

class Foobar 
    def self.include(included_module) 
    included_module.instance_methods.each{|m| self.method_added(m)} 
    super 
    end 
end 

そして、それはModuleincluded方法を再定義するよりもはるかに安全である - の変更だけ狭くなっていますあなたが自分で定義したクラスに

+0

method_addedメソッドはデフォルトでプライベートですが、この例の逆を行い、含まれているモジュールの中で作業をしたい場合は、以下のようにしなければなりません:def self.included(base); base.send:method_added、:method_name;終わり – odigity

3

コメントの1つで示唆されているように、他のフックを使用して、必要な動作を得ることができます。例えば、あなたのコードの先頭にこれを追加しよう:

class Module 
    def included(klass) 
    if klass.respond_to?(:method_added) 
     self.instance_methods.each do |method| 
     klass.method_added(method) 
     end 
    end 
    end 
end 

モジュールがクラスに含まれているときはいつでも、そのモジュールのすべてのインスタンスメソッドがある限り、それはメソッドを定義して、クラスに通知されますmethod_added。上の変更を使用してコードを実行すると、次の結果が得られます。

InstanceMethod: 'visible' added to 'Foobar' 
InstanceMethod: 'invisible' added to 'Foobar' 
invisible: true 
visible: true 
wont_exist: false 

これは私が望む動作だと思います。

+0

'include(klass) 'を上書きするときに誰かが' super'を呼び出さないと楽しいです – Reactormonk

1

私はdeprecationがライブラリを必要としていないと思います。これはdatamapperのように実装されています。約method_addedフック;すでにmoduleに追加されているメソッドはclassではないため、期待どおりに動作しています。あなただけがあなたの期待した結果を得ることができますincludedフックパッチを猿。

# got from https://github.com/datamapper/dm-core/blob/master/lib/dm-core/support/deprecate.rb 
module Deprecate 
    def deprecate(old_method, new_method) 
    class_eval <<-RUBY, __FILE__, __LINE__ + 1 
     def #{old_method}(*args, &block) 
     warn "\#{self.class}##{old_method} is deprecated, use \#{self.class}##{new_method} instead (\#{caller.first})" 
     send(#{new_method.inspect}, *args, &block) 
     end 
    RUBY 
    end 
end # module Deprecate 

class MyClass 
    extend Deprecate 

    def old_method 
    p "I am old" 
    end 
    deprecate :old_method, :new_method 

    def new_method 
    p "I am new" 
    end 
end 

m = MyClass.new 
m.old_method 

# MyClass#old_method is deprecated, use MyClass#new_method instead (pinger.rb:27:in `<main>') 
# "I am new" 
関連する問題