2010-07-13 33 views
11

Tôi muốn thực hiện một phương pháp móc được gọi là mọi lúc mọi chức năng của một lớp được gọi. Tôi đã thử method_added, nhưng nó thực hiện một lần duy nhất tại thời điểm định nghĩa lớp,Xác định "method_called" .. Làm thế nào để tạo một phương thức hook được gọi mỗi khi bất kỳ hàm nào của một lớp được gọi?

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." 

nhưng yêu cầu của tôi là bất kỳ chức năng của một lớp mà được gọi là bất cứ nơi nào trong chương trình kích hoạt "method_called", phương pháp móc .

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." 

Nếu có bất kỳ phương pháp móc hiện có nào được xác định chỉ hoạt động giống nhau, thì hãy nói về nó.

Cảm ơn trước ..

+0

Ya Tôi chắc chắn đã tìm đến họ và cảm ơn câu trả lời của bạn. –

+0

mikej có nghĩa là bạn nên quay lại các câu hỏi trước của mình và nhấp vào hộp kiểm trên bất kỳ câu trả lời nào đáp ứng đầy đủ câu hỏi của bạn. – sarnold

Trả lời

17

Hãy xem Kernel#set_trace_func. Nó cho phép bạn chỉ định một proc được gọi bất cứ khi nào một sự kiện (chẳng hạn như một cuộc gọi phương thức) xảy ra. Dưới đây là một ví dụ:

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 

Đầu ra:

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

method_added là có để chạy mã khi một phương pháp mới đã được thêm vào lớp; nó không báo cáo khi một phương thức được gọi. (. Như bạn khám phá)

Nếu bạn không muốn làm theo câu trả lời mikej của, đây là một lớp mà thực hiện đặc điểm kỹ thuật của bạn:

#!/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 

Và đầu ra:

$ ./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

Tôi thích điều này. Nó dường như ít xâm lấn hơn một 'set_trace_func' toàn cầu vì hackery này bị ràng buộc chỉ với lớp này. –

+0

Awesome Hack :) –

+0

rất thông minh! Nó quá tệ không có một tính năng ngôn ngữ mà làm cho điều này một chút dễ dàng hơn để làm mà không bị nặng. – bchurchill

1

Gần đây tôi đã viết một cái gì đó có thể hữu ích, mặc dù có một số provisos (xem dưới đây). Đây là lớp học mà bạn muốn thêm vào móc của bạn để:

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 

Và đây là đoạn code để thêm rằng móc:

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 

Nếu bạn gọi nó như thế này ...

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

Bạn nhận được kết quả sau:

không có gì bất thường về bit này

Phương thức 'Regular_old_method' được gọi!

Phương pháp này sẽ là một ý tưởng tồi nếu mã của bạn kiểm tra tên lớp, vì mặc dù bạn đã yêu cầu đối tượng lớp 'Gốc', thứ bạn nhận được là đối tượng của lớp 'OriginalWrapper'.

Plus Tôi tưởng tượng có thể có những hạn chế khác để gây rối với phương pháp 'mới', nhưng kiến ​​thức về lập trình meta của Ruby không kéo dài đến mức nào.

Các vấn đề liên quan