Beam-Emitter
view release on metacpan or search on metacpan
lib/Beam/Emitter.pm view on Meta::CPAN
#pod data => \@data,
#pod );
#pod
#pod # Give event listeners a chance to stop the write
#pod return if $event->is_default_stopped;
#pod
#pod # Write the data
#pod open my $file, '>', 'output';
#pod print { $file } @data;
#pod close $file;
#pod
#pod # Notify listeners we're done writing and send them the data
#pod # we wrote
#pod $self->emit( 'after_write', class => 'My::Event', data => \@data );
#pod }
#pod }
#pod
#pod # An event handler that increments every input value in our data
#pod sub increment {
#pod my ( $event ) = @_;
#pod my $data = $event->data;
#pod $_++ for @$data;
#pod }
#pod
#pod # An event handler that performs data validation and stops the
#pod # processing if invalid
#pod sub prevent_negative {
#pod my ( $event ) = @_;
#pod my $data = $event->data;
#pod $event->prevent_default if grep { $_ < 0 } @$data;
#pod }
#pod
#pod # An event handler that logs the data to STDERR after we've written in
#pod sub log_data {
#pod my ( $event ) = @_;
#pod my $data = $event->data;
#pod print STDERR "Wrote data: " . join( ',', @$data );
#pod }
#pod
#pod # Wire up our event handlers to a new processing object
#pod my $processor = My::Emitter->new;
#pod $processor->on( process_data => \&increment );
#pod $processor->on( process_data => \&prevent_negative );
#pod $processor->on( after_write => \&log_data );
#pod
#pod # Process some data
#pod $processor->process_data( 1, 2, 3, 4, 5 );
#pod $processor->process_data( 1, 3, 7, -9, 11 );
#pod
#pod # Log data before and after writing
#pod my $processor = My::Emitter->new;
#pod $processor->on( process_data => \&log_data );
#pod $processor->on( after_write => \&log_data );
#pod
#pod =head1 DESCRIPTION
#pod
#pod This role is used by classes that want to add callback hooks to allow
#pod users to add new behaviors to their objects. These hooks are called
#pod "events". A subscriber registers a callback for an event using the
#pod L</subscribe> or L</on> methods. Then, the class can call those
#pod callbacks by L<emitting an event with the emit() method|/emit>.
#pod
#pod Using the L<Beam::Event> class, subscribers can stop an event from being
#pod processed, or prevent the default action from happening.
#pod
#pod =head2 Using Beam::Event
#pod
#pod L<Beam::Event> is an event object with some simple methods to allow subscribers
#pod to influence the handling of the event. By calling L<the stop
#pod method|Beam::Event/stop>, subscribers can stop all futher handling of the
#pod event. By calling the L<the stop_default method|Beam::Event/stop_default>,
#pod subscribers can allow other subscribers to be notified about the event, but let
#pod the emitter know that it shouldn't continue with what it was going to do.
#pod
#pod For example, let's build a door that notifies when someone tries to open it.
#pod Different instances of a door should allow different checks before the door
#pod opens, so we'll emit an event before we decide to open.
#pod
#pod package Door;
#pod use Moo;
#pod with 'Beam::Emitter';
#pod
#pod sub open {
#pod my ( $self, $who ) = @_;
#pod my $event = $self->emit( 'before_open' );
#pod return if $event->is_default_stopped;
#pod $self->open_the_door;
#pod }
#pod
#pod package main;
#pod my $door = Door->new;
#pod $door->open;
#pod
#pod Currently, our door will open for anybody. But let's build a door that only
#pod open opens after noon (to keep us from having to wake up in the morning).
#pod
#pod use Time::Piece;
#pod my $restful_door = Door->new;
#pod
#pod $restful_door->on( before_open => sub {
#pod my ( $event ) = @_;
#pod
#pod my $time = Time::Piece->now;
#pod if ( $time->hour < 12 ) {
#pod $event->stop_default;
#pod }
#pod
#pod } );
#pod
#pod $restful_door->open;
#pod
#pod By calling L<stop_default|Beam::Event/stop_default>, we set the
#pod L<is_default_stopped|Beam::Event/is_default_stopped> flag, which the door sees
#pod and decides not to open.
#pod
#pod =head2 Using Custom Events
#pod
#pod The default C<Beam::Event> is really only useful for notifications. If you want
#pod to give your subscribers some data, you need to create a custom event class.
#pod This allows you to add attributes and methods to your events (with all
#pod the type constraints and coersions you want).
lib/Beam/Emitter.pm view on Meta::CPAN
#pod An alias for L</subscribe>. B<NOTE>: Do not use this alias for method
#pod modifiers! If you want to override behavior, override C<subscribe>.
#pod
#pod =cut
sub on { shift->subscribe( @_ ) }
#pod =method unsubscribe ( event_name [, subref ] )
#pod
#pod Unsubscribe from an event. C<event_name> is the name of the event. C<subref> is
#pod the single listener subref to be removed. If no subref is given, will remove
#pod all listeners for this event.
#pod
#pod =cut
sub unsubscribe {
my ( $self, $name, $sub ) = @_;
if ( !$sub ) {
delete $self->_listeners->{$name};
}
else {
my $listeners = $self->_listeners->{$name};
my $idx = 0;
$idx++ until $idx > $#{$listeners} or refaddr $listeners->[$idx]->callback eq refaddr $sub;
if ( $idx > $#{$listeners} ) {
croak "Could not find sub in listeners";
}
splice @{$self->_listeners->{$name}}, $idx, 1;
}
return;
}
#pod =method un ( event_name [, subref ] )
#pod
#pod An alias for L</unsubscribe>. B<NOTE>: Do not use this alias for method
#pod modifiers! If you want to override behavior, override C<unsubscribe>.
#pod
#pod =cut
sub un { shift->unsubscribe( @_ ) }
#pod =method emit ( name, event_args )
#pod
#pod Emit a L<Beam::Event> with the given C<name>. C<event_args> is a list of name => value
#pod pairs to give to the C<Beam::Event> constructor.
#pod
#pod Use the C<class> key in C<event_args> to specify a different Event class.
#pod
#pod =cut
sub emit {
my ( $self, $name, %args ) = @_;
my $class = delete $args{ class } || "Beam::Event";
$args{ emitter } = $self if ! defined $args{ emitter };
$args{ name } ||= $name;
my $event = $class->new( %args );
return $event unless exists $self->_listeners->{$name};
# don't use $self->_listeners->{$name} directly, as callbacks may unsubscribe
# from $name, changing the array, and confusing the for loop
my @listeners = @{ $self->_listeners->{$name} };
for my $listener ( @listeners ) {
$listener->callback->( $event );
last if $event->is_stopped;
}
return $event;
}
#pod =method emit_args ( name, callback_args )
#pod
#pod Emit an event with the given C<name>. C<callback_args> is a list that will be given
#pod directly to each subscribed callback.
#pod
#pod Use this if you want to avoid using L<Beam::Event>, though you miss out on the control
#pod features like L<stop|Beam::Event/stop> and L<stop default|Beam::Event/stop_default>.
#pod
#pod =cut
sub emit_args {
my ( $self, $name, @args ) = @_;
return unless exists $self->_listeners->{$name};
# don't use $self->_listeners->{$name} directly, as callbacks may unsubscribe
# from $name, changing the array, and confusing the for loop
my @listeners = @{ $self->_listeners->{$name} };
for my $listener ( @listeners ) {
$listener->callback->( @args );
}
return;
}
#pod =method listeners ( event_name )
#pod
#pod Returns a list containing the listeners which have subscribed to the
#pod specified event from this emitter. The list elements are either
#pod instances of L<Beam::Listener> or of custom classes specified in calls
#pod to L</subscribe>.
#pod
#pod =cut
sub listeners {
my ( $self, $name ) = @_;
return @{ $self->_listeners->{$name} || [] };
}
1;
__END__
=pod
=head1 NAME
Beam::Emitter - Role for event emitting classes
=head1 VERSION
version 1.007
=head1 SYNOPSIS
# A simple custom event class to perform data validation
{ package My::Event;
use Moo;
extends 'Beam::Event';
has data => ( is => 'ro' );
}
# A class that reads and writes data, allowing event handlers to
# process the data
{ package My::Emitter;
use Moo;
with 'Beam::Emitter';
sub write_data {
my ( $self, @data ) = @_;
# Give event listeners a chance to perform further processing of
# data
my $event = $self->emit( "process_data",
lib/Beam/Emitter.pm view on Meta::CPAN
data => \@data,
);
# Give event listeners a chance to stop the write
return if $event->is_default_stopped;
# Write the data
open my $file, '>', 'output';
print { $file } @data;
close $file;
# Notify listeners we're done writing and send them the data
# we wrote
$self->emit( 'after_write', class => 'My::Event', data => \@data );
}
}
# An event handler that increments every input value in our data
sub increment {
my ( $event ) = @_;
my $data = $event->data;
$_++ for @$data;
}
# An event handler that performs data validation and stops the
# processing if invalid
sub prevent_negative {
my ( $event ) = @_;
my $data = $event->data;
$event->prevent_default if grep { $_ < 0 } @$data;
}
# An event handler that logs the data to STDERR after we've written in
sub log_data {
my ( $event ) = @_;
my $data = $event->data;
print STDERR "Wrote data: " . join( ',', @$data );
}
# Wire up our event handlers to a new processing object
my $processor = My::Emitter->new;
$processor->on( process_data => \&increment );
$processor->on( process_data => \&prevent_negative );
$processor->on( after_write => \&log_data );
# Process some data
$processor->process_data( 1, 2, 3, 4, 5 );
$processor->process_data( 1, 3, 7, -9, 11 );
# Log data before and after writing
my $processor = My::Emitter->new;
$processor->on( process_data => \&log_data );
$processor->on( after_write => \&log_data );
=head1 DESCRIPTION
This role is used by classes that want to add callback hooks to allow
users to add new behaviors to their objects. These hooks are called
"events". A subscriber registers a callback for an event using the
L</subscribe> or L</on> methods. Then, the class can call those
callbacks by L<emitting an event with the emit() method|/emit>.
Using the L<Beam::Event> class, subscribers can stop an event from being
processed, or prevent the default action from happening.
=head2 Using Beam::Event
L<Beam::Event> is an event object with some simple methods to allow subscribers
to influence the handling of the event. By calling L<the stop
method|Beam::Event/stop>, subscribers can stop all futher handling of the
event. By calling the L<the stop_default method|Beam::Event/stop_default>,
subscribers can allow other subscribers to be notified about the event, but let
the emitter know that it shouldn't continue with what it was going to do.
For example, let's build a door that notifies when someone tries to open it.
Different instances of a door should allow different checks before the door
opens, so we'll emit an event before we decide to open.
package Door;
use Moo;
with 'Beam::Emitter';
sub open {
my ( $self, $who ) = @_;
my $event = $self->emit( 'before_open' );
return if $event->is_default_stopped;
$self->open_the_door;
}
package main;
my $door = Door->new;
$door->open;
Currently, our door will open for anybody. But let's build a door that only
open opens after noon (to keep us from having to wake up in the morning).
use Time::Piece;
my $restful_door = Door->new;
$restful_door->on( before_open => sub {
my ( $event ) = @_;
my $time = Time::Piece->now;
if ( $time->hour < 12 ) {
$event->stop_default;
}
} );
$restful_door->open;
By calling L<stop_default|Beam::Event/stop_default>, we set the
L<is_default_stopped|Beam::Event/is_default_stopped> flag, which the door sees
and decides not to open.
=head2 Using Custom Events
The default C<Beam::Event> is really only useful for notifications. If you want
to give your subscribers some data, you need to create a custom event class.
This allows you to add attributes and methods to your events (with all
the type constraints and coersions you want).
( run in 3.365 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )