File Coverage

blib/lib/Sub/Attempts.pm
Criterion Covered Total %
statement 56 57 98.2
branch 15 16 93.7
condition 1 3 33.3
subroutine 11 11 100.0
pod 0 1 0.0
total 83 88 94.3


line stmt bran cond sub pod time code
1             package Sub::Attempts;
2              
3 5     5   40508 use strict;
  5         9  
  5         193  
4             #use warnings;
5 5     5   29 use vars qw($VERSION @EXPORT);
  5         9  
  5         334  
6              
7             $VERSION = "1.01";
8              
9 5     5   24 use Exporter;
  5         8  
  5         197  
10 5     5   25 use base qw(Exporter);
  5         8  
  5         792  
11             @EXPORT = qw(attempts);
12 5     5   4355 use Sub::Uplevel;
  5         4882  
  5         28  
13              
14 5     5   181 use Carp;
  5         10  
  5         548  
15              
16             =head1 NAME
17              
18             Sub::Attempts - alter subroutines to try again on exceptions
19              
20             =head1 SYNOPSIS
21              
22             use Sub::Attempts;
23              
24             sub alter_db
25             {
26             my $dbh = DBI->connect("DBD::Mysql:foo", "mark", "opensaysme")
27             or die "Can't connect to database";
28             $dbh->{RaiseException} = 1;
29             $dbh->do("alter table items change pie pies int(10)");
30             }
31              
32             # if there's a problem making pies, wait and try again
33             attempts("alter_db", tries => 3, delay => 2);
34              
35              
36             =head1 DESCRIPTION
37              
38             Sometimes if a subroutine throws an exception the right thing to
39             do is wait for a while and then call the subroutine again, as the
40             error conditions that caused the subroutine to have to throw
41             the exception might have gone away.
42              
43             This module exports one subroutine C which can be used to
44             modifiy existing subroutines so that whenever that subroutine is
45             called it will be automatically be called again in the event that it
46             throws an exception.
47              
48             use LWP::Simple qw(get);
49             sub journal_rss
50             {
51             return get("http://use.perl.org/~2shortplanks/journal/rss")
52             or die "Couldn't get journal";
53             }
54             attempts("journal_rss");
55              
56             By default perl will attempt to run to run the subroutine again
57             without delay if an exception is thrown. If on the second run an
58             exception is again thrown, that exception will be propogated out of
59             the subroutine as normal.
60              
61             The particulars of the subroutines re-execution can be changed by
62             passing extra parameters to C. The C parameter
63             effects the number of times the subroutne will attempt to be executed.
64             The C parameter determines how long perl will wait - sleep -
65             in seconds (and fractions of a second) between execution attempts.
66              
67             =head2 Methods
68              
69             A method can be modified just like any other subroutine, provided the
70             subroutine defining the method is located in the same package as
71             C is called from. If this is not the case (i.e. the method
72             is inherited and not overridden) then you should use the C
73             parameter:
74              
75             attempts("get_pie", tries => 3, method => 1);
76              
77             This has the same effect as writing:
78              
79             sub get_pie
80             {
81             my $self = shift;
82             $self->SUPER::get_pie(@_);
83             }
84              
85             attempts("get_pie", tries => 3);
86              
87             If a method is defined by a subroutine in the current package then
88             the C parameter has no effect
89              
90             =cut
91              
92             sub attempts
93             {
94             # here be subroutine magic
95 5     5   27 no strict 'refs';
  5         10  
  5         1691  
96              
97 4     4 0 120 my $subname = shift;
98 4         38 my %args = @_;
99              
100             # get the ref to the existing subroutine
101 4   33     21 my $package = caller || croak "Not in a package";
102 4         84 my $glob = \*{"${package}::${subname}"};
  4         28  
103 4         8 my $old_sub = *{ $glob }{CODE};
  4         13  
104              
105             # is it a method?
106 4 100       17 if (!defined($old_sub))
107             {
108 1 50       5 if ($args{method})
109             {
110             # this eval is here as we need to switch packages to declare a
111             # subroutine so SUPER works and with the current limitations of
112             # perl, there's no way to do that by mucking about with
113             # typeglobs.
114 1         60 eval qq{package $package;
115             sub $subname
116             {
117             my \$this = shift;
118             \$this->SUPER::$subname(\@_)
119             }
120             };
121 1         3 $old_sub = *{ $glob }{CODE};
  1         4  
122             }
123             else
124             {
125 0         0 croak "Can't wrap '$subname', doesn't exist in package '$package'"
126             }
127             }
128              
129             # replace the subroutine
130 4         25 _attempts($old_sub, $glob, %args);
131             }
132              
133             sub _attempts
134             {
135             # here be subroutine magic too
136 5     5   28 no strict 'refs';
  5         8  
  5         1556  
137              
138 5     5   13 my $old_sub = shift;
139 5         10 my $glob = shift;
140 5         15 my %args = @_;
141              
142             # create a new subroutine that does the attempt stuff
143             my $sub = sub
144             {
145             # find out how many attempts we're going to take,
146             # defaulting to two.
147 14 100   14   7727 my $tries = exists($args{tries}) ? $args{tries} : 2;
148              
149             # do we want a list?
150 14         23 my $wantarray = wantarray;
151              
152             # try while we've got tries left.
153 14         16 while (1)
154             {
155 33         48 my $result;
156             my @results;
157              
158             # try running the code
159             eval
160 33         50 {
161 33 100       91 if ($wantarray)
162 4         11 { @results = uplevel 2, $old_sub, @_ }
163             else
164 29         117 { $result = uplevel 2, $old_sub, @_ }
165             };
166              
167             # return if we're sucessful
168 33 100       2119 return ($wantarray ? @results : $result )
    100          
169             unless $@;
170              
171             # we've used up a try
172 23         46 $tries--;
173 23 100       61 last if $tries < 1;
174              
175             # sleep if we need to
176 19 100       9006185 select undef, undef, undef, $args{delay}
177             if exists $args{delay};
178             }
179              
180             # got this far and didn't already return, so propogate the error
181 4         110 croak $@;
182 5         34 };
183              
184             # place the subroutine into the symbol table
185 5         11 *{ $glob } = $sub;
  5         33  
186             }
187              
188             =head1 AUTHOR
189              
190             Written by Mark Fowler Emark@twoshortplanks.comE
191              
192             Copryright Mark Fowler 2003. All Rights Reserved.
193              
194             This program is free software; you can redistribute it
195             and/or modify it under the same terms as Perl itself.
196              
197             =head1 BUGS
198              
199             Though list and scalar context will be preserved for the call to the
200             original subroutine, other forms of context (as offered by B)
201             will be lost. Therefore, amongst other things, a subroutine modified
202             by C cannot currently 1be used as a lvalue.
203              
204             The caller bug is now defeated, thanks to B things think
205             they're in a higher caller frame.
206              
207             Bugs should be reported to me via the CPAN RT system.
208             L.
209              
210             =head1 SEE ALSO
211              
212             L,
213             L
214              
215             =cut
216              
217             1;