File Coverage

blib/lib/MooX/Role/POE/Emitter.pm
Criterion Covered Total %
statement 183 196 93.3
branch 45 66 68.1
condition 11 19 57.8
subroutine 41 42 97.6
pod 7 7 100.0
total 287 330 86.9


line stmt bran cond sub pod time code
1             package MooX::Role::POE::Emitter;
2             $MooX::Role::POE::Emitter::VERSION = '1.001002';
3 4     4   45029 use strictures 2;
  4         39  
  4         197  
4              
5 4     4   1237 use feature 'state';
  4         8  
  4         445  
6 4     4   25 use Carp;
  4         7  
  4         365  
7 4     4   26 use Scalar::Util 'reftype';
  4         5  
  4         403  
8              
9 4     4   2835 use List::Objects::WithUtils;
  4         4046  
  4         30  
10 4     4   402340 use List::Objects::Types -all;
  4         425830  
  4         74  
11 4     4   27382 use Types::Standard -types;
  4         11  
  4         43  
12              
13 4     4   19654 use MooX::Role::Pluggable::Constants;
  4         1019  
  4         361  
14              
15 4     4   739 use POE;
  4         52998  
  4         38  
16              
17             sub E_TAG () { 'Emitter Running' }
18              
19             =pod
20              
21             =for Pod::Coverage E_TAG
22              
23             =cut
24              
25              
26 4     4   74083 use Moo::Role;
  4         10  
  4         38  
27             with 'MooX::Role::Pluggable';
28              
29              
30             has alias => (
31             lazy => 1,
32             is => 'ro',
33             isa => Str,
34             predicate => 'has_alias',
35             writer => 'set_alias',
36             default => sub { my $self = shift; "$self" },
37             );
38              
39             around set_alias => sub {
40             my ($orig, $self, $value) = @_;
41              
42             if ( $poe_kernel->alias_resolve( $self->session_id ) ) {
43             $self->call( __emitter_reset_alias => $value );
44             $self->emit( $self->event_prefix . 'alias_set' => $value );
45             }
46              
47             $self->$orig($value)
48             };
49              
50             has event_prefix => (
51             lazy => 1,
52             is => 'ro',
53             isa => Str,
54             predicate => 'has_event_prefix',
55             writer => 'set_event_prefix',
56             default => sub { 'emitted_' },
57             );
58              
59             has pluggable_type_prefixes => (
60             ## Optionally remap PROCESS / NOTIFY types
61             lazy => 1,
62             is => 'ro',
63             isa => HashObj,
64             coerce => 1,
65             predicate => 'has_pluggable_type_prefixes',
66             writer => 'set_pluggable_type_prefixes',
67             default => sub {
68             hash( PROCESS => 'P', NOTIFY => 'N' )
69             },
70             );
71              
72              
73             has object_states => (
74             lazy => 1,
75             is => 'ro',
76             isa => ArrayObj,
77             coerce => 1,
78             predicate => 'has_object_states',
79             writer => 'set_object_states',
80             trigger => 1,
81             default => sub { array },
82             );
83              
84             sub _trigger_object_states {
85 7     7   23187 my ($self, $states) = @_;
86              
87 7 50       38 $states = array(%$states) if reftype $states eq 'HASH';
88              
89 7 50 33     52 confess "object_states() should be an ARRAY or HASH"
90             unless ref $states and reftype $states eq 'ARRAY';
91              
92 7 50       25 $states = array(@$states) unless is_ArrayObj $states;
93              
94             state $disallowed = array( qw/
95             _start
96             _stop
97             _default
98             emit
99             register
100             unregister
101             subscribe
102             unsubscribe
103 7     24   201 / )->map(sub { $_ => 1 })->inflate;
  24         180  
104              
105 7         377 my $itr = $states->natatime(2);
106 7         83 while (my (undef, $events) = $itr->()) {
107 7 0       67 my $evarr = reftype $events eq 'ARRAY' ? array(@$events)
    50          
108             : reftype $events eq 'HASH' ? array(keys %$events)
109             : confess "Expected ARRAY or HASH but got $events";
110             $evarr->map(
111 12 100   12   101 sub { confess "Disallowed handler: $_" if $disallowed->exists($_) }
112 7         81 );
113             }
114             }
115              
116              
117             has register_prefix => (
118             lazy => 1,
119             is => 'ro',
120             isa => Str,
121             predicate => 'has_register_prefix',
122             writer => 'set_register_prefix',
123             ## Emitter_register / Emitter_unregister
124             default => sub { 'Emitter_' },
125             );
126              
127             has session_id => (
128             init_arg => undef,
129             lazy => 1,
130             is => 'ro',
131             isa => Defined,
132             predicate => 'has_session_id',
133             writer => 'set_session_id',
134             default => sub { -1 },
135             );
136              
137             has shutdown_signal => (
138             lazy => 1,
139             is => 'ro',
140             isa => Str,
141             predicate => 'has_shutdown_signal',
142             writer => 'set_shutdown_signal',
143             default => sub { 'SHUTDOWN_EMITTER' },
144             );
145              
146             has __emitter_reg_sessions => (
147             lazy => 1,
148             is => 'ro',
149             isa => TypedHash[Object],
150             default => sub { hash_of Object },
151             );
152              
153              
154             has __emitter_reg_events => (
155             ## ->{ $event }->{ $session_id } = 1
156             lazy => 1,
157             is => 'ro',
158             isa => TypedHash[ TypedHash[Int] ],
159             coerce => 1,
160             default => sub { hash_of TypedHash[Int] },
161             );
162              
163              
164             sub _start_emitter {
165             ## Call to spawn Session.
166             ## my $emitter = MyClass->new(
167             ## alias => Emitter session alias
168             ## event_prefix => Session event prefix (emitted_)
169             ## register_prefix => _register/_unregister prefix (Emitter_)
170             ## object_states => Extra object_states for Session
171             ## )->_start_emitter();
172 4     4   3984 my ($self) = @_;
173              
174 4         11 my %types;
175 4 100       27 if ( $self->has_pluggable_type_prefixes ) {
176 1   50     4 $types{PROCESS} = $self->pluggable_type_prefixes->{PROCESS} ||= 'P';
177 1   50     997 $types{NOTIFY} = $self->pluggable_type_prefixes->{NOTIFY} ||= 'N';
178             } else {
179 3         44 %types = ( PROCESS => 'P', NOTIFY => 'N' );
180             }
181              
182 4         55 $self->_pluggable_init(
183             event_prefix => $self->event_prefix,
184             reg_prefix => $self->register_prefix,
185             types => \%types,
186             );
187              
188 4 50       3790 POE::Session->create(
189             object_states => [
190              
191             $self => {
192             '_start' => '__emitter_start',
193             '_stop' => '__emitter_stop',
194             'shutdown_emitter' => '__shutdown_emitter',
195              
196             'register' => '__emitter_register',
197             'subscribe' => '__emitter_register',
198             'unregister' => '__emitter_unregister',
199             'unsubscribe' => '__emitter_unregister',
200             'emit' => '__emitter_notify',
201              
202             '_default' => '__emitter_disp_default',
203             '__emitter_real_default' => '_emitter_default',
204             },
205              
206             $self => [ qw/
207             __emitter_notify
208              
209             __emitter_timer_set
210             __emitter_timer_del
211              
212             __emitter_sigdie
213              
214             __emitter_reset_alias
215              
216             __emitter_sig_shutdown
217             / ],
218              
219             (
220             $self->has_object_states ? $self->object_states->all : ()
221             ),
222              
223             ],
224             );
225              
226 4         727 $self
227             }
228              
229             around '_pluggable_event' => sub {
230             my ($orig, $self) = splice @_, 0, 2;
231              
232             ## Overriden from Role::Pluggable
233             ## Receives plugin_error, plugin_add etc
234             ## Redispatch via emit()
235              
236             $self->emit( @_ );
237             };
238              
239              
240             ### Public:
241              
242             sub timer {
243 4     4 1 531 my ($self, $time, $event, @args) = @_;
244              
245 4 100 66     112 confess "timer() expected at least a time and event name"
246             unless defined $time and defined $event;
247              
248 3         6 $self->call( __emitter_timer_set => $time, $event, @args )
249             }
250              
251             sub __emitter_timer_set {
252 3     3   193 my ($kernel, $self) = @_[KERNEL, OBJECT];
253 3         6 my ($time, $event, @args) = @_[ARG0 .. $#_];
254              
255 3         9 my $alarm_id = $poe_kernel->delay_set( $event, $time, @args );
256              
257 3 50       147 $self->emit( $self->event_prefix . 'timer_set',
258             $alarm_id,
259             $event,
260             $time,
261             @args
262             ) if $alarm_id;
263              
264 3         8 $alarm_id
265             }
266              
267             sub timer_del {
268 2     2 1 253 my ($self, $alarm_id) = @_;
269              
270 2 100       99 confess "timer_del() expects an alarm ID"
271             unless defined $alarm_id;
272              
273 1         6 $self->call( __emitter_timer_del => $alarm_id );
274             }
275              
276             sub __emitter_timer_del {
277 1     1   63 my ($kernel, $self, $alarm_id) = @_[KERNEL, OBJECT, ARG0];
278              
279 1         5 my @deleted = $poe_kernel->alarm_remove($alarm_id);
280 1 50       36 return unless @deleted;
281              
282 0         0 my ($event, undef, $params) = @deleted;
283              
284 0 0       0 $self->emit( $self->event_prefix . 'timer_deleted',
285             $alarm_id,
286             $event,
287 0         0 @{ $params || [] }
288             );
289              
290 0         0 $params
291             }
292              
293             ## yield/call provide post()/call() frontends.
294             sub yield {
295 25     25 1 1421 my ($self, @args) = @_;
296              
297 25         74 $poe_kernel->post( $self->session_id, @args );
298              
299 25         2568 $self
300             }
301              
302             sub call {
303 39     39 1 245 my ($self, @args) = @_;
304              
305 39         131 $poe_kernel->call( $self->session_id, @args );
306              
307 39         2419 $self
308             }
309              
310             sub emit {
311             ## Async NOTIFY event dispatch.
312 18     18 1 996 my ($self, $event, @args) = @_;
313              
314 18         53 $self->yield( __emitter_notify => $event, @args );
315              
316 18         32 $self
317             }
318              
319             sub emit_now {
320             ## Synchronous NOTIFY event dispatch.
321 1     1 1 5 my ($self, $event, @args) = @_;
322              
323 1         5 $self->call( __emitter_notify => $event, @args );
324              
325 1         2 $self
326             }
327              
328             sub process {
329 11     11 1 1040 my ($self, $event, @args) = @_;
330             ## Dispatch PROCESS events.
331             ## process() events should _pluggable_process immediately
332             ## and return the EAT value.
333              
334             ## Dispatched to P_$event :
335 11         41 $self->_pluggable_process( PROCESS => $event, \@args )
336             }
337              
338              
339              
340             ## Session ref-counting bits.
341             { package MooX::Role::POE::Emitter::RegisteredSession;
342             $MooX::Role::POE::Emitter::RegisteredSession::VERSION = '1.001002';
343 4     4   10176 use Moo;
  4         10  
  4         36  
344             has [qw/id refcount/] => ( is => 'rw', required => 1 );
345             }
346              
347             sub __get_ses_refc {
348 9     9   133 my ($self, $sess_id) = @_;
349 9         29 my $regsess_obj = $self->__emitter_reg_sessions->get($sess_id);
350 9 50       360 return unless $regsess_obj;
351 9         60 $regsess_obj->refcount
352             }
353              
354             sub __reg_ses_id {
355 9     9   19 my ($self, $sess_id) = @_;
356 9 100       42 return if $self->__emitter_reg_sessions->exists($sess_id);
357 5         2594 $self->__emitter_reg_sessions->set($sess_id =>
358             MooX::Role::POE::Emitter::RegisteredSession->new(
359             id => $sess_id,
360             refcount => 0
361             )
362             );
363             }
364              
365             sub __incr_ses_refc {
366 9     9   22 my ($self, $sess_id) = @_;
367              
368 9         29 my $regsess_obj = $self->__emitter_reg_sessions->get($sess_id);
369 9 50       370 unless (defined $regsess_obj) {
370 0         0 confess "BUG; attempted to increase nonexistant refcount for '$sess_id'";
371             }
372              
373 9         30 $self->__emitter_reg_sessions->set($sess_id =>
374             MooX::Role::POE::Emitter::RegisteredSession->new(
375             id => $sess_id,
376             refcount => $regsess_obj->refcount + 1,
377             )
378             );
379              
380 9         914 $self->__emitter_reg_sessions->get($sess_id)->refcount
381             }
382              
383             sub __decr_ses_refc {
384 1     1   4 my ($self, $sess_id) = @_;
385              
386 1         6 my $regsess_obj = $self->__emitter_reg_sessions->get($sess_id);
387 1 50       59 unless (defined $regsess_obj) {
388 0         0 confess "BUG; attempted to decrease nonexistant refcount for '$sess_id'"
389             }
390              
391             $self->__emitter_reg_sessions->set($sess_id =>
392 1         5 do {
393 1         45 my $refc = $regsess_obj->refcount - 1;
394 1 50       5 $refc = 0 if $refc < 0; # FIXME delete (and return above) instead?
395 1         34 MooX::Role::POE::Emitter::RegisteredSession->new(
396             id => $sess_id,
397             refcount => $refc,
398             )
399             },
400             );
401             }
402              
403             sub __emitter_drop_sessions {
404 12     12   28 my ($self) = @_;
405              
406 12         39 for my $id ($self->__emitter_reg_sessions->keys->all) {
407 4         286 my $count = $self->__get_ses_refc($id);
408 4         20 while ( $count-- > 0 ) {
409 8         180 $poe_kernel->refcount_decrement( $id, E_TAG )
410             }
411              
412 4         109 $self->__emitter_reg_sessions->delete($id)
413             }
414              
415             1
416 12         774 }
417              
418              
419             ## Our Session's handlers:
420              
421             sub __emitter_notify {
422             ## Dispatch a NOTIFY event
423 19     19   3378 my ($kernel, $self) = @_[KERNEL, OBJECT];
424 19         67 my ($event, @args) = @_[ARG0 .. $#_];
425              
426 19         64 my $prefix = $self->event_prefix;
427              
428             ## May have event_prefix (such as $prefix.'plugin_error')
429 19 100       797 substr($event, 0, length($prefix), '') if index($event, $prefix) == 0;
430              
431 19         23 my %sessions;
432              
433 19         39 for my $registered_ev ('all', $event) {
434 38 100       297 if (my $sess_hash = $self->__emitter_reg_events->get($registered_ev)) {
435 28     28   794 $sess_hash->keys->visit(sub { $sessions{$_} = 1 })
436 19         774 }
437             }
438              
439 19         823 my $meth = $prefix . $event;
440              
441             ## Our own session will get ->event_prefix . $event first
442 19 100       79 $kernel->call( $_[SESSION], $meth, @args )
443             if delete $sessions{ $_[SESSION]->ID };
444              
445             ## Dispatched to N_$event after our Session has been notified:
446 19 100       227 unless ( $self->_pluggable_process('NOTIFY', $event, \@args) == EAT_ALL ) {
447             ## Notify subscribed sessions.
448 17         4011 $kernel->call( $_ => $meth, @args ) for keys %sessions;
449             }
450              
451             ## Received emitted 'shutdown', drop sessions.
452 19 100       1486 $self->__emitter_drop_sessions if $event eq 'shutdown';
453             }
454              
455             sub __emitter_start {
456             ## _start handler
457 4     4   3709 my ($kernel, $self) = @_[KERNEL, OBJECT];
458 4         12 my ($session, $sender) = @_[SESSION, SENDER];
459              
460 4         16 $self->set_session_id( $session->ID );
461              
462 4         2233 $kernel->alias_set( $self->alias );
463              
464 4         1907 $kernel->sig( DIE => '__emitter_sigdie' );
465 4         165 $kernel->sig( $self->shutdown_signal => '__emitter_sig_shutdown' );
466              
467 4 50       188 unless ($sender == $kernel) {
468             ## Have a parent session.
469 4         19 my $s_id = $sender->ID;
470 4         32 $kernel->refcount_increment( $s_id, E_TAG );
471 4         148 $self->__reg_ses_id( $s_id );
472 4         4207 $self->__incr_ses_refc( $s_id );
473              
474             ## subscribe parent session to all notification events
475 4         203 $self->__emitter_reg_events->{all}->{ $s_id } = 1;
476              
477             ## Detach child session.
478 4         9594 $kernel->detach_myself;
479             }
480              
481 4         251 $self->call('emitter_started');
482              
483 4         27 $self
484             }
485              
486             sub __emitter_reset_alias {
487 1     1   95 my ($kernel, $self) = @_[KERNEL, OBJECT];
488 1         7 $kernel->alias_set( $_[ARG0] );
489             }
490              
491             sub __emitter_disp_default {
492 18     18   1530 my ($kernel, $self) = @_[KERNEL, OBJECT];
493 18         39 my ($event, $args) = @_[ARG0, ARG1];
494              
495 18 100       54 if (ref $event eq 'CODE') {
496             ## Anonymous coderef callback.
497             ## Cute trick from dngor:
498             ## - Shove arguments back into @_
499             ## (starting at ARG0 and replacing ARG0/ARG1)
500             ## - Set $_[STATE] to our coderef
501             ## (callback sub can retrieve itself via $_[STATE])
502             ## - Replace current subroutine
503 2         9 splice @_, ARG0, 2, @$args;
504 2         3 $_[STATE] = $event;
505 2         6 goto $event
506             } else {
507 16         50 $self->call( __emitter_real_default => $event, $args );
508             }
509             }
510              
511             sub _emitter_default {
512 16     16   2058 my ($kernel, $self) = @_[KERNEL, OBJECT];
513 16         35 my ($event, $args) = @_[ARG0, ARG1];
514              
515             ## Session received an unknown event.
516             ## Dispatch it to any appropriate P_$event handlers.
517              
518 16 100 66     159 $self->process( $event, @$args )
      66        
519             unless index($event, '_') == 0
520             or index($event, 'emitter_') == 0
521             and $event =~ /(?:started|stopped)$/;
522             }
523              
524             sub __emitter_sig_shutdown {
525 4     4   736 my ($kernel, $self) = @_[KERNEL, OBJECT];
526 4         12 $self->yield( shutdown_emitter => @_[ARG2 .. $#_] )
527             }
528              
529             sub __emitter_sigdie {
530 0     0   0 my ($kernel, $self) = @_[KERNEL, OBJECT];
531 0         0 my $exh = $_[ARG1];
532              
533 0         0 my $event = $exh->{event};
534 0         0 my $dest_id = $exh->{dest_session}->ID;
535 0         0 my $errstr = $exh->{error_str};
536              
537 0         0 warn
538             "SIG_DIE: Event '$event' session '$dest_id'\n",
539             " exception: $errstr\n";
540              
541 0         0 $kernel->sig_handled;
542             }
543              
544             sub __emitter_stop {
545             ## _stop handler
546 4     4   1961 my ($kernel, $self) = @_[KERNEL, OBJECT];
547              
548 4         22 $self->call('emitter_stopped');
549             }
550              
551             sub _shutdown_emitter {
552             ## Opposite of _start_emitter
553 1     1   95 my $self = shift;
554              
555 1         4 $self->call( shutdown_emitter => @_ );
556              
557 1         6 1
558             }
559              
560             sub __shutdown_emitter {
561 6     6   484 my ($kernel, $self) = @_[KERNEL, OBJECT];
562              
563 6         25 $kernel->alarm_remove_all;
564              
565             ## Destroy plugin pipeline.
566 6         381 $self->_pluggable_destroy;
567              
568             ## Notify sessions.
569 6         925 $self->emit( shutdown => @_[ARG0 .. $#_] );
570              
571             ## Drop sessions and we're spent.
572 6         21 $self->call( unsubscribe => () );
573 6         19 $self->__emitter_drop_sessions;
574             }
575              
576              
577             ## Handlers for listener sessions.
578             sub __emitter_register {
579 5     5   2390 my ($kernel, $self, $sender) = @_[KERNEL, OBJECT, SENDER];
580 5         17 my @events = @_[ARG0 .. $#_];
581              
582 5 100       24 @events = 'all' unless @events;
583              
584 5         15 my $s_id = $sender->ID;
585              
586             ## Add to our known sessions.
587 5         28 $self->__reg_ses_id( $s_id );
588              
589 5         310 for my $event (@events) {
590             ## Add session to registered event lists.
591 5         20 $self->__emitter_reg_events->{$event}->{$s_id} = 1;
592              
593             ## Make sure registered session hangs around
594             ## (until _unregister or shutdown)
595 5 50 66     280 $kernel->refcount_increment( $s_id, E_TAG )
596             unless $s_id == $self->session_id
597             or $self->__get_ses_refc($s_id);
598              
599 5         50 $self->__incr_ses_refc( $s_id );
600             }
601              
602 5         237 $kernel->post( $s_id => $self->event_prefix . 'registered', $self )
603             }
604              
605             sub __emitter_unregister {
606 6     6   453 my ($kernel, $self, $sender) = @_[KERNEL, OBJECT, SENDER];
607 6         82 my @events = @_[ARG0 .. $#_];
608              
609             ## - An unsub without any arguments means "stop sending all events I
610             ## have subscribed to"
611             ## - An unsub for 'all' means "stop sending events I haven't asked for
612             ## by name"
613              
614 6 50       40 @events = $self->__emitter_reg_events->keys->all unless @events;
615              
616 6         485 my $s_id = $sender->ID;
617              
618 6         33 EV: for my $event (@events) {
619             # intentional no Lowu, leave me for autoviv:
620 6 100       20 unless (delete $self->__emitter_reg_events->{$event}->{$s_id}) {
621             next EV
622 5         336 }
623              
624             # Sessions left for this event?
625 1 50       71 $self->__emitter_reg_events->delete($event)
626             if $self->__emitter_reg_events->get($event)->is_empty;
627              
628 1         80 $self->__decr_ses_refc($s_id);
629              
630 1 50       114 unless ($self->__get_ses_refc($s_id)) {
631             ## No events left for this session.
632 1         5 $self->__emitter_reg_sessions->delete($s_id);
633              
634 1 50       87 $kernel->refcount_decrement( $s_id, E_TAG )
635             unless $_[SESSION] == $sender;
636             }
637              
638             } ## EV
639             }
640              
641             1;
642              
643              
644             =pod
645              
646             =for Pod::Coverage has_\S+
647              
648             =head1 NAME
649              
650             MooX::Role::POE::Emitter - Pluggable POE event emitter role for cows
651              
652             =head1 SYNOPSIS
653              
654             ## A POE::Session that can broadcast events to listeners:
655             package My::EventEmitter;
656             use POE;
657             use Moo;
658             with 'MooX::Role::POE::Emitter';
659              
660             sub spawn {
661             my ($self, %args) = @_;
662              
663             $self->set_object_states(
664             [
665             $self => {
666             ## Add some extra handlers to our Emitter:
667             'emitter_started' => '_emitter_started',
668             'emitter_stopped' => '_emitter_stopped',
669             },
670              
671             ## Include any object_states we had previously
672             ## (e.g. states added at construction time):
673             (
674             $self->has_object_states ?
675             @{ $self->object_states } : ()
676             ),
677              
678             ## Maybe include from named arguments, for example:
679             (
680             ref $args{object_states} eq 'ARRAY' ?
681             @{ $args{object_states} } : ()
682             ),
683             ],
684             );
685              
686             ## Start our Emitter's POE::Session:
687             $self->_start_emitter;
688             }
689              
690             sub shutdown {
691             my ($self) = @_;
692             ## .. do some cleanup, whatever ..
693             $self->_shutdown_emitter;
694             }
695              
696             sub _emitter_started {
697             my ($kernel, $self) = @_[KERNEL, OBJECT];
698             ## A POE state called when the emitter's session starts.
699             ## (Analogous to a normal '_start' handler)
700             ## Could load plugins, do initialization, etc.
701             }
702              
703             sub _emitter_stopped {
704             ## Opposite of 'emitter_started'
705             }
706              
707             sub do_something {
708             my ($self, @things) = @_;
709             # ... do some work ...
710             # ... emit an event:
711             $self->emit( did_stuff => @things )
712             }
713              
714             ## A listening POE::Session:
715             package My::Listener;
716             use POE;
717              
718             sub spawn {
719             # This spawn() takes an alias/session to subscribe to:
720             my ($self, $alias_or_sessionID) = @_;
721              
722             POE::Session->create(
723             ## Set up a Session, etc
724             object_states => [
725             $self => [
726             'emitted_did_stuff',
727             # ...
728             ],
729             ],
730             );
731              
732             ## Subscribe to all events from $alias_or_sessionID:
733             $poe_kernel->call(
734             $alias_or_sessionID => subscribe => 'all'
735             );
736             }
737              
738             sub emitted_did_stuff {
739             my ($kernel, $self) = @_[KERNEL, OBJECT];
740             ## Received 'did_stuff' from Emitter
741             my @things = @_[ARG0 .. $#_];
742             # ...
743             }
744              
745             =head1 DESCRIPTION
746              
747             Consuming this L gives your class a L capable of
748             processing events via loaded plugins and/or emitting them to registered
749             "listener" sessions.
750              
751             It is derived from L by BINGOS, HINRIK, APOCAL
752             et al, but with more cows ;-) and a few extra features (such as anonymous
753             coderef callbacks; see L), as well as the
754             faster plugin dispatch system that comes with L.
755              
756             The Emitter role consumes L,
757             making your emitter pluggable (see the
758             L documentation for plugin-related details).
759              
760             You do not need to create your own L; calling
761             L will spawn one for you.
762              
763             You also get some useful sugar over POE event dispatch; see L.
764              
765             =head2 Creating an Emitter
766              
767             L contains an emitter that uses B methods to
768             configure itself when C is called; attributes can, of course,
769             be set when your Emitter is constructed:
770              
771             my $emitter = MyEmitter->new(
772             alias => 'my_emitter',
773             pluggable_type_prefixes => {
774             NOTIFY => 'Notify',
775             PROCESS => 'Proc',
776             },
777             # . . .
778             );
779              
780             =head3 Attributes
781              
782             Most of these can be altered via B methods at any time before
783             L is called. Changing an emitter's configuration after it has
784             been started may result in undesirable behavior ;-)
785              
786             Public attributes provide B prefixed predicates; e.g.
787             B.
788              
789             =head4 alias
790              
791             B specifies the POE::Kernel alias used for our L;
792             defaults to the stringified object.
793              
794             Set via B. If the emitter is running, a prefixed B
795             event is emitted to notify listeners that need to know where to reach the emitter.
796              
797             =head4 event_prefix
798              
799             B is prepended to notification events before they are
800             dispatched to listening sessions. It is also used for the plugin
801             pipeline's internal events; see L
802             for details.
803              
804             Defaults to C
805              
806             Set via B
807              
808             =head4 pluggable_type_prefixes
809              
810             B is a hash reference that can optionally be set
811             to change the default L plugin handler prefixes for
812             C and C (which default to C

and C, respectively):

813              
814             my $emitter = $class->new(
815             pluggable_type_prefixes => {
816             PROCESS => 'P',
817             NOTIFY => 'N',
818             },
819             );
820              
821             Set via B
822              
823             =head4 object_states
824              
825             B is an array reference suitable for passing to
826             L; the subclasses own handlers should be added to
827             B prior to calling L.
828              
829             Set via B
830              
831             =head4 register_prefix
832              
833             B is prepended to 'register' and 'unregister' methods
834             called on plugins at load time (see L).
835              
836             Defaults to I
837              
838             Set via B
839              
840             =head4 session_id
841              
842             B is our emitter's L ID, set when our Session is
843             started via L.
844              
845             =head4 shutdown_signal
846              
847             B is the name of the L signal that will trigger a
848             shutdown (used to shut down multiple Emitters). See L
849              
850             =head3 _start_emitter
851              
852             B<_start_emitter()> should be called on our object to spawn the actual
853             L. It takes no arguments and should be called after the
854             object has been configured.
855              
856             =head3 _shutdown_emitter
857              
858             B<_shutdown_emitter()> must be called to terminate the Emitter's
859             L
860              
861             A 'shutdown' event will be emitted before sessions are dropped.
862              
863             =head2 Listening sessions
864              
865             =head3 Session event subscription
866              
867             An external L can subscribe to receive events via
868             normal POE event dispatch by sending a C:
869              
870             $poe_kernel->post( $emitter->session_id,
871             'subscribe',
872             @events
873             );
874              
875             Listening sessions are consumers; they cannot modify event arguments in
876             any meaningful way, and will receive arguments as-normal (in @_[ARG0 ..
877             $#_] like any other POE state). Plugins operate differently and receive
878             references to arguments that can be modified -- see
879             L for details.
880              
881             =head3 Session event unregistration
882              
883             An external Session can unregister subscribed events using the same syntax
884             as above:
885              
886             $poe_kernel->post( $emitter->session_id,
887             'unsubscribe',
888             @events
889             );
890              
891             If no events are specified, then any previously subscribed events are
892             unregistered.
893              
894             Note that unsubscribing from 'all' does not carry the same behavior; that
895             is to say, a subscriber can subscribe/unsubscribe for 'all' separately from
896             some set of specifically named events.
897              
898             =head2 Receiving events
899              
900             =head3 Events delivered to listeners
901              
902             Events are delivered to subscribed listener sessions as normal POE events,
903             with the configured L prepended and arguments available via
904             C< @_[ARG0 .. $#_] > as normal.
905              
906             sub emitted_my_event {
907             my ($kernel, $self) = @_[KERNEL, OBJECT];
908             my @args = @_[ARG0 .. $#_];
909             # . . .
910             }
911              
912             See L and L
913              
914             =head3 Events delivered to this session
915              
916             The emitter's L provides a '_default' handler that
917             redispatches unknown POE-delivered events to L
918             (except for events prefixed with '_', which are reserved).
919              
920             You can change this behavior by overriding '_emitter_default' -- here's a
921             direct adaption of the example from L:
922              
923             use Moo;
924             use POE;
925             with 'MooX::Role::POE::Emitter';
926             around '_emitter_default' => sub {
927             my $orig = shift;
928             my ($kernel, $self) = @_[KERNEL, OBJECT];
929             my ($event, $args) = @_[ARG0, ARG1];
930              
931             ## process(), then do something else, for example
932             return if $self->process( $event, @$args ) == EAT_ALL;
933              
934             . . .
935             };
936              
937             (Note that due to internal redispatch $_[SENDER] will be the Emitter's
938             Session.)
939              
940             =head2 EAT values
941              
942             L uses C constants to indicate event
943             lifetime.
944              
945             If a plugin in the pipeline returns EAT_CLIENT or EAT_ALL, events
946             are not dispatched to subscribed listening sessions; a dispatched NOTIFY
947             event goes to your emitter's Session if it is subscribed to receive it,
948             then to the plugin pipeline, and finally to other subscribed listener
949             Sessions B a plugin returned EAT_CLIENT or EAT_ALL.
950              
951             See L for more on dispatch behavior and event lifetime. See
952             L for details regarding plugins.
953              
954             =head3 NOTIFY events
955              
956             B events are intended to be dispatched asynchronously to our own
957             session, any loaded plugins in the pipeline, and subscribed listening
958             sessions, respectively.
959              
960             See L.
961              
962             =head3 PROCESS events
963              
964             B events are intended to be processed by the plugin pipeline
965             immediately; these are intended for message processing and similar
966             synchronous action handled by plugins.
967              
968             Handlers for B events are prefixed with C
969              
970             See L.
971              
972             =head2 Sending events
973              
974             =head3 emit
975              
976             $self->emit( $event, @args );
977              
978             B dispatches L -- these events are dispatched
979             first to our own session (with L prepended), then any
980             loaded plugins in the pipeline (with C prepended), then registered
981             sessions (with L prepended):
982              
983             ## With default event_prefix:
984             $self->emit( 'my_event', @args )
985             # -> Dispatched to own session as 'emitted_my_event'
986             # -> Dispatched to plugin pipeline as 'N_my_event'
987             # -> Dispatched to registered sessions as 'emitted_my_event'
988             # *unless* a plugin returned EAT_CLIENT or EAT_ALL
989              
990             See L, L
991              
992             =head3 emit_now
993              
994             $self->emit_now( $event, @args );
995              
996             B synchronously dispatches L -- see
997             L.
998              
999             =head3 process
1000              
1001             $self->process( $event, @args );
1002              
1003             B calls registered plugin handlers for L
1004             immediately; these are B dispatched to listening sessions.
1005              
1006             Returns the same value as L.
1007              
1008             See L for details on pluggable
1009             event dispatch.
1010              
1011             =head2 Methods
1012              
1013             These methods provide easy proxy mechanisms for issuing POE events and
1014             managing timers within the context of the emitter's L.
1015              
1016             =head3 yield
1017              
1018             $self->yield( $poe_event, @args );
1019              
1020             Provides an interface to L's yield/post() method, dispatching
1021             POE events within the context of the emitter's session.
1022              
1023             The event can be either a named event/state dispatched to your Emitter's
1024             L:
1025              
1026             $emitter->yield( 'some_event', @args );
1027              
1028             ... or an anonymous coderef, which is executed as if it were a named
1029             POE state belonging to your Emitter:
1030              
1031             $emitter->yield( sub {
1032             ## $_[OBJECT] is the Emitter's object:
1033             my ($kernel, $self) = @_[KERNEL, OBJECT];
1034             my @params = @_[ARG0 .. $#_];
1035              
1036             ## $_[STATE] is the current coderef
1037             ## Yield ourselves again, for example:
1038             $self->yield( $_[STATE], @new_args )
1039             if $some_condition;
1040             }, $some, $args );
1041              
1042             Inside an anonymous coderef callback such as shown above, C<$_[OBJECT]> is
1043             the Emitter's C<$self> object and C<$_[STATE]> contains the callback
1044             coderef itself.
1045              
1046             =head3 call
1047              
1048             $self->call( $poe_event, @args );
1049              
1050             The synchronous counterpart to L.
1051              
1052             =head3 timer
1053              
1054             my $alarm_id = $self->timer(
1055             $delayed_seconds,
1056             $event,
1057             @args
1058             );
1059              
1060             Set a timer in the context of the emitter's L. Returns the
1061             POE alarm ID.
1062              
1063             The event can be either a named event/state or an anonymous coderef (see
1064             L).
1065              
1066             A prefixed (L) 'timer_set' event is emitted when a timer is
1067             set. Arguments are the alarm ID, the event name or coderef, the delay time,
1068             and any event parameters, respectively.
1069              
1070             =head3 timer_del
1071              
1072             $self->timer_del( $alarm_id );
1073              
1074             Clears a pending L.
1075              
1076             A prefixed (L) 'timer_deleted' event is emitted when a timer
1077             is deleted. Arguments are the removed alarm ID, the event name or coderef,
1078             and any event parameters, respectively.
1079              
1080             =head2 Signals
1081              
1082             =head3 Shutdown Signal
1083              
1084             The attribute L defines a POE signal that will trigger a
1085             shutdown; it defaults to C:
1086              
1087             ## Shutdown *all* emitters (with a default shutdown_signal()):
1088             $poe_kernel->signal( $poe_kernel, 'SHUTDOWN_EMITTER' );
1089              
1090             See L for details on L signals.
1091              
1092             =head1 SEE ALSO
1093              
1094             For details regarding POE, see L, L, L
1095              
1096             For details regarding Moo classes and Roles, see L, L,
1097             L
1098              
1099             =head1 AUTHOR
1100              
1101             Jon Portnoy
1102              
1103             Written from the ground up, but conceptually derived from
1104             L-0.06 copyright Hinrik Orn Sigurosson (HINRIK),
1105             Chris Williams (BINGOS), APOCAL et al -- that will probably do you for
1106             non-Moo(se) use cases; I needed something cow-like that worked with
1107             L.
1108              
1109             Licensed under the same terms as Perl 5; see the license that came with your
1110             Perl distribution for details.
1111              
1112             =cut
1113