File Coverage

blib/lib/AnyEvent/Retry.pm
Criterion Covered Total %
statement 30 83 36.1
branch 1 20 5.0
condition 0 3 0.0
subroutine 10 23 43.4
pod 3 9 33.3
total 44 138 31.8


line stmt bran cond sub pod time code
1             package AnyEvent::Retry;
2             BEGIN {
3 3     3   96560 $AnyEvent::Retry::VERSION = '0.03';
4             }
5             # ABSTRACT: try something until it works
6 3     3   9280 use Moose;
  3         1819075  
  3         28  
7 3     3   28527 use MooseX::Types::Common::Numeric qw(PositiveNum);
  3         374127  
  3         38  
8 3     3   11063 use AnyEvent::Retry::Types qw(Interval);
  3         9765  
  3         25  
9              
10 3     3   9077 use AnyEvent;
  3         19946  
  3         142  
11 3     3   35 use Try::Tiny;
  3         9  
  3         350  
12 3     3   23 use Scalar::Util qw(weaken);
  3         8  
  3         175  
13              
14 3     3   20 use true;
  3         7  
  3         33  
15 3     3   3668 use namespace::autoclean;
  3         7  
  3         36  
16              
17             has 'after' => (
18             is => 'ro',
19             isa => PositiveNum,
20             default => 0,
21             );
22              
23             has 'interval' => (
24             is => 'ro',
25             isa => Interval,
26             required => 1,
27             coerce => 1,
28             );
29              
30             has '_sent_result' => (
31             accessor => '_sent_result',
32             isa => 'Bool',
33             default => 0,
34             );
35              
36             has [qw/try on_failure on_success/] => (
37             is => 'ro',
38             isa => 'CodeRef',
39             required => 1,
40             );
41              
42             has 'max_tries' => (
43             is => 'ro',
44             isa => PositiveNum,
45             default => 0,
46             );
47              
48             has 'autostart' => (
49             is => 'ro',
50             isa => 'Bool',
51             default => 0,
52             );
53              
54             has '_timer' => (
55             init_arg => undef,
56             writer => '_set_timer',
57             clearer => 'kill_timer',
58             predicate => 'has_timer',
59             );
60              
61             sub BUILD {
62 0     0 0 0 my $self = shift;
63 0 0       0 $self->start if $self->autostart;
64             }
65              
66             sub DEMOLISH {
67 3     3 0 11 my $self = shift;
68 3         143 $self->kill_timer;
69              
70 3 50       128 if(!$self->_sent_result){
71 3         119 $self->_sent_result(1);
72 3         121 $self->on_failure->(demolish => 'DEMOLISH');
73             }
74             }
75              
76             # set a timer to call handle_tick in the future
77             sub set_timer {
78 0     0 0   my ($self, $time, $i) = @_;
79 0 0         return $self->handle_tick($i) if $time <= 0;
80              
81 0           weaken $self;
82             $self->_set_timer(
83             AnyEvent->timer( after => $time, cb => sub {
84 0     0     $self->kill_timer;
85 0           $self->handle_tick($i);
86 0           }),
87             );
88              
89 0           return;
90             }
91              
92             # called when the timer ticks; start the user's code running
93             sub handle_tick {
94 0     0 0   my ($self, $this_i) = @_;
95 0           $self->run_code;
96             }
97              
98             # called when the user's code signals success or error
99             sub handle_result {
100 0     0 0   my ($self, $success, $status, $msg) = @_;
101              
102             # if we hit these two cases, we are done forever
103 0 0         if($success){
    0          
104 0           $self->_sent_result(1);
105 0           $self->on_success->($msg);
106 0           return;
107             }
108             elsif($status =~ /error/){
109 0           $self->_sent_result(1);
110 0           $self->on_failure->( exception => $msg, $status );
111 0           return;
112             }
113              
114             # no error, but not success (try again later)
115 0           my ($next_time, $next_i) = $self->interval->next;
116 0 0 0       if($self->max_tries > 0 && $next_i > $self->max_tries){
117             # done forever
118 0           $self->_sent_result(1);
119 0           $self->on_failure->( max_tries => $self->max_tries );
120 0           return;
121             }
122              
123             # we didn't get the result this time, and we haven't exceeded
124             # $max_tries, so set the timer and do the whole thing again
125 0           $self->set_timer( $next_time, $next_i );
126 0           return;
127             }
128              
129             # start the user's code running, with a continuation-passing-style
130             # callback to call when the result is ready
131             sub run_code {
132 0     0 0   my ($self) = @_;
133              
134             # we weaken $self here so that if the user does "undef $retry", we
135             # DEMOLISH the object and silently discard the results of the
136             # running code. feel free to subclass if want to keep the class
137             # alive arbitrarily.
138 0           weaken $self;
139              
140             my $success = sub {
141 0     0     my $result = shift;
142 0 0         return unless defined $self;
143 0 0         $self->handle_result(($result ? 1 : 0), 'success', $result);
144 0           return;
145 0           };
146              
147             my $error = sub {
148 0     0     my $msg = shift;
149 0 0         return unless defined $self;
150 0           $self->handle_result(0, 'run error', $msg);
151 0           return;
152 0           };
153              
154 0     0     try { $self->try->($success, $error) }
155 0     0     catch { $self->handle_result(0, 'startup error', $_) };
  0            
156 0           return;
157             }
158              
159             # if the timer is running, stop it until resume is called
160             sub pause {
161 0     0 1   my $self = shift;
162 0           $self->kill_timer;
163             }
164              
165             # fake a timer tick; run the user code, and set the timer to retry if
166             # necessary
167             sub resume {
168 0     0 1   my $self = shift;
169 0           $self->kill_timer; # just in case
170 0           $self->handle_tick(0);
171             }
172              
173             # start the process. if the timer is running, die. if the timer is
174             # not running, start completely over.
175             sub start {
176 0     0 1   my $self = shift;
177 0 0         confess 'the job is already running' if $self->has_timer;
178              
179 0           $self->interval->reset;
180 0           $self->_sent_result(0);
181 0           $self->set_timer( $self->after, 0 );
182 0           return;
183             }
184              
185             __PACKAGE__->meta->make_immutable;
186              
187              
188              
189             =pod
190              
191             =head1 NAME
192              
193             AnyEvent::Retry - try something until it works
194              
195             =head1 VERSION
196              
197             version 0.03
198              
199             =head1 SYNOPSIS
200              
201             This module lets you retry a non-blocking task at timed intervals
202             until it succeeds.
203              
204             If you work for Aperture Science, something like this might be good:
205              
206             my $r = AnyEvent::Retry->new(
207             on_failure => sub {
208             my ($error_type, $error_message) = @_;
209             $condvar->croak($error_message);
210             },
211             on_success => sub {
212             my ($result) = @_;
213             $condvar->send($result);
214             },
215             max_tries => 100, # eventually give up
216             interval => { Constant => { interval => 1 } }, # try every second
217             try => {
218             my ($success, $error) = @_;
219             $error->('out of cake!') if $cake-- < 0;
220             do_science( on_success => $success, on_error => $error );
221             },
222              
223             );
224              
225             $r->start; # keep on trying until you run out of cake
226             my $neat_gun = $condvar->recv;
227              
228             Now, as long as you have cake, you will keep doing science (every
229             second). When your science results in the creation of a neat gun,
230             $neat_gun will contain it. If there's an error, C<< $condvar->recv >>
231             will die.
232              
233             This sort of thing is also good for networking or sysadmin tasks; poll
234             the mail server until you get an email message, poll a webserver until
235             the super-hot tickets go on sale (and then buy them), etc.
236              
237             =head1 METHODS
238              
239             =head2 new({INITARGS})
240              
241             Create a new, un-C<start>-ed retry-er object. If you C<undef> this object,
242             your job is cancelled and your C<on_failure> callback is notified.
243              
244             See the INITARGS section below for information on what params to pass.
245              
246             =head2 start
247              
248             Start the job. Dies if the job is already running.
249              
250             (You can call this again when the job is done to run the job again.)
251              
252             =head2 pause
253              
254             Stop the timer, pausing the job until C<resume> is called.
255              
256             =head2 resume
257              
258             Resume the task as though the last-running timer just expired.
259              
260             =head1 INITARGS
261              
262             =head2 try
263              
264             Required. This is the coderef to run repeatedly. It is passed two
265             coderefs as args, C<success_cb> and C<error_cb>. Your coderef must
266             call one of those; success with a true value if the process is
267             complete and should not run again, success with a false value if the
268             process should run again, or error with an error message if the
269             process failed (and will not run again).
270              
271             This is "continuation passing style". It's necessary so that your
272             C<try> block can kick off asynchronous jobs.
273              
274             =head2 on_failure
275              
276             Required. Callback to call when the job fails. Called a maximum of one time.
277              
278             When called, it will be called with two args; the type of error, and
279             the error message.
280              
281             The type of error can be C<max_tries>, C<exception>, or C<demolish>.
282              
283             Note that if C<on_failure> is called, it's guaranteed that
284             C<on_success> will never be called.
285              
286             =head2 on_success
287              
288             Required. Called when your job succeeds. Called a maximum of one
289             time.
290              
291             When called, it will be called with one arg; the value your try block
292             code passed to the C<success_cb>.
293              
294             Note that if C<on_success> is called, it's guaranteed that
295             C<on_failure> will never be called.
296              
297             =head2 max_tries
298              
299             Optional. The maximum number of times to run your job before
300             considering it failed.
301              
302             If it's set to 0, then your job will be run an infinite number of
303             times, subject to the continued existence of the Universe.
304              
305             Defaults to 0.
306              
307             =head2 autostart
308              
309             Optional. Boolean. Defaults to 0.
310              
311             If set to 1, the job will start as soon as the constructor is
312             finished. You need not call C<start>.
313              
314             =head2 interval
315              
316             Required. Controls how long to wait between retries. It must be a
317             blessed Moose object that does the L<AnyEvent::Retry::Interval> role.
318              
319             Some existing interval classes are L<AnyEvent::Retry::Constant>,
320             L<AnyEvent::Retry::Fibonacci>, and L<AnyEvent::Retry::Multi>.
321              
322             This attribute has a coercion from strings and hashrefs. If you pass
323             a string, it will be treated as a class name (under
324             C<AnyEvent::Retry::Interval::>, unless it is prefxed with a C<+>) to
325             instantiate.
326              
327             If you pass a hashref, the first key will be treated as a class name
328             as above, and the value of that key will be treated as the args to
329             pass to C<new>.
330              
331             =head1 AUTHOR
332              
333             Jonathan Rockway <jrockway@cpan.org>
334              
335             =head1 COPYRIGHT AND LICENSE
336              
337             This software is copyright (c) 2010 by Jonathan Rockway.
338              
339             This is free software; you can redistribute it and/or modify it under
340             the same terms as the Perl 5 programming language system itself.
341              
342             =cut
343              
344              
345             __END__
346