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