2012-02-21 82 views
5

我想寫一點「棄用它」庫,並使用「method_added」回調很多。 但現在我注意到,當包含一個模塊時,這個回調沒有被觸發。紅寶石method_added回調不觸發,包括模塊

有沒有任何回調或變通方法,當somewhing被包含到自身中時,通知類「Foobar」?

小的演示證明:

# 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 

這就是結果:

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

附加信息:

我真的需要使用一個鉤狀method_added。

ActiveModel在運行時通過匿名模塊將public_instance_methods添加到類。

+1

不幸的是我不認爲有一個很好的答案,但這可能會指向你的一些黑客工作的方向.. http:// stackoverflow。com/questions/4191214/callback-for-classes-defined-within-a-module – David 2012-02-21 23:45:53

回答

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 2015-11-08 22:08:11

3

正如其中一條評論所建議的那樣,您可以使用其他掛鉤來獲得所需的行爲。例如,試着在你的代碼的開頭補充一點:

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

哦,如果有人在覆蓋'包含(klass)時沒有調用'super',那麼玩得開心' – Reactormonk 2012-02-29 22:18:59

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"