I revisited my Event.observe proof of concept and tidied it up a bit with a little help from stackoverflow.com.
Here’s how it do
This here defines a method to determine which method is currently running. There’s a slicker way to do this in 1.9 and you can read about them both on stackoverflow.com.
module Kernel private def current_method_name caller[0] =~ /`([^']*)'/ and $1 end end
Here’s my actual observer class, named after it’s inspiration.
class Event def initialize @events = [] end def observe object, method, callback handle = Handle.new self, object, method, callback @events << handle handle end def delete handle @events.delete handle end def call object, method handle = Handle.new self, object, method @events.each do |e| e.call if e.shares_type_with end end end
And finally there's a basic handle class:
class Handle attr_reader :observer, :object, :method, :callback def initialize observer, object, method, callback = nil @observer = observer @object = object @method = method @callback = callback end def shares_type_with? handle object == handle.object && method == handle.method end def call callback.call end def stop_observing observer.delete self end end
And here's how you use it, In the following example we want our "second" Test_Object to run it's test_method whenever we run the same method for our "first" object.
The important thing here is the "notify" method, you'll need to drop this method into whatever you'd like to be able to observe. If you wanted to be able to observe everything you could always loop through the methods, alias and override.
class Test_Object def initialize observer, label @observer = observer @label = label end def notify method @observer.call self, method end def test_method puts "Calling => #{@label}" notify current_method_name end end observer = Event.new first = Test_Object.new observer, "First" second = Test_Object.new observer, "Second" h1 = observer.observe( first, "test_method", second.method :test_method ) first.test_method # Calling First # Calling Second #=> nil
And we can drop the handle by simply calling it's stop_observing method
h1.stop_observing first.test_method # Calling First #=> nil
That's it.