NAME

Net::Object::Peer - Peer-to-Peer Publish/Subscribe Network of Objects

VERSION

version 0.06

SYNOPSIS

use 5.10.0;

package Node;
use Moo;
with 'Net::Object::Peer';

has name => is => ( 'ro', required => 1 );

sub default_events { qw[ hey ] }

sub _notify_subscribed {
    my ( $self, $peer, $name ) = @_;
    say $self->name, ":\t@{[ $peer->name ]} subscribed to event $name";
}

sub _cb_hey {
    my ( $self, $event ) = @_;
    say $self->name,
      ":\t@{[ $event->emitter->name ]} sent @{[$event->name]}";
}

sub _cb_unsubscribed {
    my ( $self, $event ) = @_;
    say $self->name, ":\t@{[ $event->emitter->name ]} unsubscribed";
}

package main;


my $n1 = Node->new( name => 'N1' );
my $n2 = Node->new( name => 'N2' );
my $n3 = Node->new( name => 'N3' );

# Net::Object::Peer provides an "unsubscribed" event; $n1 will be
# notified when $n2 unsubscribes from it
$n1->subscribe( $n2, 'unsubscribed' );

# $n2 and $n3 will be notified when $n1 sends a "hey" event, and $n1
# will be notified that they have subscribed 

$n2->subscribe( $n1, 'hey' );
$n3->subscribe( $n1, 'hey' );

# both $n2 and $n3 will get notified
$n1->emit( 'hey' );

# only $n2 gets notified
$n1->send( $n2, 'hey' );

# destroy $n2; $n2 will unsubscribe from all of its events and $n1
# will be notified that $n2 has unsubscribed from it.
undef $n2;

Resulting in:

N2:	N1 subscribed to event unsubscribed
N1:	N2 subscribed to event hey
N1:	N3 subscribed to event hey
N2:	N1 sent hey
N3:	N1 sent hey
N2:	N1 sent hey
N1:	N2 unsubscribed

DESCRIPTION

Net::Object::Peer is a Moo Role which implements a publish/subscribe peer-to-peer messaging system, based upon Beam::Emitter. Objects in the network may broadcast events to all subscribers or may send events to a particular subscriber.

Subscriptions and unsubscriptions are tracked and messages will be sent to affected objects upon request.

While Net::Object::Peer is designed around the concept of nodes being objects with methods as event handlers, it retains Beam::Emitter's ability to register code references as well.

Net::Object::Peer::Cookbook provides some recipes.

Usage

As Net::Object::Peer is purely peer based with no common message bus, a network is built up by creating a set of network nodes and linking them via subscriptions.

my $n1 = Node->new( name => 'N1' );
my $n2 = Node->new( name => 'N2' );

$n1->subscribe( $n2, 'changed' );

Here $n1 subscribes to $n2's changed event. By default, $n1's _cb_changed method is invoked when $n2 emits a changed event.

Events

An emitter must register the events that it will emit. Here's how

  1. Class defaults

    A default set of events for the class may be specified by defining the "default_events" class method, which should return a list of event names:

    sub default_events{ qw[ evt1 evt2 evt3 ] }
  2. Object defaults

    During object construction, the events attribute may be used to specify a list of events.

  3. Runtime manipulation

    The "events" object method may be used to overwrite the list of events.

When a subscriber recieves an event, its registered handler for that event type is invoked. If the object creating the event used the "emit" or "send" methods,

$emitter->emit( $event_name );

the event handler will be invoked as

$subscriber->method( $event );

where $event is an object derived from the Net::Object::Peer::Event class. (This assumes that the handler is a method; it may be a simple callback).

If the event was created with the "emit_args" or "send_args" methods,

$emitter->emit_args( $event_name, @arguments );

the event handler will invoked as

$subscriber->method( @arguments );

Subscription and Unsubscription Events

When a subscriber registers one or more event handlers with an emitter via the subscriber's "subscribe" method, the emitter's _notify_subscribed method will be invoked (if it exists) as

$emitter->_notify_subscribed( $subscriber, @event_names );

If the subscription already exists, it will be unsubscribed and then resubscribed.

After a subscriber de-registers a handler, either explicitly via "unsubscribe" or when the object is destroyed, it will "emit" an unsubscribed event with a Net::Object::Peer::UnsubscribeEvent object as a payload.

While emitters are not automatically subscribed to unsubscribed events, this is easily accomplished by adding code to the emitters' _notify_subscribed method.

Detach Events

When an object is destroyed, it emits a detach event after unsubscribing from other peers' events.

ATTRIBUTES

event_handler_prefix

The string which prefixes default event handler method names. See "subscribe". It will by default be initialized to the return value of the "default_event_handler_prefix" method. It may be specified during object construction.

For example, the default handler method name for an event named changed would be _cb_changed. The class must provide that method. See "subscribe" for more information.

addr

A Net::Object::Peer::RefAddr object providing a unique identity for this emitter.

METHODS

emits_events

$bool = $obj->emits_events( @event_names );

Returns true if the object emits all of the named events

default_events

@events = $class->default_events;

Returns a list of events which this class will emit, excluding the detach and unsubscribed events. The default implementation returns an empty list. A per object event list may be specified via the "events" attribute or the events option to the constructor.

new

$obj = Net::Object::Peer->new( %args | \%args );

Construct a new object. The following arguments are available:

event_handler_prefix => string

The string which prefixes default event handler method names. See "event_handler_prefix"

events => string | arrayref

The name(s) of the event(s) this object will emit (don't include the unsubscribed and detach events). May be a single string or an arrayref. If not specified, the list of events will be initialized via the "default_events" class method.

default_event_handler_prefix

This class method returns the prefix for the default event handler method names. It defaults to returning _cb_.

build_sub

$coderef = $self->build_sub( $emitter, @tuple );

build_sub is the method responsible for creating and compiling the code for an event handler. It is invoked from the "subscribe()" method, with the following parameters

$emitter

The emitter object.

@tuple

the tuple passed to "subscribe" for this event.

The default implementation will return a Sub::Quote::quote_sub generated code reference for method calls and code specified as a string. See "Loop Detecton" in Net::Object::Peer::Cookbook for an example of using this attribute to inline additional code in the event handler.

subscribe

$self->subscribe( $peer, @event_tuple [, @event_tuple, ...  ] );

Subscribe to one or more events sent by $peer, which must consume the Net::Object::Peer role. If $peer additionally consumes the Net::Object::Peer::Ephemeral role, a strong reference to $peer is stored. (See "Translation/Proxy Nodes" in Net::Object::Peer::Cookbook.) An attempt to subscribe to an event the peer does not produce will throw an exception.

The event name and the action to be performed when the event is emitted are specified by a tuple with the following forms:

$event_name

the event handler will invoke the ${prefix}${event_name} method on $self, where $prefix is the event_handler_prefix attribute.

$event_name => { method => $method_name }

The event handler will invoke the $method_name method on $self.

$event_name => CODEREF

The passed code reference is called.

$event_name => { code => $code, capture => \%capture }

$code is a string containing code to be run by the event handler. %capture is a hash containing variable captures. See the documentation for "\%captures" in "quote_sub" in Sub::Quote for more information.

If $peer provides a _notify_subscribed method, that will be invoked as

$peer->_notify_subscribed( $self, $event_name, ... );

for each subscription.

unsubscribe

# Unsubscribe from all events from all peers.
$self->unsubscribe;

# Unsubscribe from all events emitted by a peer
$self->unsubscribe( $peer );

# Unsubscribe from one or more events emitted by a peer
$self->unsubscribe( $peer, $event_name [, $event_name [, ... ]);

# Unsubscribe from the peer and event specified by the passed
# Net::Object::Peer::Event object
$self->unsubscribe( $event_object );

# Unsubscribe from one or more events emitted by all peers
$self->unsubscribe( $event_name [, $event_name [, ... ] ] )

Unsubscribe from events/peers. After unsubscription, an unsubscribed event with a Net::Object::Peer::UnsubscribeEvent as a payload will be sent to affected peers who have subscribed to the unsubscribed event(s).

$peer may be either a Net::Object::Peer or a Net::Object::Peer::RefAddr object.

Note that Net::Object::Peer::Event objects which are passed to event handlers may have a masqueraded emitter attribute. Attempting to unsubscribe from that emitter is unwise. Instead, pass either the event object or the addr field in that object, which is guaranteed to identify the actual emitter subscribed to.

events

@events = $obj->events;
$obj->events( \@event_names | $event_name );

As a getter, returns a list of event names which the object may emit.

As a setter, accepts either an arrayref or a single event name. Event names must a valid Perl identifier (e.g., no : or - characters).

detach

$self->detach;

Detach the object from the network. It will

  1. Unsubscribe from all events from all peers.

  2. Emit an unsubscribed event with a Net::Object::Peer::UnsubscribeEvent as a payload.

  3. Emit a detach event.

subscriptions

# return all subscriptions
my @subscriptions = $self->subscriptions;

# return matching subscriptions
my @subscriptions = $self->subscriptions( $coderef | %spec );

Returns the events to which $self is subscribed as a list of hashrefs (see Net::Object::Peer::Subscription::as_hashref). If arguments are specified, only those which match are returned; see "find" in Net::Object::Peer::Subscrition;

emit

$self->emit( $event_name, %args );

Broadcast the named event to all subscribed peers. %args contains arguments to be passed the the payload class constructor. The default payload class is a Net::Object::Peer::Event object; use the class key to specify an alternate class, which must be derived from Net::Object::Peer::Event. An attempt to emit an event which is not supported by the emitter will cause an exception to be thrown.

send

$self->send( $peer, $event_name, %args );

This is similar to the "emit" method, but only sends the event to the specified peer. An attempt to emit an event which is not supported by the emitter will cause an exception to be thrown.

emit_args

$self->emit_args( $event_name, @args );

Broadcast the named event to all subscribed peers. @args will be passed directly to each subscriber's callback. An attempt to emit an event which is not supported by the emitter will cause an exception to be thrown.

send_args

$self->send_args( $peer, $event_name, @args );

This is similar to the "emit_args" method, but only sends the event to the specified peer. An attempt to emit an event which is not supported by the emitter will cause an exception to be thrown.

AUTHOR

Diab Jerius <djerius@cpan.org>

COPYRIGHT AND LICENSE

This software is Copyright (c) 2016 by Smithsonian Astrophysical Observatory.

This is free software, licensed under:

The GNU General Public License, Version 3, June 2007