# NAME

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

# VERSION

version 0.02

# SYNOPSIS

    use 5.10.0;
    
    package Node;
    use Moo;
    with 'Net::Object::Peer';
    
    has name => is => ( 'ro', required => 1 );
    
    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_unsubscribe {
        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 "unsubscribe" event; $n1 will be
    # notified when $n2 unsubscribes from it
    $n1->subscribe( $n2, 'unsubscribe' );
    
    # $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 unsubscribe
    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](https://metacpan.org/pod/Moo) [Role](https://metacpan.org/pod/Moo::Role) which
implements a publish/subscribe peer-to-peer messaging system, based
upon [Beam::Emitter](https://metacpan.org/pod/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](https://metacpan.org/pod/Beam::Emitter)'s ability to register code references as well.

[Net::Object::Peer::Cookbook](https://metacpan.org/pod/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

When a subscriber recieves an event, its registered handler for that
event type is invoked.  If the object creating the event used the
["emit"](#emit) or ["send"](#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](https://metacpan.org/pod/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"](#emit_args) or ["send\_args"](#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"](#subscribe) method, the emitter's
`_notify_subscribed` method will be invoked (if it exists) as

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

After a subscriber de-registers a handler, either explicitly via
["unsubscribe"](#unsubscribe) or when the object is destroyed, it will ["emit"](#emit) an
`unsubscribe` event with a [Net::Object::Peer::UnsubscribeEvent](https://metacpan.org/pod/Net::Object::Peer::UnsubscribeEvent)
object as a payload.

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

# METHODS

## 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()"](#subscribe) method, with the following parameters

- `$emitter`

    The emitter object.

- `@tuple`

    the tuple passed to ["subscribe"](#subscribe) for this event.

The default implementation will return a [Sub::Quote::quote\_sub](https://metacpan.org/pod/Sub::Quote#quote_sub)
generated code reference for method calls and code specified as a
string.  See ["Loop Detecton" in Net::Object::Peer::Cookbook](https://metacpan.org/pod/Net::Object::Peer::Cookbook#Loop-Detecton) 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](https://metacpan.org/pod/Net::Object::Peer) role.

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 `_cb_${event_name}` method on `$self`.

- `$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](https://metacpan.org/pod/Sub::Quote#quote_sub) 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 one or more events emitted by all peers
    $self->unsubscribe( $event_name [, $event_name [, ... ] ] )

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

## subscriptions

    my @subscriptions = $self->subscriptions;

Returns the events to which `$self` is subscribed as a list of
[Net::Object::Peer::Subscription](https://metacpan.org/pod/Net::Object::Peer::Subscription) objects.

## 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](https://metacpan.org/pod/Net::Object::Peer::Event) object; use the `class` key to
specify an alternate class, which must be erived from **Net::Object::Peer::Event**.

## send

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

This is similar to the ["emit"](#emit) method, but only sends the event to the
specified peer.

## 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.

## send\_args

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

This is similar to the ["emit\_args"](#emit_args) method, but only sends the event to the
specified peer.

# 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
