File Coverage

lib/AnyEvent/Callback.pm
Criterion Covered Total %
statement 103 114 90.3
branch 25 38 65.7
condition 8 15 53.3
subroutine 24 27 88.8
pod 2 3 66.6
total 162 197 82.2


line stmt bran cond sub pod time code
1             package AnyEvent::Callback;
2              
3 2     2   84934 use 5.010001;
  2         11  
  2         681  
4 2     2   16 use strict;
  2         4  
  2         91  
5 2     2   13 use warnings;
  2         10  
  2         130  
6              
7             require Exporter;
8 2     2   13 use base 'Exporter';
  2         4  
  2         313  
9 2     2   14 use Carp;
  2         3  
  2         1028  
10              
11             our @EXPORT = qw(CB CBS);
12              
13             our $VERSION = '0.05';
14              
15              
16             =head1 NAME
17              
18             AnyEvent::Callback - callback aggregator for L watchers.
19              
20             =head1 SYNOPSIS
21              
22             use AnyEvent::Callback;
23              
24              
25             # usually watchers are looked as:
26             AE::something @args, sub { ... };
27             AE::something
28             @args,
29             sub { ... }, # result
30             sub { ... }; # error
31              
32              
33             use AnyEvent::Callback;
34              
35             AE::something @args, CB { ... };
36             AE::something @args,
37             CB sub { ... }, # result
38             sub { ... }; # error
39              
40             Inside Your callback You can:
41              
42             sub my_watcher {
43             my $cb = pop;
44             my @args = @_;
45              
46             # ...
47              
48             $cb->error( @error ); # error callback will be called
49             # or:
50             $cb->( $value ); # result callback will be called
51             }
52              
53              
54             Callbacks stack
55              
56             my $cbs = CBS;
57              
58             for (1 .. $n) {
59             AE::something @args, $cbs->cb;
60             }
61              
62             $cbs->wait(sub {
63             for (@_) {
64             if ($_->is_error) { # handle one error
65             my @err = $_->errors; # or:
66             my $errstr = $_->errstr;
67             } else { # results
68             my @res = $_->results;
69             }
70             }
71              
72             });
73              
74             =head1 DESCRIPTION
75              
76             The module allows You to create callback's hierarchy. Also the module groups
77             error and result callbacks into one object.
78              
79             Also the module checks if one callback was called by watcher or not.
80             If a watcher doesn't call result or error callback, error callback will be
81             called automatically.
82              
83             Also the module checks if a callback was called reentrant. In the case the
84             module will complain (using L).
85              
86             If a watcher touches error callback and if superior didn't define error
87             callback, the module will call error callback upwards hierarchy. Example:
88              
89             AE::something @args, CB \&my_watcher, \&on_error;
90              
91             sub on_error {
92              
93             }
94              
95             sub my_watcher {
96             my $cb = pop;
97              
98             ...
99              
100             the_other_watcher $cb->CB( sub { # error callback wasn't defined
101             my $cb = pop;
102             ...
103             yet_another_watcher1 $cb->CB( sub {
104             my $cb = pop;
105             ...
106             $cb->( 123 ); # upwards callback
107              
108             });
109             yet_another_watcher2 $cb->CB( sub {
110             my $cb = pop;
111             ...
112              
113             $cb->error( 456 ); # on_error will be called
114              
115             });
116             });
117             }
118              
119              
120             =head1 METHODS
121              
122             =head2 'CODE' (overloaded fake method)
123              
124             $cb->( ... );
125              
126             You can use the object as usually B.
127              
128             =cut
129              
130             use overload
131             '&{}' => sub {
132 15     15   5586 my ($self) = shift;
133             sub {
134 15     15   92682 $self->{called}++;
135 15 50       78 carp "Repeated callback calling: $self->{called}"
136             if $self->{called} > 1;
137 15 100       503 carp "Calling result callback after error callback"
138             if $self->{ecalled};
139 15 100       112 $self->{cb}->(@_) if $self->{cb};
140 15         112 delete $self->{cb};
141 15         65 delete $self->{ecb};
142 15         32 delete $self->{parent};
143 15         79 return;
144 15         79 };
145             },
146 4     4   12 bool => sub { 1 } # for 'if ($cb)'
147 2     2   5882 ;
  2         3058  
  2         32  
148              
149              
150             =head2 CB
151              
152             Creates new callback object that have binding on parent callback.
153              
154             my $new_cb = $cb->CB(sub { ... }); # the cb doesn't catch errors
155              
156             my $new_cb = CB(sub { ... }, sub { ... }); # the cb catches errors
157              
158             my $new_cb = $cb->CB(sub { ... }, sub { ... }); # the same
159              
160             =cut
161              
162             sub CB(&;&) {
163              
164 19     19 1 4516 my $parent;
165 19         27 my ($cb, $ecb) = @_;
166              
167 19 100       58 ($parent, $cb, $ecb) = @_ unless 'CODE' eq ref $cb;
168              
169 19 50       42 croak 'Callback must be CODEREF' unless 'CODE' eq ref $cb;
170 19 50 66     181 croak 'Error callback must be CODEREF or undef'
171             unless 'CODE' eq ref $ecb or !defined $ecb;
172              
173             # don't translate erorrs upwards if error callback if exists
174 19 100       36 $parent = undef if $ecb;
175              
176 19         91 my $self = bless {
177             cb => $cb,
178             ecb => $ecb,
179             parent => $parent,
180             called => 0,
181             ecalled => 0,
182             } => __PACKAGE__;
183              
184 19         42 $self;
185             }
186              
187             sub CBS {
188 2     2 0 6788 return AnyEvent::Callback::Stack->new;
189             }
190              
191              
192             =head2 error
193              
194             Calls error callback. If the object has no registered error callbacks,
195             parent object's error callback will be called.
196              
197             $cb->error('WTF?');
198              
199             =cut
200              
201             sub error {
202 8     8 1 1046 my ($self, @error) = @_;
203              
204 8         18 $self->{ecalled}++;
205 8 50       25 carp "Repeated error callback calling: $self->{ecalled}"
206             if $self->{ecalled} > 1;
207 8 100       385 carp "Calling error callback after result callback"
208             if $self->{called};
209              
210 8 100       106 if ($self->{ecb}) {
211 4         16 $self->{ecb}( @error );
212 4         26 delete $self->{ecb};
213 4         16 delete $self->{cb};
214 4         7 delete $self->{parent};
215 4         9 return;
216             }
217              
218 4         9 delete $self->{ecb};
219 4         14 delete $self->{cb};
220 4         9 my $parent = delete $self->{parent};
221              
222 4 100       16 unless($parent) {
223 2         189 carp "Uncaught error: @error";
224 2         51 return;
225             }
226              
227 2         10 $parent->error( @error );
228 2         4 return;
229             }
230              
231              
232             sub DESTROY {
233 19     19   11172 my ($self) = @_;
234 19 100 100     296 return if $self->{called} or $self->{ecalled};
235 2         6 $self->error("no one touched registered callback");
236 2         3 delete $self->{cb};
237 2         10 delete $self->{ecb};
238             }
239              
240              
241             package AnyEvent::Callback::Stack;
242 2     2   1573 use Scalar::Util 'weaken';
  2         5  
  2         359  
243 2     2   30 use Carp;
  2         5  
  2         2330  
244              
245             sub new {
246 2     2   4 my ($class) = @_;
247 2   33     23 return bless { stack => [], done => 0 } => ref($class) || $class;
248             }
249              
250             sub cb {
251 12     12   147 my ($self) = @_;
252 12         12 my $idx = @{ $self->{stack} };
  12         16  
253             my $cb = AnyEvent::Callback::CB
254             sub {
255 11     11   90 $self->{stack}[$idx] = AnyEvent::Callback::Stack::Result->new(@_);
256 11         34 $self->{done}++;
257 11         38 $self->_check_if_done;
258             },
259             sub {
260 1     1   8 $self->{stack}[$idx] = AnyEvent::Callback::Stack::Result->err(@_);
261 1         2 $self->{done}++;
262 1         2 $self->_check_if_done;
263             }
264 12         66 ;
265 12         12 push @{ $self->{stack} } => $cb;
  12         19  
266 12         45 weaken $self->{stack}[$idx];
267 12         89 return $self->{stack}[$idx];
268             }
269              
270              
271             sub _check_if_done {
272 14     14   26 my ($self) = @_;
273 14 50       45 return unless $self->{waiter};
274 14 100       26 return unless $self->{done} >= @{ $self->{stack} };
  14         60  
275 2         9 my $cb = delete $self->{waiter};
276 2         4 $cb->(@{ $self->{stack} });
  2         14  
277 2         116 $self->{stack} = [];
278 2         16 $self->{done} = 0;
279             }
280              
281             sub wait :method {
282 2     2   16 my ($self, $cb) = @_;
283 2 50       7 croak 'Usage: $cbs->wait(sub { ... })' unless 'CODE' eq ref $cb;
284 2 50       13 croak 'You have already initiated wait process' if $self->{waiter};
285 2         5 $self->{waiter} = $cb;
286 2         6 $self->_check_if_done;
287             }
288              
289             package AnyEvent::Callback::Stack::Result;
290              
291             sub new {
292 11     11   63 my ($class, @res) = @_;
293 11   33     201 return bless { res => \@res } => ref($class) || $class;
294             }
295              
296             sub err {
297 1     1   2 my ($class, @res) = @_;
298 1   33     9 return bless { err => \@res, res => [] } => ref($class) || $class;
299             }
300              
301             sub is_error {
302 12     12   23425 my ($self) = @_;
303 12         93 return exists $self->{err};
304             }
305              
306             sub results {
307 0     0     my ($self) = @_;
308 0 0         return $self->{res} unless wantarray;
309 0           return @{ $self->{res} };
  0            
310             }
311              
312             sub errors {
313 0     0     my ($self) = @_;
314 0 0         return unless $self->is_error;
315 0 0         return $self->{err} unless wantarray;
316 0           return @{ $self->{err} };
  0            
317             }
318              
319             sub errstr {
320 0     0     my ($self) = @_;
321 0           return join ' ' => $self->errors;
322             }
323              
324             =head1 COPYRIGHT AND LICENCE
325              
326             Copyright (C) 2012 by Dmitry E. Oboukhov
327              
328             This library is free software; you can redistribute it and/or modify
329             it under the same terms as Perl itself.
330              
331             =cut
332              
333             1;