Why not adopt me?
NAME
MooX::Role::POE::Emitter - Pluggable POE event emitter role for cows
SYNOPSIS
## A POE::Session that can broadcast events to listeners:
package My::EventEmitter;
use POE;
use Moo;
with 'MooX::Role::POE::Emitter';
sub spawn {
my ($self, %args) = @_;
$self->set_object_states(
[
$self => {
## Add some extra handlers to our Emitter:
'emitter_started' => '_emitter_started',
'emitter_stopped' => '_emitter_stopped',
},
## Include any object_states we had previously
## (e.g. states added at construction time):
(
$self->has_object_states ?
@{ $self->object_states } : ()
),
## Maybe include from named arguments, for example:
(
ref $args{object_states} eq 'ARRAY' ?
@{ $args{object_states} } : ()
),
],
);
## Start our Emitter's POE::Session:
$self->_start_emitter;
}
sub shutdown {
my ($self) = @_;
## .. do some cleanup, whatever ..
$self->_shutdown_emitter;
}
sub _emitter_started {
my ($kernel, $self) = @_[KERNEL, OBJECT];
## A POE state called when the emitter's session starts.
## (Analogous to a normal '_start' handler)
## Could load plugins, do initialization, etc.
}
sub _emitter_stopped {
## Opposite of 'emitter_started'
}
sub do_something {
my ($self, @things) = @_;
# ... do some work ...
# ... emit an event:
$self->emit( did_stuff => @things )
}
## A listening POE::Session:
package My::Listener;
use POE;
sub spawn {
# This spawn() takes an alias/session to subscribe to:
my ($self, $alias_or_sessionID) = @_;
POE::Session->create(
## Set up a Session, etc
object_states => [
$self => [
'emitted_did_stuff',
# ...
],
],
);
## Subscribe to all events from $alias_or_sessionID:
$poe_kernel->call(
$alias_or_sessionID => subscribe => 'all'
);
}
sub emitted_did_stuff {
my ($kernel, $self) = @_[KERNEL, OBJECT];
## Received 'did_stuff' from Emitter
my @things = @_[ARG0 .. $#_];
# ...
}
DESCRIPTION
Consuming this Moo::Role gives your class a POE::Session capable of processing events via loaded plugins and/or emitting them to registered "listener" sessions.
It is derived from POE::Component::Syndicator by BINGOS, HINRIK, APOCAL et al, but with more cows ;-) and a few extra features (such as anonymous coderef callbacks; see "yield"), as well as the faster plugin dispatch system that comes with MooX::Role::Pluggable.
The Emitter role consumes MooX::Role::Pluggable, making your emitter pluggable (see the MooX::Role::Pluggable documentation for plugin-related details).
You do not need to create your own POE::Session; calling "_start_emitter" will spawn one for you.
You also get some useful sugar over POE event dispatch; see "Methods".
Creating an Emitter
"SYNOPSIS" contains an emitter that uses set_$attrib methods to configure itself when spawn()
is called; attributes can, of course, be set when your Emitter is constructed:
my $emitter = MyEmitter->new(
alias => 'my_emitter',
pluggable_type_prefixes => {
NOTIFY => 'Notify',
PROCESS => 'Proc',
},
# . . .
);
Attributes
Most of these can be altered via set_$attrib methods at any time before "_start_emitter" is called. Changing an emitter's configuration after it has been started may result in undesirable behavior ;-)
Public attributes provide has_ prefixed predicates; e.g. has_event_prefix.
alias
alias specifies the POE::Kernel alias used for our POE::Session; defaults to the stringified object.
Set via set_alias. If the emitter is running, a prefixed alias_set event is emitted to notify listeners that need to know where to reach the emitter.
event_prefix
event_prefix is prepended to notification events before they are dispatched to listening sessions. It is also used for the plugin pipeline's internal events; see "_pluggable_event" in MooX::Role::Pluggable for details.
Defaults to emitted_
Set via set_event_prefix
pluggable_type_prefixes
pluggable_type_prefixes is a hash reference that can optionally be set to change the default MooX::Role::Pluggable plugin handler prefixes for PROCESS
and NOTIFY
(which default to P
and N
, respectively):
my $emitter = $class->new(
pluggable_type_prefixes => {
PROCESS => 'P',
NOTIFY => 'N',
},
);
Set via set_pluggable_type_prefixes
object_states
object_states is an array reference suitable for passing to POE::Session; the subclasses own handlers should be added to object_states prior to calling "_start_emitter".
Set via set_object_states
register_prefix
register_prefix is prepended to 'register' and 'unregister' methods called on plugins at load time (see MooX::Role::Pluggable).
Defaults to Emitter_
Set via set_register_prefix
session_id
session_id is our emitter's POE::Session ID, set when our Session is started via "_start_emitter".
shutdown_signal
shutdown_signal is the name of the POE signal that will trigger a shutdown (used to shut down multiple Emitters). See "Signals"
_start_emitter
_start_emitter() should be called on our object to spawn the actual POE::Session. It takes no arguments and should be called after the object has been configured.
_shutdown_emitter
_shutdown_emitter() must be called to terminate the Emitter's POE::Session
A 'shutdown' event will be emitted before sessions are dropped.
Listening sessions
Session event subscription
An external POE::Session can subscribe to receive events via normal POE event dispatch by sending a subscribe
:
$poe_kernel->post( $emitter->session_id,
'subscribe',
@events
);
Listening sessions are consumers; they cannot modify event arguments in any meaningful way, and will receive arguments as-normal (in @_[ARG0 .. $#_] like any other POE state). Plugins operate differently and receive references to arguments that can be modified -- see MooX::Role::Pluggable for details.
Session event unregistration
An external Session can unregister subscribed events using the same syntax as above:
$poe_kernel->post( $emitter->session_id,
'unsubscribe',
@events
);
If no events are specified, then any previously subscribed events are unregistered.
Note that unsubscribing from 'all' does not carry the same behavior; that is to say, a subscriber can subscribe/unsubscribe for 'all' separately from some set of specifically named events.
Receiving events
Events delivered to listeners
Events are delivered to subscribed listener sessions as normal POE events, with the configured "event_prefix" prepended and arguments available via @_[ARG0 .. $#_]
as normal.
sub emitted_my_event {
my ($kernel, $self) = @_[KERNEL, OBJECT];
my @args = @_[ARG0 .. $#_];
# . . .
}
See "Session event subscription" and "emit"
Events delivered to this session
The emitter's POE::Session provides a '_default' handler that redispatches unknown POE-delivered events to "process" (except for events prefixed with '_', which are reserved).
You can change this behavior by overriding '_emitter_default' -- here's a direct adaption of the example from POE::Component::Syndicator:
use Moo;
use POE;
with 'MooX::Role::POE::Emitter';
around '_emitter_default' => sub {
my $orig = shift;
my ($kernel, $self) = @_[KERNEL, OBJECT];
my ($event, $args) = @_[ARG0, ARG1];
## process(), then do something else, for example
return if $self->process( $event, @$args ) == EAT_ALL;
. . .
};
(Note that due to internal redispatch $_[SENDER] will be the Emitter's Session.)
EAT values
MooX::Role::Pluggable uses EAT_*
constants to indicate event lifetime.
If a plugin in the pipeline returns EAT_CLIENT or EAT_ALL, events are not dispatched to subscribed listening sessions; a dispatched NOTIFY event goes to your emitter's Session if it is subscribed to receive it, then to the plugin pipeline, and finally to other subscribed listener Sessions unless a plugin returned EAT_CLIENT or EAT_ALL.
See "emit" for more on dispatch behavior and event lifetime. See MooX::Role::Pluggable for details regarding plugins.
NOTIFY events
NOTIFY events are intended to be dispatched asynchronously to our own session, any loaded plugins in the pipeline, and subscribed listening sessions, respectively.
See "emit".
PROCESS events
PROCESS events are intended to be processed by the plugin pipeline immediately; these are intended for message processing and similar synchronous action handled by plugins.
Handlers for PROCESS events are prefixed with P_
See "process".
Sending events
emit
$self->emit( $event, @args );
emit() dispatches "NOTIFY events" -- these events are dispatched first to our own session (with "event_prefix" prepended), then any loaded plugins in the pipeline (with N_
prepended), then registered sessions (with "event_prefix" prepended):
## With default event_prefix:
$self->emit( 'my_event', @args )
# -> Dispatched to own session as 'emitted_my_event'
# -> Dispatched to plugin pipeline as 'N_my_event'
# -> Dispatched to registered sessions as 'emitted_my_event'
# *unless* a plugin returned EAT_CLIENT or EAT_ALL
See "Receiving events", "EAT values"
emit_now
$self->emit_now( $event, @args );
emit_now() synchronously dispatches "NOTIFY events" -- see "emit".
process
$self->process( $event, @args );
process() calls registered plugin handlers for "PROCESS events" immediately; these are not dispatched to listening sessions.
Returns the same value as "_pluggable_process" in MooX::Role::Pluggable.
See MooX::Role::Pluggable for details on pluggable event dispatch.
Methods
These methods provide easy proxy mechanisms for issuing POE events and managing timers within the context of the emitter's POE::Session.
yield
$self->yield( $poe_event, @args );
Provides an interface to POE::Kernel's yield/post() method, dispatching POE events within the context of the emitter's session.
The event can be either a named event/state dispatched to your Emitter's POE::Session:
$emitter->yield( 'some_event', @args );
... or an anonymous coderef, which is executed as if it were a named POE state belonging to your Emitter:
$emitter->yield( sub {
## $_[OBJECT] is the Emitter's object:
my ($kernel, $self) = @_[KERNEL, OBJECT];
my @params = @_[ARG0 .. $#_];
## $_[STATE] is the current coderef
## Yield ourselves again, for example:
$self->yield( $_[STATE], @new_args )
if $some_condition;
}, $some, $args );
Inside an anonymous coderef callback such as shown above, $_[OBJECT]
is the Emitter's $self
object and $_[STATE]
contains the callback coderef itself.
call
$self->call( $poe_event, @args );
The synchronous counterpart to "yield".
timer
my $alarm_id = $self->timer(
$delayed_seconds,
$event,
@args
);
Set a timer in the context of the emitter's POE::Session. Returns the POE alarm ID.
The event can be either a named event/state or an anonymous coderef (see "yield").
A prefixed ("event_prefix") 'timer_set' event is emitted when a timer is set. Arguments are the alarm ID, the event name or coderef, the delay time, and any event parameters, respectively.
timer_del
$self->timer_del( $alarm_id );
Clears a pending "timer".
A prefixed ("event_prefix") 'timer_deleted' event is emitted when a timer is deleted. Arguments are the removed alarm ID, the event name or coderef, and any event parameters, respectively.
Signals
Shutdown Signal
The attribute "shutdown_signal" defines a POE signal that will trigger a shutdown; it defaults to SHUTDOWN_EMITTER
:
## Shutdown *all* emitters (with a default shutdown_signal()):
$poe_kernel->signal( $poe_kernel, 'SHUTDOWN_EMITTER' );
See "Signal Watcher Methods" in POE::Kernel for details on POE signals.
SEE ALSO
For details regarding POE, see POE, POE::Kernel, POE::Session
For details regarding Moo classes and Roles, see Moo, Moo::Role, Role::Tiny
AUTHOR
Jon Portnoy <avenj@cobaltirc.org>
Written from the ground up, but conceptually derived from POE::Component::Syndicator-0.06 copyright Hinrik Orn Sigurosson (HINRIK), Chris Williams (BINGOS), APOCAL et al -- that will probably do you for non-Moo(se) use cases; I needed something cow-like that worked with MooX::Role::Pluggable.
Licensed under the same terms as Perl 5; see the license that came with your Perl distribution for details.