2010-07-13 29 views
11

我想做一個鉤子方法,每次調用某個類的任何函數時都會調用它。 我已經試過method_added,但它在類定義時只執行一次,定義「method_called」..我如何製作一個鉤子方法,每次調用一個類的任何函數時都會被調用?

class Base 

    def self.method_added(name) 
    p "#{name.to_s.capitalize} Method's been called!!" 
    end 
    def a 
    p "a called." 
    end 
    def b 
    p "b called." 
    end 
end 
t1 = Base.new 
t1.a 
t1.b 
t1.a 
t1.b 

Output: 

"A Method's been called!!" 
"B Method's been called!!" 
"a called." 
"b called." 
"a called." 
"b called." 

但我的要求是,是獲取程序的任何地方被稱爲類的任何功能觸發「method_called」,掛鉤方法。

Expected Output: 
"A Method's been called!!" 
"a called." 
"B Method's been called!!" 
"b called." 
"A Method's been called!!" 
"a called." 
"B Method's been called!!" 
"b called." 

如果有任何已定義的現有掛鉤方法工作原理相同,請告訴它。

在此先感謝..

+0

雅我絕對仰望他們,並感謝您的答案。 – 2010-07-13 11:23:07

+0

mikej意味着您應該回頭查看以前的問題,然後單擊任何可以充分解決問題的答案的複選框。 – sarnold 2010-07-13 11:47:43

回答

17

看看Kernel#set_trace_func。它允許你指定一個事件(比如方法調用)發生時調用的proc。下面是一個例子:

class Base 
    def a 
    puts "in method a" 
    end 

    def b 
    puts "in method b" 
    end 
end 

set_trace_func proc { |event, file, line, id, binding, classname| 
    # only interested in events of type 'call' (Ruby method calls) 
    # see the docs for set_trace_func for other supported event types 
    puts "#{classname} #{id} called" if event == 'call' 
} 

b = Base.new 
b.a 
b.b 

輸出:

Base a called 
in method a 
Base b called 
in method b 
18

method_added是有運行時的新方法已被添加到類代碼;它不報告何時調用了一個方法。 (如你發現)

如果您不想遵守mikej的答案,這裏是實現你的規範的類:

#!/usr/bin/ruby 

class Base 
    def self.method_added(name) 
    if /hook/.match(name.to_s) or method_defined?("#{name}_without_hook") 
     return 
    end 
    hook = "def #{name}_hook\n p 'Method #{name} has been called'\n #{name}_without_hook\nend" 
    self.class_eval(hook) 

    a1 = "alias #{name}_without_hook #{name}" 
    self.class_eval(a1) 

    a2 = "alias #{name} #{name}_hook" 
    self.class_eval(a2) 
    end 
    def a 
    p "a called." 
    end 
    def b 
    p "b called." 
    end 
end 
t1 = Base.new 
t1.a 
t1.b 
t1.a 
t1.b 

輸出:

$ ./meta.rb 
"Method a has been called" 
"a called." 
"Method b has been called" 
"b called." 
"Method a has been called" 
"a called." 
"Method b has been called" 
"b called." 
+1

我喜歡這個。它似乎不如全局的'set_trace_func'更具有侵入性,因爲這個hackery僅限於這個類。 – 2010-07-13 16:56:34

+0

真棒哈克:) – 2012-12-18 11:10:41

+0

非常聰明!這太糟糕了,沒有一種語言功能,這使得這一點很容易做到沒有重量級。 – bchurchill 2013-06-20 19:27:44

1

我最近寫這可能是有用的,儘管有一些附帶條件(見下文)。 這裏有你想要你的鉤子添加到類:

class Original 
    def regular_old_method msg 
    puts msg 
    end 

private 

    def always_called method_called 
    puts "'#{method_called.to_s.capitalize}' method's been called!" 
    end 
end 

而這裏的代碼,用於增加掛鉤:

class << Original 
    def new(*args) 
    inner = self.allocate 
    outer_name = self.name + 'Wrapper' 
    outer_class = Class.new do 
     def initialize inner_object 
     @inner = inner_object 
     end 
     def method_missing method_called, *args 
     @inner.send method_called, *args 
     @inner.send :always_called, method_called 
     end 
    end 
    outer_class_constant = Object.const_set(outer_name, outer_class) 
    inner.send :initialize, *args 
    outer_class_constant.new inner 
    end 
end 

如果你這樣稱呼它......

o = Original.new 
o.regular_old_method "nothing unusual about this bit" 

您會得到以下輸出:

這個位沒什麼不尋常

'Regular_old_method'方法被調用!

這種做法將是一個糟糕的主意,如果你的代碼進行覈對的類名,因爲即使你自找的類的對象「原始」,你回來了類的對象「OriginalWrapper」。

另外我想可能還有其他的缺點來搞亂'新'方法,但是我對Ruby元編程的知識並沒有延伸到那麼遠。

相關問題