Evented-Object

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

NAME

    Evented::Object - base class which allows you to attach callbacks to
    objects and then fire events on them.

SYNOPSIS

     package Person;
     
     use warnings;
     use strict;
     use 5.010;
     use parent 'Evented::Object';

README  view on Meta::CPAN

     # The result:
     #
     #   not quite 21 yet...
     #   time to get drunk!

DESCRIPTION

    I doubt your objects have ever been this evented in your entire life.

    Evented::Object supplies an (obviously objective) interface to store
    and manage callbacks for events, fire events upon objects, and more.

    Evented::Object allows you to attach event callbacks to an object
    (i.e., a blessed hash reference) and then fire events on that object.
    Event fires are much like method calls, except that there can be many
    responders.

    Whereas many event systems involve globally unique event names,
    Evented::Object allows you to attach events on specific objects. The
    event callbacks, priority options, and other data are stored within the
    object itself.

MANAGING CALLBACKS

    The Evented::Object package provides several convenient methods for
    managing an event-driven object.

 Evented::Object->new()

    Creates a new Evented::Object.

    Typically, this method is overriden by a child class of
    Evented::Object.

     my $eo = Evented::Object->new();

 $eo->register_callback($event_name => \&callback, %options)

    Attaches an event callback the object.

    When the specified event is fired, each of the callbacks registered
    using this method will be called by descending priority order
    (numerically higher priority numbers are called first.)

     $eo->register_callback(myEvent => sub {
         ...
     }, name => 'some.callback', priority => 200);

    Parameters

      * $event_name - name of the event.

README  view on Meta::CPAN

    the callback name. In this case, the with_eo option is also
    automatically enabled.

     $eo->register_callback(myEvent => sub {
         ...
     }, 'some.callback, priority => 200);

    See the above method specification for parameters and supported
    options.

 $eo->register_callbacks(@events)

    Registers several event callbacks at once.

    The arguments should be a list of hash references. These references
    take the same options as ->register_callback(). Returns a list of
    return values in the order that the events were specified.

     $eo->register_callbacks(
         { myEvent => \&my_event_1, name => 'cb.1', priority => 200 },
         { myEvent => \&my_event_2, name => 'cb.2', priority => 100 }
     );

    Parameters

      * @events - array of hash references to pass to
      ->register_callback().

 $eo->delete_event($event_name)

    Deletes all callbacks registered for the supplied event.

    Returns number of callbacks deleted, false if none.

     $eo->delete_event('myEvent');

    Parameters

      * $event_name - name of the event.

 $eo->delete_callback($event_name, $callback_name)

    Deletes an event callback from the object with the given callback name.

README  view on Meta::CPAN

     $eo->delete_callback(myEvent => 'my.callback');

    Parameters

      * $event_name - name of the event.

      * $callback_name - name of the callback being removed.

 $eo->delete_all_events()

    Deletes all events and all callbacks from the object.

    If you know that an evented object will no longer be used in your
    program, by calling this method you can be sure that no cyclical
    references from within callbacks will cause the object to be leaked.

FIRING EVENTS

 $eo->fire_event($event_name => @arguments)

    Fires the specified event, calling each callback that was registered
    with ->register_callback() in descending order of their priorities.

    Returns the fire object.

     $eo->fire_event('some_event');
    
     $eo->fire_event(some_event => $some_argument, $some_other_argument);

    Parameters

      * $event_name - name of the event being fired.

      * @arguments - optional, list of arguments to pass to event
      callbacks.

 $eo->fire_once($event_name => @arguments)

    Fires the specified event, calling each callback that was registered
    with ->register_callback() in descending order of their priorities.

    Then, all callbacks for the event are deleted. This method is useful
    for situations where an event will never be fired more than once.

    Returns the fire object.

     $eo->fire_once('some_event');
     $eo->fire_event(some_event => $some_argument, $some_other_argument);
     # the second does nothing because the first deleted the callbacks

    Parameters

      * $event_name - name of the event being fired.

      * @arguments - optional, list of arguments to pass to event
      callbacks.

 $eo->fire_events_together(@events)

    The fire_events_together() function can be used as a method on evented
    objects. See the documentation for the function in "PROCEDURAL
    FUNCTIONS".

 $eo->prepare_event(event_name => @arguments)

    Prepares a single event for firing.

    Returns an Evented::Object::Collection representing the pending
    callbacks.

     # an example using the fire option return_check.
     $eo->prepare_event(some_event => @arguments)->fire('return_check');

 $eo->prepare_together(@events)

    The preparatory method equivalent to ->fire_events_together.

    Returns an Evented::Object::Collection representing the pending
    callbacks.

 $eo->prepare(...)

    A smart method that uses the best guess between ->prepare_event and
    ->prepare_together.

     # uses ->prepare_event()
     $eo->prepare(some_event => @arguments);
    
     # uses ->prepare_together()

README  view on Meta::CPAN

    farm. You have another class which represents a cow. You would like to
    use the same callback for all of the moos that occur on the farm,
    regardless of which cow initiated it.

    Rather than attaching an event callback to every cow, you can instead
    make the farm a listener of the cow. Then, you can attach a single
    callback to your farm. If your cow's event for mooing is moo, your
    farm's event for any mooing is cow.moo.

    When an event is fired on an object, the same fire object is used for
    callbacks belonging to both the evented object and its listening
    objects. Therefore, callback names should be unique not only to the
    listener object but to the object being listened on as well.

    You should also note the values of the fire object:

      * $fire->event_name - name of the event from the perspective of the
      listener; i.e. cow.moo (NOT moo)

      * $fire->object - object being listened to; i.e. $cow (NOT $farm)

    This also means that stopping the event from a listener object will
    cancel all remaining callbacks.

 $eo->add_listener($other_eo, $prefix)

    Makes the passed evented object a listener of this evented object.

    See "LISTENERS".

     $cow->add_listener($farm, 'cow');

    Parameters

README  view on Meta::CPAN


    Parameters

      * $other_eo - evented object that will listen.

CLASS MONITORS

    An evented object can be registered as a "monitor" of a specific
    class/package.

    All event callbacks that are added from that class to any evented
    object of any type will trigger an event on the monitor object.

    An example scenario of when this might be useful is an evented object
    for debugging all events being registered by a certain package. It
    would log all of them, making it easier to find a problem.

 $eo->monitor_events($pkg)

    Registers an evented object as the class monitor for a specific
    package.

README  view on Meta::CPAN


    The Evented::Object package provides some functions for use. These
    functions typically are associated with more than one evented object or
    none at all.

 fire_events_together(@events)

    Fires multiple events at the same time.

    This allows you to fire multiple similar events on several evented
    objects at the same time. It essentially pretends that the callbacks
    are all for the same event and all on the same object.

    It follows priorities throughout all of the events and all of the
    objects, so it is ideal for firing similar or identical events on
    multiple objects.

    The same fire object is used throughout. This means that callback names
    must unique among all of these objects and events. It also means that
    stopping an event from any callback will cancel all remaining
    callbacks, regardless to which event or which object they belong.

    The function takes a list of array references in the form of: [
    $evented_object, event_name => @arguments ]

     Evented::Object::fire_events_together(
         [ $server,  user_joined_channel => $user, $channel ],
         [ $channel, user_joined         => $user           ],
         [ $user,    joined_channel      => $channel        ]
     );

README  view on Meta::CPAN

 $eo->fire(...)

    Alias for $eo->fire_event().

 $eo->register_event(...)

    Alias for $eo->register_callback().

 $eo->register_events(...)

    Alias for $eo->register_callbacks().

 $fire->eo

    Alias for $fire->object.

AUTHOR

    Mitchell Cooper <https://github.com/cooper> <cooper@cpan.org>

    Copyright © 2011-2020. Released under New BSD license.

README.pod  view on Meta::CPAN

=head1 NAME

B<Evented::Object> - base class which allows you to attach callbacks to
objects and then fire events on them.

=head1 SYNOPSIS

 package Person;
 
 use warnings;
 use strict;
 use 5.010;
 use parent 'Evented::Object';

README.pod  view on Meta::CPAN

 # The result:
 #
 #   not quite 21 yet...
 #   time to get drunk!

=head1 DESCRIPTION

B<I doubt your objects have ever been this evented in your entire life.>

Evented::Object supplies an (obviously objective) interface to store and manage
callbacks for events, fire events upon objects, and more.

Evented::Object allows you to attach event callbacks to an object
(i.e., a blessed hash reference) and then fire events on that object. Event
fires are much like method calls, except that there can be many responders.

Whereas many event systems involve globally unique event names, Evented::Object
allows you to attach events on specific objects. The event callbacks,
priority options, and other data are stored within the object itself.

=head1 MANAGING CALLBACKS

The Evented::Object package provides several convenient methods for managing an
event-driven object.

=head2 Evented::Object->new()

Creates a new Evented::Object.

Typically, this method is overriden by a child class of Evented::Object.

 my $eo = Evented::Object->new();

=head2 $eo->register_callback($event_name => \&callback, %options)

Attaches an event callback the object.

When the specified event is fired, each
of the callbacks registered using this method will be called by descending
priority order (numerically higher priority numbers are called first.)

 $eo->register_callback(myEvent => sub {
     ...
 }, name => 'some.callback', priority => 200);

B<Parameters>

=over

README.pod  view on Meta::CPAN

If the list of options is odd, it is assumed that the first element is the
callback name. In this case, the C<with_eo> option is also automatically
enabled.

 $eo->register_callback(myEvent => sub {
     ...
 }, 'some.callback, priority => 200);

See the above method specification for parameters and supported options.

=head2 $eo->register_callbacks(@events)

Registers several event callbacks at once.

The arguments should be a list of hash
references. These references take the same options as
C<< ->register_callback() >>. Returns a list of return values in the order that
the events were specified.

 $eo->register_callbacks(
     { myEvent => \&my_event_1, name => 'cb.1', priority => 200 },
     { myEvent => \&my_event_2, name => 'cb.2', priority => 100 }
 );

B<Parameters>

=over

=item *

B<@events> - array of hash references to pass to C<< ->register_callback() >>.

=back

=head2 $eo->delete_event($event_name)

Deletes all callbacks registered for the supplied event.

Returns number of callbacks deleted, false if none.

 $eo->delete_event('myEvent');

B<Parameters>

=over

=item *

B<$event_name> - name of the event.

README.pod  view on Meta::CPAN

B<$event_name> - name of the event.

=item *

B<$callback_name> - name of the callback being removed.

=back

=head2 $eo->delete_all_events()

Deletes all events and all callbacks from the object.

If you know that an
evented object will no longer be used in your program, by calling this method
you can be sure that no cyclical references from within callbacks will cause the
object to be leaked.

=head1 FIRING EVENTS

=head2 $eo->fire_event($event_name => @arguments)

Fires the specified event, calling each callback that was registered with
C<< ->register_callback() >> in descending order of their priorities.

Returns the L<fire object|Evented::Object::EventFire>.

README.pod  view on Meta::CPAN

B<Parameters>

=over

=item *

B<$event_name> - name of the event being fired.

=item *

B<@arguments> - I<optional>, list of arguments to pass to event callbacks.

=back

=head2 $eo->fire_once($event_name => @arguments)

Fires the specified event, calling each callback that was registered with
C<< ->register_callback() >> in descending order of their priorities.

Then, all callbacks for the event are deleted. This method is useful for
situations where an event will never be fired more than once.

Returns the L<fire object|Evented::Object::EventFire>.

 $eo->fire_once('some_event');
 $eo->fire_event(some_event => $some_argument, $some_other_argument);
 # the second does nothing because the first deleted the callbacks

B<Parameters>

=over

=item *

B<$event_name> - name of the event being fired.

=item *

B<@arguments> - I<optional>, list of arguments to pass to event callbacks.

=back

=head2 $eo->fire_events_together(@events)

The C<fire_events_together()> function can be used as a method on evented
objects. See the documentation for the function in L</"PROCEDURAL FUNCTIONS">.

=head2 $eo->prepare_event(event_name => @arguments)

Prepares a single event for firing.

Returns an L<Evented::Object::Collection> representing the pending callbacks.

 # an example using the fire option return_check.
 $eo->prepare_event(some_event => @arguments)->fire('return_check');

=head2 $eo->prepare_together(@events)

The preparatory method equivalent to C<< ->fire_events_together >>.

Returns an L<Evented::Object::Collection> representing the pending callbacks.

=head2 $eo->prepare(...)

A smart method that uses the best guess between C<< ->prepare_event >> and
C<< ->prepare_together >>.

 # uses ->prepare_event()
 $eo->prepare(some_event => @arguments);

 # uses ->prepare_together()

README.pod  view on Meta::CPAN

Consider a scenario where you have a class whose objects represent a farm. You
have another class which represents a cow. You would like to use the same
callback for all of the moos that occur on the farm, regardless of which cow
initiated it.

Rather than attaching an event callback to every cow, you can instead make the
farm a listener of the cow. Then, you can attach a single callback to your farm.
If your cow's event for mooing is C<moo>, your farm's event for any mooing is
C<cow.moo>.

When an event is fired on an object, the same fire object is used for callbacks
belonging to both the evented object and its listening objects. Therefore,
callback names should be unique not only to the listener object but to the
object being listened on as well.

You should also note the values of the
L<fire object|Evented::Object::EventFire>:

=over

=item *

README.pod  view on Meta::CPAN

B<< $fire->event_name >> - name of the event from the perspective of the
listener; i.e. C<cow.moo> (NOT C<moo>)

=item *

B<< $fire->object >> - object being listened to; i.e. C<$cow> (NOT C<$farm>)

=back

This also means that stopping the event from a listener object will cancel
I<all> remaining callbacks.

=head2 $eo->add_listener($other_eo, $prefix)

Makes the passed evented object a listener of this evented object.

See L</"LISTENERS">.

 $cow->add_listener($farm, 'cow');

B<Parameters>

README.pod  view on Meta::CPAN

=item *

B<$other_eo> - evented object that will listen.

=back

=head1 CLASS MONITORS

An evented object can be registered as a "monitor" of a specific class/package.

I<All> event callbacks that are added from that class to I<any> evented object
of I<any> type will trigger an event on the monitor object.

An example scenario of when this might be useful is an evented object for
debugging all events being registered by a certain package. It would log all of
them, making it easier to find a problem.

=head2 $eo->monitor_events($pkg)

Registers an evented object as the class monitor for a specific package.

README.pod  view on Meta::CPAN


The Evented::Object package provides some functions for use. These functions
typically are associated with more than one evented object or none at all.

=head2 fire_events_together(@events)

Fires multiple events at the same time.

This allows you to fire multiple similar
events on several evented objects at the same time. It essentially pretends that
the callbacks are all for the same event and all on the same object.

It follows priorities throughout all of the events and all of the objects, so it
is ideal for firing similar or identical events on multiple objects.

The same L<fire object|Evented::Object::EventFire> is used throughout.
This means that callback names must unique among all of these
objects and events. It also means that stopping an event from any callback will
cancel all remaining callbacks, regardless to which event or which object they
belong.

The function takes a list of array references in the form of:
C<< [ $evented_object, event_name => @arguments ] >>

 Evented::Object::fire_events_together(
     [ $server,  user_joined_channel => $user, $channel ],
     [ $channel, user_joined         => $user           ],
     [ $user,    joined_channel      => $channel        ]
 );

README.pod  view on Meta::CPAN

=head2 $eo->fire(...)

Alias for C<< $eo->fire_event() >>.

=head2 $eo->register_event(...)

Alias for C<< $eo->register_callback() >>.

=head2 $eo->register_events(...)

Alias for C<< $eo->register_callbacks() >>.

=head2 $fire->eo

Alias for C<< $fire->object >>.

=head1 AUTHOR

L<Mitchell Cooper|https://github.com/cooper> <cooper@cpan.org>

Copyright E<copy> 2011-2020. Released under New BSD license.

lib/Evented/Object.pm  view on Meta::CPAN

    my $event_store = _event_store($eo);

    # before/after a callback.
    my $priority = delete $opts{priority} || 0;
    if (defined $opts{before} or defined $opts{after}) {
        $priority = 'nan';
        # nan priority indicates it should be determined at a later time.
    }

    # add the callback.
    my $callbacks = $event_store->{$event_name}{$priority} ||= [];
    push @$callbacks, my $cb = {
        %opts,
        code   => $code,
        caller => \@caller
    };

    # tell class monitor.
    _monitor_fire(
        $opts{_caller} // $caller[0],
        register_callback => $eo, $event_name, $cb
    );

    return $cb;
}

# ->register_callbacks()
#
# attaches several event callbacks at once.
#
sub register_callbacks {
    my $eo = shift;
    return map { $eo->register_callback(%$_, _caller => caller) } @_;
}

##########################
### DELETING CALLBACKS ###
##########################

# ->delete_callback(event_name => 'callback.name')
# ->delete_event('event_name')
#
# deletes an event callback or all callbacks of an event.
# returns a true value if any events were deleted, false otherwise.
# more specifically, it returns the number of callbacks deleted.
#
sub delete_callback {
    my ($eo, $event_name, $name, $caller) = @_;
    my @caller      = $caller && ref $caller eq 'ARRAY' ? @$caller : caller;
    my $amount      = 0;
    my $event_store = _event_store($eo);

    # event does not have any callbacks.
    return 0 unless $event_store->{$event_name};

     # if no callback is specified, delete all events of this type.
    if (!$name) {
        $amount = scalar keys %{ $event_store->{$event_name} };
        delete $event_store->{$event_name};
        _monitor_fire($caller[0], delete_event => $eo, $event_name);
        return $amount;
    }

    # iterate through callbacks and delete matches.
    PRIORITY: foreach my $priority (keys %{ $event_store->{$event_name} }) {
        my $callbacks = $event_store->{$event_name}{$priority};
        my @goodbacks;

        CALLBACK: foreach my $cb (@$callbacks) {

            # don't want this one.
            if (ref $cb ne 'HASH' || $cb->{name} eq $name) {
                $amount++;
                next CALLBACK;
            }

            push @goodbacks, $cb;
        }

        # no callbacks left in this priority.
        if (!scalar @goodbacks) {
            delete $event_store->{$event_name}{$priority};
            next PRIORITY;
        }

        # keep these callbacks.
        @$callbacks = @goodbacks;

    }

    return $amount;
}

# ->delete_all_events()
#
# deletes all the callbacks of EVERY event.
# useful when you're done with an object to ensure any possible self-referencing
# callbacks are properly destroyed for garbage collection to work.
#
sub delete_all_events {
    my ($eo, $amount) = (shift, 0);
    my $event_store   = _event_store($eo) or return;
    ref $event_store eq 'HASH'            or return;

    # delete one-by-one.
    # we can't simply set an empty list because monitor events must be fired.
    foreach my $event_name (keys %$event_store) {
        $eo->delete_event($event_name);

lib/Evented/Object.pm  view on Meta::CPAN

            ($event_name, @args) = @$set;
        }

        # no object; fall back to $obj.
        else {
            $eo = $obj or return;
            ($event_name, @args) = ($eo_maybe, @$set);
        }

        # add to the collection.
        my ($callbacks, $names) =
            _get_callbacks($eo, $event_name, @args);
        $collection->push_callbacks($callbacks, $names);

    }

    return $collection;
}

#####################
### FIRING EVENTS ###
#####################

lib/Evented/Object.pm  view on Meta::CPAN

# fire_events_together()
#
# prepares several events and then fires them together.
#
sub fire_events_together {
    prepare_together(@_)->fire(caller => [caller 1]);
}

# ->fire_once()
#
# prepares an event, fires it, and deletes all callbacks afterward.
#
sub fire_once {
    my ($eo, $event_name, @args) = @_;

    # fire with this caller.
    my $fire = $eo->prepare_event($event_name, @args)->fire(
        caller => [caller 1]
    );

    # delete the event.

lib/Evented/Object.pm  view on Meta::CPAN

######################

# for objective use $eo->monitor_events($pkg)
sub monitor_events  {    add_class_monitor(reverse @_) }
sub stop_monitoring { delete_class_monitor(reverse @_) }

# add_class_monitor()
#
# set the monitor object of a class.
#
# TODO: honestly class monitors need to track individual callbacks so that the
# monitor is notified of all deletes of callbacks added by the class being
# monitored even if the delete action was not committed by that package.
#
sub add_class_monitor {
    my ($pkg, $obj) = @_;

    # ensure it's an evented object.
    return unless $obj->isa(__PACKAGE__);

    # it's already in the list.
    my $m = $monitors{$pkg} ||= [];

lib/Evented/Object.pm  view on Meta::CPAN

sub _prop_store {
    my $eo    = shift;
    return $eo->{$props}   ||= {} if blessed $eo;
    my $store = _package_store($eo);
    return $store->{props} ||= {} if not blessed $eo;
}

# fetch a callback from its name.
sub _get_callback_named {
    my ($eo, $event_name, $callback_name) = @_;
    foreach my $callback (@{ _get_callbacks($eo, $event_name) }) {
        return $callback if $callback->[2]{name} eq $callback_name
    }
    return;
}

# fetches callbacks of an event.
# internal use only.
sub _get_callbacks {
    my ($eo, $event_name, @args) = @_;
    my (%callbacks, %callback_names);

    # start out with two stores: the object and the package.
    my @stores = (
        [ $event_name => $eo->{$events}             ],
        [ $event_name => _event_store(blessed $eo)  ]
    );


    # if there are any listening objects, add those stores.
    if (my $listeners = $eo->{$props}{listeners}) {

lib/Evented/Object.pm  view on Meta::CPAN


            push @stores, [ $listener_event_name => $lis->{$events} ];

        }

        # delete listeners if necessary.
        splice @$listeners, $_, 1 foreach @delete;

    }

    # add callbacks from each store.
    foreach my $st (@stores) {
        my ($event_name, $event_store) = @$st;
        my $store = $event_store->{$event_name} or next;
        foreach my $priority (keys %$store) {

            # create a group reference.
            my $group_id = "$eo/$event_name";
            my $group    = [ $eo, $event_name, \@args, $group_id ];
            weaken($group->[0]);

            # add each callback set. inject callback name.
            foreach my $cb_ref (@{ $store->{$priority} }) {
                my %cb = %$cb_ref; # make a copy
                $cb{id} = "$group_id/$cb{name}";
                $callbacks{ $cb{id} } = [ $priority, $group, \%cb ];
                $callback_names{$group_id}{ $cb{name} } = $cb{id};
            }

        }
    }

    return wantarray ? (\%callbacks, \%callback_names) : \%callbacks;
}

# fire a class monitor event.
sub _monitor_fire {
    my ($pkg, $event_name, @args) = @_;
    my $m = $monitors{$pkg} or return;
    safe_fire($_, "monitor:$event_name" => @args) foreach @$m;
}

sub DESTROY { shift->delete_all_events }

lib/Evented/Object.pm  view on Meta::CPAN

sub register_event;
sub register_events;
sub delete_event;

sub on;
sub del;
sub fire;

BEGIN {
    *register_event     = *register_callback;
    *register_events    = *register_callbacks;
    *delete_event       = *delete_callback;
    *on                 = *register_callback;
    *del                = *delete_callback;
    *fire               = *fire_event;
}

1;

=head1 NAME

B<Evented::Object> - base class which allows you to attach callbacks to
objects and then fire events on them.

=head1 SYNOPSIS

 package Person;
 
 use warnings;
 use strict;
 use 5.010;
 use parent 'Evented::Object';

lib/Evented/Object.pm  view on Meta::CPAN

 # The result:
 #
 #   not quite 21 yet...
 #   time to get drunk!

=head1 DESCRIPTION

B<I doubt your objects have ever been this evented in your entire life.>

Evented::Object supplies an (obviously objective) interface to store and manage
callbacks for events, fire events upon objects, and more.

Evented::Object allows you to attach event callbacks to an object
(i.e., a blessed hash reference) and then fire events on that object. Event
fires are much like method calls, except that there can be many responders.

Whereas many event systems involve globally unique event names, Evented::Object
allows you to attach events on specific objects. The event callbacks,
priority options, and other data are stored within the object itself.

=head1 MANAGING CALLBACKS

The Evented::Object package provides several convenient methods for managing an
event-driven object.

=head2 Evented::Object->new()

Creates a new Evented::Object.

Typically, this method is overriden by a child class of Evented::Object.

 my $eo = Evented::Object->new();

=head2 $eo->register_callback($event_name => \&callback, %options)

Attaches an event callback the object.

When the specified event is fired, each
of the callbacks registered using this method will be called by descending
priority order (numerically higher priority numbers are called first.)

 $eo->register_callback(myEvent => sub {
     ...
 }, name => 'some.callback', priority => 200);

B<Parameters>

=over

lib/Evented/Object.pm  view on Meta::CPAN

If the list of options is odd, it is assumed that the first element is the
callback name. In this case, the C<with_eo> option is also automatically
enabled.

 $eo->register_callback(myEvent => sub {
     ...
 }, 'some.callback, priority => 200);

See the above method specification for parameters and supported options.

=head2 $eo->register_callbacks(@events)

Registers several event callbacks at once.

The arguments should be a list of hash
references. These references take the same options as
C<< ->register_callback() >>. Returns a list of return values in the order that
the events were specified.

 $eo->register_callbacks(
     { myEvent => \&my_event_1, name => 'cb.1', priority => 200 },
     { myEvent => \&my_event_2, name => 'cb.2', priority => 100 }
 );

B<Parameters>

=over

=item *

B<@events> - array of hash references to pass to C<< ->register_callback() >>.

=back

=head2 $eo->delete_event($event_name)

Deletes all callbacks registered for the supplied event.

Returns number of callbacks deleted, false if none.

 $eo->delete_event('myEvent');

B<Parameters>

=over

=item *

B<$event_name> - name of the event.

lib/Evented/Object.pm  view on Meta::CPAN

B<$event_name> - name of the event.

=item *

B<$callback_name> - name of the callback being removed.

=back

=head2 $eo->delete_all_events()

Deletes all events and all callbacks from the object.

If you know that an
evented object will no longer be used in your program, by calling this method
you can be sure that no cyclical references from within callbacks will cause the
object to be leaked.

=head1 FIRING EVENTS

=head2 $eo->fire_event($event_name => @arguments)

Fires the specified event, calling each callback that was registered with
C<< ->register_callback() >> in descending order of their priorities.

Returns the L<fire object|Evented::Object::EventFire>.

lib/Evented/Object.pm  view on Meta::CPAN

B<Parameters>

=over

=item *

B<$event_name> - name of the event being fired.

=item *

B<@arguments> - I<optional>, list of arguments to pass to event callbacks.

=back

=head2 $eo->fire_once($event_name => @arguments)

Fires the specified event, calling each callback that was registered with
C<< ->register_callback() >> in descending order of their priorities.

Then, all callbacks for the event are deleted. This method is useful for
situations where an event will never be fired more than once.

Returns the L<fire object|Evented::Object::EventFire>.

 $eo->fire_once('some_event');
 $eo->fire_event(some_event => $some_argument, $some_other_argument);
 # the second does nothing because the first deleted the callbacks

B<Parameters>

=over

=item *

B<$event_name> - name of the event being fired.

=item *

B<@arguments> - I<optional>, list of arguments to pass to event callbacks.

=back

=head2 $eo->fire_events_together(@events)

The C<fire_events_together()> function can be used as a method on evented
objects. See the documentation for the function in L</"PROCEDURAL FUNCTIONS">.

=head2 $eo->prepare_event(event_name => @arguments)

Prepares a single event for firing.

Returns an L<Evented::Object::Collection> representing the pending callbacks.

 # an example using the fire option return_check.
 $eo->prepare_event(some_event => @arguments)->fire('return_check');

=head2 $eo->prepare_together(@events)

The preparatory method equivalent to C<< ->fire_events_together >>.

Returns an L<Evented::Object::Collection> representing the pending callbacks.

=head2 $eo->prepare(...)

A smart method that uses the best guess between C<< ->prepare_event >> and
C<< ->prepare_together >>.

 # uses ->prepare_event()
 $eo->prepare(some_event => @arguments);

 # uses ->prepare_together()

lib/Evented/Object.pm  view on Meta::CPAN

Consider a scenario where you have a class whose objects represent a farm. You
have another class which represents a cow. You would like to use the same
callback for all of the moos that occur on the farm, regardless of which cow
initiated it.

Rather than attaching an event callback to every cow, you can instead make the
farm a listener of the cow. Then, you can attach a single callback to your farm.
If your cow's event for mooing is C<moo>, your farm's event for any mooing is
C<cow.moo>.

When an event is fired on an object, the same fire object is used for callbacks
belonging to both the evented object and its listening objects. Therefore,
callback names should be unique not only to the listener object but to the
object being listened on as well.

You should also note the values of the
L<fire object|Evented::Object::EventFire>:

=over

=item *

lib/Evented/Object.pm  view on Meta::CPAN

B<< $fire->event_name >> - name of the event from the perspective of the
listener; i.e. C<cow.moo> (NOT C<moo>)

=item *

B<< $fire->object >> - object being listened to; i.e. C<$cow> (NOT C<$farm>)

=back

This also means that stopping the event from a listener object will cancel
I<all> remaining callbacks.

=head2 $eo->add_listener($other_eo, $prefix)

Makes the passed evented object a listener of this evented object.

See L</"LISTENERS">.

 $cow->add_listener($farm, 'cow');

B<Parameters>

lib/Evented/Object.pm  view on Meta::CPAN

=item *

B<$other_eo> - evented object that will listen.

=back

=head1 CLASS MONITORS

An evented object can be registered as a "monitor" of a specific class/package.

I<All> event callbacks that are added from that class to I<any> evented object
of I<any> type will trigger an event on the monitor object.

An example scenario of when this might be useful is an evented object for
debugging all events being registered by a certain package. It would log all of
them, making it easier to find a problem.

=head2 $eo->monitor_events($pkg)

Registers an evented object as the class monitor for a specific package.

lib/Evented/Object.pm  view on Meta::CPAN


The Evented::Object package provides some functions for use. These functions
typically are associated with more than one evented object or none at all.

=head2 fire_events_together(@events)

Fires multiple events at the same time.

This allows you to fire multiple similar
events on several evented objects at the same time. It essentially pretends that
the callbacks are all for the same event and all on the same object.

It follows priorities throughout all of the events and all of the objects, so it
is ideal for firing similar or identical events on multiple objects.

The same L<fire object|Evented::Object::EventFire> is used throughout.
This means that callback names must unique among all of these
objects and events. It also means that stopping an event from any callback will
cancel all remaining callbacks, regardless to which event or which object they
belong.

The function takes a list of array references in the form of:
C<< [ $evented_object, event_name => @arguments ] >>

 Evented::Object::fire_events_together(
     [ $server,  user_joined_channel => $user, $channel ],
     [ $channel, user_joined         => $user           ],
     [ $user,    joined_channel      => $channel        ]
 );

lib/Evented/Object.pm  view on Meta::CPAN

=head2 $eo->fire(...)

Alias for C<< $eo->fire_event() >>.

=head2 $eo->register_event(...)

Alias for C<< $eo->register_callback() >>.

=head2 $eo->register_events(...)

Alias for C<< $eo->register_callbacks() >>.

=head2 $fire->eo

Alias for C<< $fire->object >>.

=head1 AUTHOR

L<Mitchell Cooper|https://github.com/cooper> <cooper@cpan.org>

Copyright E<copy> 2011-2020. Released under New BSD license.

lib/Evented/Object/Collection.pm  view on Meta::CPAN

my %boolopts = map { $_ => 1 } qw(safe return_check fail_continue);

sub new {
    return bless {
        pending         => {},
        default_names   => {},
        names           => {}
    }, shift;
}

sub push_callbacks {
    my ($collection, $callbacks, $names) = @_;
    my $pending  = $collection->{pending};
    my $my_names = $collection->{names};

    # add to pending callbacks and callback name-to-ID mapping.
    @$pending { keys %$callbacks } = values %$callbacks;
    @$my_names{ keys %$names     } = values %$names;

    # set default names for any callback names which were not found
    $collection->{default_names}{ $_->[2]{name} } ||= $_->[2]{id}
        for values %$callbacks;
}

#
#   Available fire options
#   ----------------------
#
#   safe            calls all callbacks within eval blocks.
#                   consumes no parameter.
#
#   return_check    causes the event to ->stop if any callback returns false
#                   BUT IT WAITS until all have been fired. so if one returns false,
#                   the rest will be called, but $fire->stopper will be true afterward.
#                   consumes no parameter.
#
#   caller          specify an alternate [caller 1] value, mostly for internal use.
#                   parameter = caller(1) info wrapped in an array reference.
#

lib/Evented/Object/Collection.pm  view on Meta::CPAN


    # create fire object.
    my $fire = Evented::Object::EventFire->new(
        caller     => $caller ||= [caller 1], # $fire->caller
        data       => $data,                  # $fire->data
        collection => $collection
    );

    # if it hasn't been sorted, do so now.
    $collection->sort if not $collection->{sorted};
    my $callbacks = $collection->{sorted} or return $fire;

    # if return_check is enabled, add a callback to be fired last that will
    # check the return values. this is basically hackery using a dummy object.
    if ($collection->{return_check}) {
        my $cb = {
            name   => 'eventedObject.returnCheck',
            caller => $caller,
            code   => \&_return_check
        };
        my $group = [
            $dummy ||= Evented::Object->new,
            'returnCheck',
            [],
            "$dummy/returnCheck"
        ];
        push @$callbacks, [
            -inf,           # [0] $priority
            $group,         # [1] $group
            $cb             # [2] $cb
        ];
        $cb->{id} = "$$group[3]/$$cb{name}";
        $collection->{pending}{ $cb->{id} } = $cb;
    }

    # call them.
    return $collection->_call_callbacks($fire);

}

# sorts the callbacks, trying its best to listen to before and after.
sub sort : method {
    my $collection = shift;
    return unless $collection->{pending};
    my %callbacks = %{ $collection->{pending} };
    my (@sorted, %done, %waited);

    # iterate over the callback sets,
    # which are array refs of [ priority, group, cb ]
    my @callbacks = values %callbacks;
    while (my $set = shift @callbacks) {
        my ($priority, $group, $cb) = @$set;
        my $cb_id    = $cb->{id};
        my $group_id = $group->[3];

        next if $done{$cb_id};

        # a real priority exists already.
        if (defined $priority && $priority ne 'nan') {
            push @sorted, $set;
            $done{$cb_id} = 1;

lib/Evented/Object/Collection.pm  view on Meta::CPAN

        # are currently skipped. maybe there should be a way to specify that a callback
        # dependency is REQUIRED, meaning to skip the callback entirely if it cannot
        # be done. or maybe something more sophisticated that can prioritize the
        # befores and afters in this way. for now though, we will just try to not
        # specify impossible befores and afters.



        # callback priority determination can be postponed until another's
        # priority is determined. the maxmium number of times one particular
        # callback can be postponed is the number of total callbacks.
        my $wait_max = keys %callbacks;

        my $name_to_id = $collection->_group_names($group_id);
        my $get_befores_afters = sub {
            my ($key, @results) = shift;
            my $list = $cb->{$key} or return;
            $list = [ $list ] if ref $list ne 'ARRAY';

            # for each callback name, find its priority.
            foreach my $their_name (@$list) {

                # map callback name to id, id to cbref, and cbref to priority.
                my $their_id = $name_to_id->{$their_name} or next;
                my $their_cb = $callbacks{$their_id}      or next;
                my $their_p  = $their_cb->[0];

                # if their priority is nan,
                # we have to wait until it is determined.
                if ($their_p eq 'nan') {
                    my $wait_key = "$cb_id $their_id";
                    push @callbacks, $set
                        unless $waited{$key}++ > $wait_max;
                    return 1;
                }

                push @results, $their_p;
            }

            return (undef, @results);
        };

lib/Evented/Object/Collection.pm  view on Meta::CPAN

    }

    # the final sort by numerical priority.
    $collection->{sorted} = [ sort { $b->[0] <=> $a->[0] } @sorted ];

}

# Nov. 22, 2013 revision
# ----------------------
#
#   collection      a set of callbacks about to be fired. they might belong to multiple
#                   objects or maybe even multiple events. they can each have their own
#                   arguments, and they all have their own options, code references, etc.
#
#        group      represents the group to which a callback belongs. a group consists of
#                   the associated evented object, event name, and arguments.
#
# This revision eliminates all of these nested structures by reworking the way
# a callback collection works. A collection should be an array of callbacks.
# This array, unlike before, will contain an additional element: an array
# reference representing the "group."
#
#   @collection = (
#       [ $priority, $group, $cb ],
#       [ $priority, $group, $cb ],
#       ...
#   )
#
#   $group =                                $cb =
#   [ $eo, $event_name, $args, $id ]        { code, caller, %opts }
#
# This format has several major advantages over the former one. Specifically,
# it makes it very simple to determine which callbacks will be called in the
# future, which ones have been called already, how many are left, etc.
#

# call the passed callback priority sets.
sub _call_callbacks {
    my ($collection, $fire) = @_;
    my $ef_props = $fire->{$props};

    # store the collection.
    my $remaining = $collection->{sorted} or return;
    $ef_props->{collection} = $collection;

    # call each callback.
    while (my $entry = shift @$remaining) {
        my ($priority, $group, $cb) = @$entry;

lib/Evented/Object/Collection.pm  view on Meta::CPAN

        return $fire->stop("$cb_id returned false with return_check enabled");
    }
    return 1;
}

1;

=head1 NAME

B<Evented::Object::Collection> - represents a group of pending
L<Evented::Object> callbacks.

=head1 DESCRIPTION

L</Collections> are returned by the evented object 'prepare' methods. They
represent a group of callbacks that are about to be fired. Using collections
allows you to prepare a fire ahead of time before executing it. You can also
fire events with special options this way.

=head1 METHODS


=head2 $col->fire(@options)

Fires the pending callbacks with the specified options, if any. If the callbacks
have not yet been sorted, they are sorted before the event is fired.

 $eo->prepare(some_event => @arguments)->fire('safe');

B<Parameters>

=over

=item *

lib/Evented/Object/Collection.pm  view on Meta::CPAN

=over

=item *

B<caller> - I<requires value>, use an alternate C<[caller 1]> value for the event
fire. This is typically only used internally.

=item *

B<return_check> - I<boolean>, if true, the event will yield that it was stopped
if any of the callbacks return a false value. Note however that if one callbacks
returns false, the rest will still be called. The fire object will only yield
stopped status after all callbacks have been called and any number of them
returned false.

=item *

B<safe> - I<boolean>, wrap all callback calls in C<eval> for safety. if any of
them fail, the event will be stopped at that point with the error.

=item *

B<fail_continue> - I<boolean>, if C<safe> above is enabled, this tells the fire
to continue even if one of the callbacks fails. This could be dangerous if any
of the callbacks expected a previous callback to be done when it actually
failed.

=item *

B<data> - I<requires value>, a scalar value that can be fetched by
C<< $fire->data >> from within the callbacks. Good for data that might be useful
sometimes but not frequently enough to deserve a spot in the argument list. If
C<data> is a hash reference, its values can be fetched conveniently with
C<< $fire->data('key') >>.

=back

=head2 $col->sort

Sorts the callbacks according to C<priority>, C<before>, and C<after> options.

=head1 AUTHOR

L<Mitchell Cooper|https://github.com/cooper> <cooper@cpan.org>

Copyright E<copy> 2011-2017. Released under New BSD license.

Comments, complaints, and recommendations are accepted. Bugs may be reported on
L<GitHub|https://github.com/cooper/evented-object/issues>.

lib/Evented/Object/EventFire.pm  view on Meta::CPAN

our $events  = $Evented::Object::events;
our $props   = $Evented::Object::props;

# create a new fire object.
sub new {
    my ($class, %opts) = @_;
    $opts{callback_i} ||= 0;
    return bless { $props => \%opts }, $class;
}

# cancel all future callbacks once.
# if stopped already, returns the reason.
sub stop {
    my ($fire, $reason) = @_;
    $fire->{$props}{stop} ||= $reason || 'unspecified';
}

# returns a true value if the given callback has been called.
# with no argument, returns number of callbacks called so far.
sub called {
    my ($fire, $cb_name) = @_;

    # return the number of callbacks called.
    # this includes the current callback.
    if (!length $cb_name) {
        my $called = scalar keys %{ $fire->{$props}{called} };
        $called++ unless $fire->{$props}{complete};
        return $called;
    }

    # return whether the specified callback was called.
    my $cb_id = $fire->_cb_id($cb_name) or return;
    return $fire->{$props}{called}{$cb_id};
}

# returns a true value if the given callback will be called soon.
# with no argument, returns number of callbacks pending.
sub pending {
    my ($fire, $cb_name) = @_;
    my $pending = $fire->{$props}{collection}{pending};

    # return number of callbacks remaining.
    if (!length $cb_name) {
        return scalar keys %$pending;
    }

    # return whether the specified callback is pending.
    my $cb_id = $fire->_cb_id($cb_name) or return;
    return $pending->{$cb_id};
}

# cancels a future callback once.

lib/Evented/Object/EventFire.pm  view on Meta::CPAN

1;

=head1 NAME

B<Evented::Object::EventFire> - represents an L<Evented::Object> event fire.

=head1 DESCRIPTION

The fire object provides methods for fetching information related to the current
event fire. It also provides an interface for modifying the behavior of the
remaining callbacks.

Fire objects are specific to the particular event fire, not the event itself.
If you fire the same event twice in a row, the fire object used the first time
will not be the same as the second time. Therefore, all modifications made by
the fire object's methods apply only to the callbacks remaining in this
particular fire. For example, C<< $fire->cancel($callback) >> will only cancel
the supplied callback once.

=head1 METHODS

=head2 $fire->object

Returns the evented object.

 $fire->object->delete_event('myEvent');

lib/Evented/Object/EventFire.pm  view on Meta::CPAN


Returns the value of C<caller(1)> from within the C<< ->fire() >> method.
This allows you to determine from where the event was fired.

 my $name   = $fire->event_name;
 my @caller = $fire->caller;
 say "Package $caller[0] line $caller[2] called event $name";

=head2 $fire->stop($reason)

Cancels all remaining callbacks. This stops the rest of the event firing. After
a callback calls $fire->stop, the name of that callback is stored as
C<< $fire->stopper >>.

If the event has already been stopped, this method returns the reason for which
the fire was stopped or "unspecified" if no reason was given.

 # ignore messages from trolls
 if ($user eq 'noah') {
     # user is a troll.
     # stop further callbacks.
     return $fire->stop;
 }

=over

=item *

B<$reason> - I<optional>, the reason for stopping the event fire.

=back

lib/Evented/Object/EventFire.pm  view on Meta::CPAN


Returns the callback which called C<< $fire->stop >>.

 if ($fire->stopper) {
     say 'Fire was stopped by '.$fire->stopper;
 }

=head2 $fire->exception

If the event was fired with the C<< safe >> option, it is possible that an
exception occurred in one (or more if C<< fail_continue >> enabled) callbacks.
This method returns the last exception that occurred or C<< undef >> if none
did.

 if (my $e = $fire->exception) {
    say "Exception! $e";
 }

=head2 $fire->called($callback)

If no argument is supplied, returns the number of callbacks called so far,
including the current one. If a callback argument is supplied, returns whether
that particular callback has been called.

 say $fire->called, 'callbacks have been called so far.';

 if ($fire->called('some.callback')) {
     say 'some.callback has been called already.';
 }

B<Parameters>

=over

=item *

B<$callback> - I<optional>, the callback being checked.

=back

=head2 $fire->pending($callback)

If no argument is supplied, returns the number of callbacks pending to be
called, excluding the current one. If a callback  argument is supplied, returns
whether that particular callback is pending for being called.

 say $fire->pending, ' callbacks are left.';

 if ($fire->pending('some.callback')) {
     say 'some.callback will be called soon (unless it gets canceled)';
 }

B<Parameters>

=over

=item *

t/01priorities.t  view on Meta::CPAN

use warnings;

use Test::More;
use Evented::Object;


# tests basic priorities
my @results;
my $eo = Evented::Object->new;

# add several callbacks which push numbers to @results
$eo->register_callback(hi => sub {
    push @results, 100;
}, priority => 100);

$eo->register_callback(hi => sub {
    push @results, 200;
}, priority => 200);

$eo->register_callback(hi => sub {
    push @results, -5;

t/02delete-event.t  view on Meta::CPAN

use strict;
use warnings;

use Test::More;
use Evented::Object;


my @results;
my $eo = Evented::Object->new;

# add several callbacks which push to @results
$eo->register_callback(hi => sub { push @results, @_ })
    for 1..5;

# delete all callbacks then fire it
$eo->delete_event('hi');
$eo->fire_event('hi');

# better have no results
is(scalar @results, 0, 'deleting entire event');


done_testing;

t/03delete-callback.t  view on Meta::CPAN


use Test::More;
use Evented::Object;


# tests deleting a single callback.
# ensures only that specific callback is deleted.
my $eo = Evented::Object->new;
my ($lost, $won);

# add two callbacks, one which will be deleted
$eo->register_callback(hi => sub {
    $won = 1;
}, priority => 100);

$eo->register_callback(hi => sub {
    $lost = 1;
}, name => 'loser');

# delete one of them and fire
$eo->delete_callback('hi', 'loser');

t/11complex-priorities.t  view on Meta::CPAN

use strict;
use warnings;

use Test::More;
use Evented::Object;


my @results;
my $eo = Evented::Object->new;

# add several callbacks with complex befores and afters to resolve
my $cb = sub { push @results, shift->callback_name };
$eo->register_callback(event => $cb, name => 'third');
$eo->register_callback(event => $cb, name => 'second', before => 'third');
$eo->register_callback(event => $cb, name => 'first',  before => ['second', 'third']);
$eo->register_callback(event => $cb, name => 'fourth', after  => ['first', 'third']);

# fire the event
$eo->fire_event('event');

# make sure they occurred in the correct order

t/12impossible-priorities.t  view on Meta::CPAN

use warnings;

use Test::More;
use Evented::Object;


my @results;
my $eo = Evented::Object->new;
my $cb = sub { push @results, shift->callback_name };

# add callbacks with befores and afters that cannot be resolved
$eo->register_callback(event => $cb, name => 'unresolvable1',
    before => 'first', after => ['fourth', 'unresolvable2']);
$eo->register_callback(event => $cb, name => 'unresolvable2',
    before => ['first', 'unresolvable1'], after => 'fourth');

# fire the event
$eo->fire_event('event');
pass('unresolvable priorities ok');




( run in 0.443 second using v1.01-cache-2.11-cpan-9b1e4054eb1 )