The problem is very simple. An object needs to notify some events that might be of interest to observers.
When I sat to validate a design that I cooked up now in Ruby just to validate it.. I find myself stumped as to how to implement the object events. In .Net this would be a one-liner.. .Net also does handler method signature verification,etc. e.g.
// Object with events
public delegate void HandlerSignature(int a);
public event HandlerSignature MyEvent;
public event HandlerSignature AnotherCriticalEvent;
// Client
MyObject.MyEvent += new HandlerSignature(MyHandlerMethod); // MyHandlerMethod has same signature as delegate
Is there an EventDispatcher module or something that I am missing that I can strap on to a Ruby class ? Hoping for an answer that plays along with Ruby's principle of least surprise. An event would be the name of the event plus a queue of [observer, methodName] objects that need to be invoked when the event takes place.
Firstly, in Ruby there are no method signatures. The closest would be checking the function's arity. Duck typing requires thinking differently (slightly).
The Observable module is a start, but if you have a requirement to have multiple events from a single class it might not be enough.
This is a quick sketch. It supports methods and blocks. Modify as necessary to adapt for your code, threading approach, etc. For example, you could use method_missing to have the event name in the method name rather than having it as a parameter.
class EventBase
def initialize
@listeners = Hash.new
end
def listen_event(name, *func, &p)
if p
(@listeners[name] ||= Array.new) << p
else
(@listeners[name] ||= Array.new) << func[0]
end
end
def ignore_event(name, func)
return if [email protected]_key?(name)
@listeners[name].delete_if { |o| o == func }
end
def trigger_event(name, *args)
return if [email protected]_key?(name)
@listeners[name].each { |f| f.call(*args) }
end
end
class MyClass < EventBase
def raise_event1(*args)
trigger_event(:event1, *args)
end
def raise_event2(*args)
trigger_event(:event2, *args)
end
end
class TestListener
def initialize(source)
source.listen_event(:event1, method(:event1_arrival))
source.listen_event(:event2) do |*a|
puts "event 2 arrival, args #{a}"
end
end
def event1_arrival(*a)
puts "Event 1 arrived, args #{a}"
end
end
s = MyClass.new
l = TestListener.new(s)
s.raise_event1("here is event 1")
s.raise_event2("here is event 2")
Why not write your own event class? Something like
class Event
def initialize
@handlers = Array.new
end
def fire
@handlers.each do |v|
v.call
end
end
def << handler
@handlers << handler
end
end
e = Event.new
e << lambda { puts "hello" }
e << lambda { puts "test" }
e.fire
This is just a minimal sample, but can be extended in any ways. Add parameters like sender and eventArgs in .Net, or whatever you like ;-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With