File Coverage

blib/lib/Role/EventEmitter.pm
Criterion Covered Total %
statement 41 41 100.0
branch 10 12 83.3
condition 3 4 75.0
subroutine 11 11 100.0
pod 7 7 100.0
total 72 75 96.0


line stmt bran cond sub pod time code
1             package Role::EventEmitter;
2              
3 1     1   7174 use Scalar::Util qw(blessed weaken);
  1         2  
  1         92  
4 1   50 1   5 use constant DEBUG => $ENV{ROLE_EVENTEMITTER_DEBUG} || 0;
  1         2  
  1         68  
5              
6 1     1   13 use Moo::Role;
  1         1  
  1         5  
7              
8             our $VERSION = '0.001';
9              
10             has '_events' => (
11             is => 'ro',
12             lazy => 1,
13             default => sub { {} },
14             init_arg => undef,
15             );
16              
17 1 50   1 1 200 sub catch { $_[0]->on(error => $_[1]) and return $_[0] }
18              
19             sub emit {
20 31     31 1 1358 my ($self, $name) = (shift, shift);
21              
22 31 100       61 if (my $s = $self->_events->{$name}) {
23 29         712 warn "-- Emit $name in @{[blessed $self]} (@{[scalar @$s]})\n" if DEBUG;
24 29         40 for my $cb (@$s) { $self->$cb(@_) }
  32         69  
25             }
26             else {
27 2         50 warn "-- Emit $name in @{[blessed $self]} (0)\n" if DEBUG;
28 2 100       7 die "@{[blessed $self]}: $_[0]" if $name eq 'error';
  1         10  
29             }
30              
31 28         101 return $self;
32             }
33              
34 8     8 1 603 sub has_subscribers { !!shift->_events->{shift()} }
35              
36 19 50   19 1 5977 sub on { push @{$_[0]->_events->{$_[1]}}, $_[2] and return $_[2] }
  19         41  
37              
38             sub once {
39 11     11 1 587 my ($self, $name, $cb) = @_;
40              
41 11         22 weaken $self;
42 11         11 my $wrapper;
43             $wrapper = sub {
44 10     10   202 $self->unsubscribe($name => $wrapper);
45 10         19 $cb->(@_);
46 11         25 };
47 11         16 $self->on($name => $wrapper);
48 11         287 weaken $wrapper;
49              
50 11         19 return $wrapper;
51             }
52              
53 18   100 18 1 794 sub subscribers { shift->_events->{shift()} ||= [] }
54              
55             sub unsubscribe {
56 15     15 1 364 my ($self, $name, $cb) = @_;
57              
58             # One
59 15 100       21 if ($cb) {
60 14         15 $self->_events->{$name} = [grep { $cb ne $_ } @{$self->_events->{$name}}];
  22         343  
  14         20  
61 14 100       271 delete $self->_events->{$name} unless @{$self->_events->{$name}};
  14         24  
62             }
63              
64             # All
65 1         4 else { delete $self->_events->{$name} }
66              
67 15         445 return $self;
68             }
69              
70             1;
71              
72             =head1 NAME
73              
74             Role::EventEmitter - Event emitter role for Moo(se) classes
75              
76             =head1 SYNOPSIS
77              
78             package Cat;
79             use Moo;
80             with 'Role::EventEmitter';
81              
82             # Emit events
83             sub poke {
84             my $self = shift;
85             $self->emit(roar => 3);
86             }
87              
88             package main;
89              
90             # Subscribe to events
91             my $tiger = Cat->new;
92             $tiger->on(roar => sub {
93             my ($tiger, $times) = @_;
94             say 'RAWR!' for 1 .. $times;
95             });
96             $tiger->poke;
97              
98             =head1 DESCRIPTION
99              
100             L is a simple L for event emitting objects based
101             on L.
102              
103             =head1 EVENTS
104              
105             L can emit the following events.
106              
107             =head2 error
108              
109             $e->on(error => sub {
110             my ($e, $err) = @_;
111             ...
112             });
113              
114             This is a special event for errors, it will not be emitted directly by the
115             class consuming this role but is fatal if unhandled.
116              
117             $e->on(error => sub {
118             my ($e, $err) = @_;
119             say "This looks bad: $err";
120             });
121              
122             =head1 METHODS
123              
124             L composes the following methods.
125              
126             =head2 catch
127              
128             $e = $e->catch(sub {...});
129              
130             Subscribe to L event.
131              
132             # Longer version
133             $e->on(error => sub {...});
134              
135             =head2 emit
136              
137             $e = $e->emit('foo');
138             $e = $e->emit('foo', 123);
139              
140             Emit event.
141              
142             =head2 has_subscribers
143              
144             my $bool = $e->has_subscribers('foo');
145              
146             Check if event has subscribers.
147              
148             =head2 on
149              
150             my $cb = $e->on(foo => sub {...});
151              
152             Subscribe to event.
153              
154             $e->on(foo => sub {
155             my ($e, @args) = @_;
156             ...
157             });
158              
159             =head2 once
160              
161             my $cb = $e->once(foo => sub {...});
162              
163             Subscribe to event and unsubscribe again after it has been emitted once.
164              
165             $e->once(foo => sub {
166             my ($e, @args) = @_;
167             ...
168             });
169              
170             =head2 subscribers
171              
172             my $subscribers = $e->subscribers('foo');
173              
174             All subscribers for event.
175              
176             # Unsubscribe last subscriber
177             $e->unsubscribe(foo => $e->subscribers('foo')->[-1]);
178              
179             # Change order of subscribers
180             @{$e->subscribers('foo')} = reverse @{$e->subscribers('foo')};
181              
182             =head2 unsubscribe
183              
184             $e = $e->unsubscribe('foo');
185             $e = $e->unsubscribe(foo => $cb);
186              
187             Unsubscribe from event.
188              
189             =head1 DEBUGGING
190              
191             You can set the C environment variable to get some
192             advanced diagnostics information printed to C.
193              
194             ROLE_EVENTEMITTER_DEBUG=1
195              
196             =head1 BUGS
197              
198             Report any issues on the public bugtracker.
199              
200             =head1 AUTHOR
201              
202             Dan Book
203              
204             =head1 COPYRIGHT AND LICENSE
205              
206             This software is Copyright (c) 2015 by Dan Book.
207              
208             This is free software, licensed under:
209              
210             The Artistic License 2.0 (GPL Compatible)
211              
212             =head1 SEE ALSO
213              
214             L, L, L