line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Role::EventEmitter; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
613
|
use Carp 'croak'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
63
|
|
4
|
1
|
|
|
1
|
|
7
|
use Scalar::Util qw(blessed refaddr weaken); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
54
|
|
5
|
1
|
|
50
|
1
|
|
5
|
use constant DEBUG => $ENV{ROLE_EVENTEMITTER_DEBUG} || 0; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
89
|
|
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
7
|
use Role::Tiny; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
6
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '0.003'; |
10
|
|
|
|
|
|
|
|
11
|
1
|
50
|
|
1
|
1
|
3
|
sub catch { $_[0]->on(error => $_[1]) and return $_[0] } |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
sub emit { |
14
|
31
|
|
|
31
|
1
|
2883
|
my $self = shift; |
15
|
31
|
|
|
|
|
73
|
my $name = shift; |
16
|
31
|
100
|
|
|
|
116
|
if (my $s = $self->{_role_ee_events}{$name}) { |
17
|
29
|
|
|
|
|
48
|
warn "-- Emit $name in @{[blessed $self]} (@{[scalar @$s]})\n" if DEBUG; |
18
|
29
|
|
|
|
|
78
|
for my $cb (@$s) { $self->$cb(@_) } |
|
32
|
|
|
|
|
138
|
|
19
|
|
|
|
|
|
|
} else { |
20
|
2
|
|
|
|
|
5
|
warn "-- Emit $name in @{[blessed $self]} (0)\n" if DEBUG; |
21
|
2
|
100
|
|
|
|
8
|
die "@{[blessed $self]}: $_[0]" if $name eq 'error'; |
|
1
|
|
|
|
|
11
|
|
22
|
|
|
|
|
|
|
} |
23
|
28
|
|
|
|
|
175
|
return $self; |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
8
|
|
|
8
|
1
|
589
|
sub has_subscribers { !!$_[0]->{_role_ee_events}{$_[1]} } |
27
|
|
|
|
|
|
|
|
28
|
19
|
50
|
|
19
|
1
|
1868
|
sub on { push @{$_[0]{_role_ee_events}{$_[1]}}, $_[2] and return $_[2] } |
|
19
|
|
|
|
|
125
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub once { |
31
|
11
|
|
|
11
|
1
|
805
|
my ($self, $name, $cb) = @_; |
32
|
|
|
|
|
|
|
|
33
|
11
|
|
|
|
|
78
|
weaken $self; |
34
|
11
|
|
|
|
|
48
|
my $wrapper; |
35
|
|
|
|
|
|
|
$wrapper = sub { |
36
|
10
|
|
|
10
|
|
56
|
$self->unsubscribe($name => $wrapper); |
37
|
10
|
|
|
|
|
45
|
$cb->(@_); |
38
|
11
|
|
|
|
|
84
|
}; |
39
|
11
|
|
|
|
|
69
|
$self->on($name => $wrapper); |
40
|
11
|
|
|
|
|
49
|
weaken $wrapper; |
41
|
|
|
|
|
|
|
|
42
|
11
|
|
|
|
|
42
|
return $wrapper; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my $has_future; |
46
|
|
|
|
|
|
|
sub once_f { |
47
|
0
|
|
|
0
|
1
|
0
|
my ($self, $name) = @_; |
48
|
|
|
|
|
|
|
|
49
|
0
|
0
|
|
|
|
0
|
unless (defined $has_future) { |
50
|
0
|
|
|
|
|
0
|
local $@; |
51
|
0
|
0
|
|
|
|
0
|
eval { require Future; $has_future = 1 } or $has_future = 0; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
52
|
|
|
|
|
|
|
} |
53
|
0
|
0
|
|
|
|
0
|
croak "Future is required for once_f method" unless $has_future; |
54
|
|
|
|
|
|
|
|
55
|
0
|
|
|
|
|
0
|
my $f = Future->new; |
56
|
0
|
|
|
0
|
|
0
|
my $wrapper = sub { $f->done(@_) }; |
|
0
|
|
|
|
|
0
|
|
57
|
0
|
|
|
|
|
0
|
$self->on($name => $wrapper); |
58
|
0
|
|
|
|
|
0
|
$self->{_role_ee_futures}{$name}{refaddr $wrapper} = $f; |
59
|
|
|
|
|
|
|
|
60
|
0
|
|
|
|
|
0
|
weaken $self; |
61
|
0
|
|
|
|
|
0
|
weaken $wrapper; |
62
|
0
|
|
|
0
|
|
0
|
return $f->on_ready(sub { $self->unsubscribe($name => $wrapper) }); |
|
0
|
|
|
|
|
0
|
|
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
18
|
|
100
|
18
|
1
|
1785
|
sub subscribers { $_[0]->{_role_ee_events}{$_[1]} ||= [] } |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub unsubscribe { |
68
|
15
|
|
|
15
|
1
|
55
|
my ($self, $name, $cb) = @_; |
69
|
15
|
100
|
|
|
|
47
|
if ($cb) { # One |
70
|
14
|
|
|
|
|
65
|
my $addr = refaddr $cb; |
71
|
14
|
|
|
|
|
30
|
$self->{_role_ee_events}{$name} = [grep { $addr != refaddr $_ } @{$self->{_role_ee_events}{$name}}]; |
|
22
|
|
|
|
|
129
|
|
|
14
|
|
|
|
|
67
|
|
72
|
14
|
100
|
|
|
|
42
|
delete $self->{_role_ee_events}{$name} unless @{$self->{_role_ee_events}{$name}}; |
|
14
|
|
|
|
|
75
|
|
73
|
14
|
50
|
33
|
|
|
82
|
if ($self->{_role_ee_futures}{$name} and my $f = delete $self->{_role_ee_futures}{$name}{$addr}) { |
74
|
0
|
|
|
|
|
0
|
$f->cancel; |
75
|
0
|
0
|
|
|
|
0
|
delete $self->{_role_ee_futures}{$name} unless keys %{$self->{_role_ee_futures}{$name}}; |
|
0
|
|
|
|
|
0
|
|
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
} else { # All |
78
|
1
|
|
|
|
|
12
|
delete $self->{_role_ee_events}{$name}; |
79
|
1
|
50
|
|
|
|
6
|
$_->cancel for values %{delete $self->{_role_ee_futures}{$name} || {}}; |
|
1
|
|
|
|
|
14
|
|
80
|
|
|
|
|
|
|
} |
81
|
15
|
|
|
|
|
52
|
return $self; |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
1; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=head1 NAME |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Role::EventEmitter - Event emitter role |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
=head1 SYNOPSIS |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
package Channel; |
93
|
|
|
|
|
|
|
use Moo; |
94
|
|
|
|
|
|
|
with 'Role::EventEmitter'; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# Emit events |
97
|
|
|
|
|
|
|
sub send_message { |
98
|
|
|
|
|
|
|
my $self = shift; |
99
|
|
|
|
|
|
|
$self->emit(message => @_); |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
package main; |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# Subscribe to events |
105
|
|
|
|
|
|
|
my $channel_a = Channel->new; |
106
|
|
|
|
|
|
|
$channel_a->on(message => sub { |
107
|
|
|
|
|
|
|
my ($channel, $text) = @_; |
108
|
|
|
|
|
|
|
say "Received message: $text"; |
109
|
|
|
|
|
|
|
}); |
110
|
|
|
|
|
|
|
$channel_a->send_message('All is well'); |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head1 DESCRIPTION |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
L is a simple L role for event emitting objects |
115
|
|
|
|
|
|
|
based on L. This role can be applied to any hash-based |
116
|
|
|
|
|
|
|
object class such as those created with L, L, or L. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=head1 EVENTS |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
L can emit the following events. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head2 error |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
$e->on(error => sub { |
125
|
|
|
|
|
|
|
my ($e, $err) = @_; |
126
|
|
|
|
|
|
|
... |
127
|
|
|
|
|
|
|
}); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
This is a special event for errors, it will not be emitted directly by this |
130
|
|
|
|
|
|
|
role but is fatal if unhandled. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
$e->on(error => sub { |
133
|
|
|
|
|
|
|
my ($e, $err) = @_; |
134
|
|
|
|
|
|
|
say "This looks bad: $err"; |
135
|
|
|
|
|
|
|
}); |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=head1 METHODS |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
L composes the following methods. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=head2 catch |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
$e = $e->catch(sub {...}); |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
Subscribe to L"error"> event. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# Longer version |
148
|
|
|
|
|
|
|
$e->on(error => sub {...}); |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head2 emit |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
$e = $e->emit('foo'); |
153
|
|
|
|
|
|
|
$e = $e->emit('foo', 123); |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Emit event. |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head2 has_subscribers |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
my $bool = $e->has_subscribers('foo'); |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
Check if event has subscribers. |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head2 on |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
my $cb = $e->on(foo => sub {...}); |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
Subscribe to event. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
$e->on(foo => sub { |
170
|
|
|
|
|
|
|
my ($e, @args) = @_; |
171
|
|
|
|
|
|
|
... |
172
|
|
|
|
|
|
|
}); |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=head2 once |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
my $cb = $e->once(foo => sub {...}); |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Subscribe to event and unsubscribe again after it has been emitted once. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
$e->once(foo => sub { |
181
|
|
|
|
|
|
|
my ($e, @args) = @_; |
182
|
|
|
|
|
|
|
... |
183
|
|
|
|
|
|
|
}); |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=head2 once_f |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
my $f = $e->once_f('foo'); |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
Subscribe to event as in L"once">, returning a L that will be marked |
190
|
|
|
|
|
|
|
complete after it has been emitted once. Requires L to be installed. |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
my $f = $e->once_f('foo')->on_done(sub { |
193
|
|
|
|
|
|
|
my ($e, @args) = @_; |
194
|
|
|
|
|
|
|
... |
195
|
|
|
|
|
|
|
}); |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
To unsubscribe the returned L early, cancel it. |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
$f->cancel; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head2 subscribers |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
my $subscribers = $e->subscribers('foo'); |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
All subscribers for event. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# Unsubscribe last subscriber |
208
|
|
|
|
|
|
|
$e->unsubscribe(foo => $e->subscribers('foo')->[-1]); |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# Change order of subscribers |
211
|
|
|
|
|
|
|
@{$e->subscribers('foo')} = reverse @{$e->subscribers('foo')}; |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=head2 unsubscribe |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
$e = $e->unsubscribe('foo'); |
216
|
|
|
|
|
|
|
$e = $e->unsubscribe(foo => $cb); |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
Unsubscribe from event. Related Futures will also be cancelled. |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=head1 DEBUGGING |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
You can set the C environment variable to get some |
223
|
|
|
|
|
|
|
advanced diagnostics information printed to C. |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
ROLE_EVENTEMITTER_DEBUG=1 |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=head1 BUGS |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Report any issues on the public bugtracker. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=head1 AUTHOR |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
Dan Book |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Code and tests adapted from L, an event emitter base class |
236
|
|
|
|
|
|
|
by the L team. |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Copyright (c) 2008-2015 Sebastian Riedel. |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
Copyright (c) 2015 Dan Book for adaptation to a role and further changes. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
This is free software, licensed under: |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
The Artistic License 2.0 (GPL Compatible) |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=head1 SEE ALSO |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
L, L, L, |
251
|
|
|
|
|
|
|
L |