I want to make a hook method which gets called everytime any function of a class gets called. I have tried method_added, but it executes only once at the time of class defin
I recently wrote something that might be useful, though there are some provisos (see below). Here's the class you want to add your hook to:
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
And here's the code for adding that hook:
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
If you call it like this...
o = Original.new
o.regular_old_method "nothing unusual about this bit"
You get the following output:
nothing unusual about this bit
'Regular_old_method' method's been called!
This approach would be a bad idea if your code checked class names, because even though you've asked for an object of class 'Original', what you got back was an object of class 'OriginalWrapper'.
Plus I imagine there could be other drawbacks to messing with the 'new' method, but my knowledge of Ruby metaprogramming doesn't stretch that far yet.
Take a look at Kernel#set_trace_func. It lets you specify a proc which is invoked whenever an event (such as a method call) occurs. Here's an example:
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
Outputs:
Base a called
in method a
Base b called
in method b
method_added
is there to run code when a new method has been added to the class; it doesn't report when a method has been called. (As you discovered.)
If you don't want to follow mikej's answer, here is a class that implements your specification:
#!/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
And output:
$ ./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."