Reputation: 11683
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?
Upvotes: 0
Views: 78
Reputation: 588
Several separated features of ruby is used: In the line event :new_contact
the "evnet" is class method (http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_classes.html#UE).
Usually class methods are defined by:
class A
def A.my_class_method
#code
end
end
A.my_class_method #executing class_method
or
class A
def <<self #usefull when you want delare several class methods
def A.my_class_method
#code
end
end
end
A.my_class_method #executing class_method
In the code the method is included by the module Macros
.
The key thing is, that (class method) event
is dynamicaly creating new (instance) method (in this case new_contact
)
The name of the method is passed as argument to event
). And this method providing calling of the listener.
Can anyone explain why it works like this? calling :new_contact on an instance dradis to trigger the event method?
This is by the dynammically created method new_contact
which is calling notify_listeners(:#{name}, *args)
Upvotes: 1
Reputation: 230561
I didn't watch the episode, but look, it's right there.
module Macros
def event(name)
module_eval(%Q{
def #{name}(*args)
notify_listeners(:#{name}, *args)
end
})
end
end
event
is a method which defines another method (new_contact
) which calls notify_listeners
from Eventful
.
and that triggers the event method in the Macro module
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
/ def
and that new method (new_contact
) is what's getting called.
It's important to understand that event
method runs only once, when the Dradis
class is parsed and loaded. It does not get run on every instantiation of Dradis
.
Upvotes: 1