NAME

   Event::Distributor - a simple in-process pub/sub mechanism

SYNOPSIS

      use Event::Distributor;

      my $dist = Event::Distributor->new;

      $dist->declare_signal( "announce" );


      $dist->subscribe_sync( announce => sub {
         my ( $dist, $message ) = @_;
         say $message;
      });

      $dist->subscribe_async( announce => sub {
         my ( $dist, $message ) = @_;
         return $async_http->POST( "http://server/message", $message );
      });


      $dist->fire_sync( announce => "Hello, world!" );

DESCRIPTION

   Instances of this class provide a simple publish/subscribe mechanism
   within a single process, for either synchronous or Future-based
   asynchronous use.

   A given instance has a set of named events. Subscribers are CODE
   references attached to a named event. Publishers can declare the
   existence of a named event, and then later invoke it by passing in
   arguments, which are distributed to all of the subscribers of that
   named event.

   It is specifically not an error to request to subscribe an event that
   has not yet been declared, in order to allow multiple modules of code
   to be loaded and subscribe events the others publish, without
   introducing loading order dependencies. An event only needs to be
   declared by the time it is fired.

   Natively all of the events provided by the distributor are
   fully-asynchronous in nature. Each subscriber is expected to return a
   Future instance which will indicate its completion; the results of
   these are merged into a single future returned by the fire method
   itself. However, to support synchronous or semi-synchronous programs
   using it, both the observe and invoke methods also have a synchronous
   variant. Note however, that this module does not provide any kind of
   asynchronous detachment of synchronous functions; using the
   "subscribe_sync" method to subscribe a long-running blocking function
   will cause the fire_* methods to block until that method returns. To
   achieve a truely-asynchronous experience the attached code will need to
   use some kind of asynchronous event system.

   This module is very-much a work-in-progress, and many ideas may still
   be added or changed about it. It is the start of a concrete
   implementaion of some of the ideas in my "Event-Reflexive Programming"
   series of blog posts. See the "TODO" and "SEE ALSO" sections for more
   detail.

EVENTS

   Each of the events known by a distributor has a name. Conceptually each
   also has a type. Currently there are three types of event, a "signal",
   an "action", and a "query".

     * A signal event simply informs subscribers that some event or
     condition has occurred. Additional arguments can be passed from the
     invoker to the subscribers, but subscriptions are not expected to
     return a meaningful value, nor does firing this event return a value.
     All subscriber functions are invoked sequentually and synchronously
     by a fire_* method (though, of course, asynchronous subscribers
     synchronously return a future instance, which allows them to continue
     working asynchronously).

     * An action event requires a single subscriber, and represents a
     request from the invoker to the subscriber to perform some activity.
     This behaves much like a regular (Future-returning) method call,
     except that the indirection mechanism of the distributor allows a
     more flexible method of connection between the two sides.

     * A query event invokes subscriber code expecting a successful
     return, returning the first result that is successful. If a
     synchronous subscriber returns a result, or if an asynchronous one
     returns a successful immediate Future, then no further subscribers
     are invoked, and that result is taken immediately. Any other pending
     Futures are then cancelled.

METHODS

declare_signal

      $distributor->declare_signal( $name )

   Declares a new "signal" event of the given name.

declare_action

      $distributor->declare_action( $name )

   Since version 0.04.

   Declares a new "action" event of the given name.

declare_query

      $distributor->declare_query( $name )

   Since version 0.02.

   Declares a new "query" event of the given name.

subscribe_async

      $distributor->subscribe_async( $name, $code )

   Adds a new CODE reference to the list of subscribers for the named
   event. This subscriber is expected to return a Future that will
   eventually yield its result.

   When invoked the code will be passed the distributor object itself and
   the list of arguments, and is expected to return a Future.

    $f = $code->( $distributor, @args )

subscribe_sync

      $distributor->subscribe_sync( $name, $code )

   Adds a new CODE reference to the list of subscribers for the named
   event. This subscriber is expected to perform its work synchronously
   and return its result immediately.

   In non-blocking or asynchronous applications, this method should only
   be used for simple subscribers which can immediately return having
   completed their work. If the work is likely to take some time by
   blocking on external factors, consider instead using the
   "subscribe_async" method.

   When invoked the code will be passed the distributor object itself and
   the list of arguments.

      $code->( $distributor, @args )

fire_async

      $f = $distributor->fire_async( $name, @args )

   Invokes the named event, passing the arguments to the subscriber
   functions. This function returns as soon as all the subscriber
   functions have been invoked, returning a Future that will eventually
   complete when all the futures returned by the subscriber functions have
   completed.

fire_sync

      $distributor->fire_sync( $name, @args )

   Invokes the named event, passing the arguments to the subscriber
   functions. This function synchronously waits until all the subscriber
   futures have completed, and will return once they have all done so.

   Note that since this method calls the get method on the Future instance
   returned by "fire_async", it is required that this either be an
   immediate, or be some subclass that can actually perform the await
   operation. This should be the case if it is provided by an event
   framework or similar, or custom application logic.

TODO

   Some of these ideas appear in the "Event-Reflexive Progamming" series
   of blog posts, and may be suitable for implementation here. All of
   these ideas are simply for consideration; there is no explicit promise
   that any of these will actually be implemented.

     * Unsubscription from events.

     * Define (or document the lack of) ordering between subscriptions of
     a given event.

     * Refine the failure-handling semantics of signals.

     * Ability to invoke signals after the current one is finished, by
     deferring the fire method. Should this be a new fire_* method, or a
     property of the signal itself?

     * More control over the semantics of value-returning events -
     scatter/map/gather pattern.

     * Sub-heirarchies of events.

     * Subclasses for specific event frameworks (IO::Async).

     * Subclasses (or other behaviours) for out-of-process event
     serialisation and subscribers.

     * Event parameter filtering mechanics - allows parametric
     heirarchies, instrumentation logging, efficient out-of-process
     subscribers.

SEE ALSO

   Event-Reflexive Programming
   <http://leonerds-code.blogspot.co.uk/search/label/event-reflexive>

AUTHOR

   Paul Evans <[email protected]>