# 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](https://metacpan.org/pod/Moo::Role) gives your class a [POE::Session](https://metacpan.org/pod/POE::Session) capable of 
processing events via loaded plugins and/or emitting them to registered 
"listener" sessions.

It is derived from [POE::Component::Syndicator](https://metacpan.org/pod/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"](#yield)), as well as the 
faster plugin dispatch system that comes with [MooX::Role::Pluggable](https://metacpan.org/pod/MooX::Role::Pluggable).

The Emitter role consumes [MooX::Role::Pluggable](https://metacpan.org/pod/MooX::Role::Pluggable), 
making your emitter pluggable (see the 
[MooX::Role::Pluggable](https://metacpan.org/pod/MooX::Role::Pluggable) documentation for plugin-related details).

You do not need to create your own [POE::Session](https://metacpan.org/pod/POE::Session); calling 
["\_start\_emitter"](#_start_emitter) will spawn one for you.

You also get some useful sugar over POE event dispatch; see ["Methods"](#methods).

## Creating an Emitter

["SYNOPSIS"](#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"](#_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](https://metacpan.org/pod/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](https://metacpan.org/pod/MooX::Role::Pluggable#pluggable_event) 
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](https://metacpan.org/pod/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](https://metacpan.org/pod/POE::Session); the subclasses own handlers should be added to
**object\_states** prior to calling ["\_start\_emitter"](#_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](https://metacpan.org/pod/MooX::Role::Pluggable)).

Defaults to _Emitter\__

Set via **set\_register\_prefix**

#### session\_id

**session\_id** is our emitter's [POE::Session](https://metacpan.org/pod/POE::Session) ID, set when our Session is 
started via ["\_start\_emitter"](#_start_emitter).

#### shutdown\_signal

**shutdown\_signal** is the name of the [POE](https://metacpan.org/pod/POE) signal that will trigger a 
shutdown (used to shut down multiple Emitters). See ["Signals"](#signals)

### \_start\_emitter

**\_start\_emitter()** should be called on our object to spawn the actual
[POE::Session](https://metacpan.org/pod/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](https://metacpan.org/pod/POE::Session)

A 'shutdown' event will be emitted before sessions are dropped.

## Listening sessions

### Session event subscription

An external [POE::Session](https://metacpan.org/pod/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](https://metacpan.org/pod/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"](#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"](#session-event-subscription) and ["emit"](#emit)

### Events delivered to this session

The emitter's [POE::Session](https://metacpan.org/pod/POE::Session) provides a '\_default' handler that 
redispatches unknown POE-delivered events to ["process"](#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](https://metacpan.org/pod/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](https://metacpan.org/pod/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"](#emit) for more on dispatch behavior and event lifetime. See 
[MooX::Role::Pluggable](https://metacpan.org/pod/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"](#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"](#process).

## Sending events

### emit

    $self->emit( $event, @args );

**emit()** dispatches ["NOTIFY events"](#notify-events) -- these events are dispatched
first to our own session (with ["event\_prefix"](#event_prefix) prepended), then any
loaded plugins in the pipeline (with `N_` prepended), then registered
sessions (with ["event\_prefix"](#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"](#receiving-events), ["EAT values"](#eat-values)

### emit\_now

    $self->emit_now( $event, @args );

**emit\_now()** synchronously dispatches ["NOTIFY events"](#notify-events) -- see
["emit"](#emit).

### process

    $self->process( $event, @args );

**process()** calls registered plugin handlers for ["PROCESS events"](#process-events)
immediately; these are **not** dispatched to listening sessions.

Returns the same value as ["\_pluggable\_process" in MooX::Role::Pluggable](https://metacpan.org/pod/MooX::Role::Pluggable#pluggable_process).

See [MooX::Role::Pluggable](https://metacpan.org/pod/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](https://metacpan.org/pod/POE::Session).

### yield

    $self->yield( $poe_event, @args );

Provides an interface to [POE::Kernel](https://metacpan.org/pod/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](https://metacpan.org/pod/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"](#yield).

### timer

    my $alarm_id = $self->timer(
      $delayed_seconds,
      $event,
      @args
    );

Set a timer in the context of the emitter's [POE::Session](https://metacpan.org/pod/POE::Session). Returns the 
POE alarm ID.

The event can be either a named event/state or an anonymous coderef (see 
["yield"](#yield)).

A prefixed (["event\_prefix"](#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"](#timer).

A prefixed (["event\_prefix"](#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"](#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](https://metacpan.org/pod/POE::Kernel#Signal-Watcher-Methods) for details on [POE](https://metacpan.org/pod/POE) signals.

# SEE ALSO

For details regarding POE, see [POE](https://metacpan.org/pod/POE), [POE::Kernel](https://metacpan.org/pod/POE::Kernel), [POE::Session](https://metacpan.org/pod/POE::Session)

For details regarding Moo classes and Roles, see [Moo](https://metacpan.org/pod/Moo), [Moo::Role](https://metacpan.org/pod/Moo::Role), 
[Role::Tiny](https://metacpan.org/pod/Role::Tiny)

# AUTHOR

Jon Portnoy <avenj@cobaltirc.org>

Written from the ground up, but conceptually derived from
[POE::Component::Syndicator](https://metacpan.org/pod/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](https://metacpan.org/pod/MooX::Role::Pluggable). 

Licensed under the same terms as Perl 5; see the license that came with your
Perl distribution for details.