I want to be notified when certain things happen in some of my classes. I want to set this up in such a way that the implementation of my methods in those classes doesn\'t c
I imagine you could use an alias method chain.
Something like this:
def notify_when(method)
alias_method "#{method}_without_notification", method
define_method method do |*args|
puts "#{method} called"
send "#{method}_without_notification", args
end
end
You do not have to modify methods yourself with this approach.
It has been quite a while since this question here has been active, but there is another possibility to wrap methods by an included (or extended) Module.
Since 2.0 you can prepend a Module, effectively making it a proxy for the prepending class.
In the example below, a method of an extended module module is called, passing the names of the methods you want to be wrapped. For each of the method names, a new Module is created and prepended. This is for code simplicity. You can also append multiple methods to a single proxy.
An important difference to the solutions using alias_method
and instance_method
which is later bound on self
is that you can define the methods to be wrapped before the methods themselves are defined.
module Prepender
def wrap_me(*method_names)
method_names.each do |m|
proxy = Module.new do
define_method(m) do |*args|
puts "the method '#{m}' is about to be called"
super *args
end
end
self.prepend proxy
end
end
end
Use:
class Dogbert
extend Prepender
wrap_me :bark, :deny
def bark
puts 'Bah!'
end
def deny
puts 'You have no proof!'
end
end
Dogbert.new.deny
# => the method 'deny' is about to be called
# => You have no proof!
I can think of two approaches:
(1) Decorate the Foo
methods to include a notification.
(2) Use a proxy object that intercepts method calls to Foo
and notifies you when they happen
The first solution is the approach taken by Jakub, though the alias_method
solution is not the best way to achieve this, use this instead:
def notify_when(meth)
orig_meth = instance_method(meth)
define_method(meth) do |*args, &block|
puts "#{meth} called"
orig_meth.bind(self).call *args, &block
end
end
The second solution requires you to use method_missing
in combination with a proxy:
class Interceptor
def initialize(target)
@target = target
end
def method_missing(name, *args, &block)
if @target.respond_to?(name)
puts "about to run #{name}"
@target.send(name, *args, &block)
else
super
end
end
end
class Hello; def hello; puts "hello!"; end; end
i = Interceptor.new(Hello.new)
i.hello #=> "about to run hello"
#=> "hello!"
The first method requires modifying the methods (something you said you didn't want) and the second method requires using a proxy, maybe something you do not want. There is no easy solution I'm sorry.