Rui::Event::Listenable - base class for listenable objects
# subclassing package Car; use base qw(Rui::Event::Listenable); ... sub openFrontLeftDoor { ... # DoorOpened is an event class $self->fireEvent(DoorOpened->new( name => 'FrontLeftDoorOpened', source => $self, time => time, )); } ...
# using $car = Car->new(events => { FrontLeftDoorOpened => sub {print shift->time}, # 1st param is event FrontLeftDoorClosed => sub {print shift->time}, });
$car->addListener(FrontLeftDoorOpened => $listener = sub { my $event = shift; print 'name:'. $event->name. ' source:'. $event->source; }); $car->removeListener($listener); $car->addListener(FrontLeftDoorOpened => $listener = MyListener->new); $car->removeListener($listener); $car->addListener( FrontLeftDoorClosed => [CalledWhenDoorOpened => $listener = MyListener->new] ); $car->removeListener($listener); # listening package MyListener; ... sub handleEvent_FrontLeftDoorOpened { my ($self, $event) = @_; ... } sub CalledWhenDoorClosed { my ($self, $event) = @_; ... }
An implementation of the observer pattern. Listenable objects fire
events: different events are fired by different objects. For each event
a name and a class are defined. For example: A car class defines an
event with a name FrontLeftDoorOpened
, and an event class
CarSoftware::Event::DoorOpened
. The event name is used as the key
when adding and removing listeners, and as a hint when the listener
needs to find a listener method.
You interface with a listenable object by adding and removing listeners. Listeners are called when an event is fired, with a the Rui::Event manpage as the only parameter.
A listener is a DWIMer and can be one of the following:
handleEvent_eventName
will be called
array ref with two elements- scalar method name, object
When the listener is an object (listener type 2), the method name to be
called is computed from the event name by adding handleEvent_
in
front of the event name. For example: a car object will call the method
handleEvent_FrontLeftDoorOpened
on its listeners that are objects.
When the listener is an array ref (listener type 3), the method name (1st element) is called on the object (2nd element). When removing this type of listener, you do not remove the array ref but the listener object, i.e. exactly like you remove a type 2 listeners.
For implementing listenable functionality, all you need to do is call
$self->fireEvent($event)
to fire an event. You can also override
add/removeListener()
to modify events fired by your class.
A listenable also features support for creating listener classes, thus making it possible to create a listenable that is also a listener (as most are). These classes may listen to several events on several listenables. They will want to attach to all of them when created, and attach/detach when specific listenables added or removed. The framework helps you so:
attach()/detach()
makes or breaks all
connections to the listenables that you specify with the template method
explained below. attach()
is called by the framework when you create
your object, detach()
when you destroy it. It is up to you to call
them when the listenables are replaced, to give the framework a chance
to attach()/detach. If only one of the listenables is set or removed,
you can call attachTo()/detachFrom()
with one parameter: the name of
listenable.
If you want to use the default attach()/detach()
methods, you need to
provide one template method: getAttachments()
. The results will be
used to determine the attachments of this listener to listenable
objects. You must return a hash ref of listenable names. For each name
you provide a hash ref of event names that interest you on that
listenable, and for each event name an event handler name on your
object.
Here is an example of a car listening to its parts: the door and the sun roof:
sub getAttachments : Protected { my $self = shift; return { door => { Open => 'StateChange', Close => 'StateChange', } $self->hasSunRoof? ( sunRoof => { Open => 'Open', Close => 'Close', } ): () }; }
The car listens to the sun roof only if it has one. The car
listens to the Open and Close events of both parts. These will call the
following methods of the car: handleEvent_door_StateChanged()
,
handleEvent_sunRoof_Open()
, and handleEvent_sunRoof_Close()
.
The framework will attach/detach automatically when your listener object
is created and destroyed. However you must call
attachTo()/detachFrom()
when a door is being replaced with a new one,
or when changing the sun roof.
You must provide a getter for each listenable name in
getAttachments()
, so that the framework can find the listenable by
name. Thus in the above example you would need to provide the methods
door()
, and sunRoof()
, that return the named listenables.
getAttachments()
, so it
overrides attach()/detach()
, and implements its own mechanism,
without using getAttachments()
.
events - Optional. Hash ref. keys are event names, values are listeners.
listener - callback, object, or method/object pair.
listener - callback, object, or method/object pair.