I got the following code from @avdi’s Ruby Tapas episode for today:
module Eventful
def self.included(other)
other.extend(Macros)
end
def add_listener(listener)
(@listeners ||= []) << listener
end
def notify_listeners(event, *args)
(@listeners || []).each do |listener|
listener.public_send("on_#{event}", *args)
end
end
module Macros
def event(name)
module_eval(%Q{
def #{name}(*args)
notify_listeners(:#{name}, *args)
end
})
end
end
end
class Dradis
include Eventful
event :new_contact
end
class ConsoleListener
def on_new_contact(direction, range)
puts "DRADIS contact! #{range} kilometers, bearing #{direction}"
end
end
dradis = Dradis.new
dradis.add_listener(ConsoleListener.new)
dradis.new_contact(120, 23000)
I understand the concept of events and listeners and the observer pattern, but don’t get how/why this syntax is working, and haven’t seen it in any manuals. The class Dradis has this:
event :new_contact
At first, I thought that event was a method and :new_contact was an argument so that I would call event on an instance of Dradis, something like:
dradis = Dradis.new
dradis.event
but instead, new_contact is called on an instance of Dradis like:
dradis = Dradis.new
dradis.add_listener(ConsoleListener.new)
dradis.new_contact(120, 23000)
and that triggers the event method in the Macro module.
Can anyone explain why it works like this? calling :new_contact on an instance dradis to trigger the event method?
I didn’t watch the episode, but look, it’s right there.
eventis a method which defines another method (new_contact) which callsnotify_listenersfromEventful.Incorrect. That method has finished its work a long time ago and it doesn’t get invoked again. It produced a new method using
module_eval/defand that new method (new_contact) is what’s getting called.It’s important to understand that
eventmethod runs only once, when theDradisclass is parsed and loaded. It does not get run on every instantiation ofDradis.