File Coverage

blib/lib/Perinci/Sub/Property/retry.pm
Criterion Covered Total %
statement 56 59 94.9
branch 17 24 70.8
condition 8 13 61.5
subroutine 5 5 100.0
pod n/a
total 86 101 85.1


line stmt bran cond sub pod time code
1             package Perinci::Sub::Property::retry;
2              
3 1     1   43674 use 5.010001;
  1         3  
  1         37  
4 1     1   6 use strict;
  1         2  
  1         34  
5 1     1   7 use warnings;
  1         2  
  1         41  
6              
7 1     1   819 use Perinci::Sub::PropertyUtil qw(declare_property);
  1         881  
  1         773  
8              
9             our $VERSION = '0.08'; # VERSION
10              
11             declare_property(
12             name => 'retry',
13             type => 'function',
14             schema => ['any' => {default=>0, of=>[
15             ['int' => {min=>0, default=>0}],
16             ['hash*' => {keys=>{
17             'n' => ['int' => {min=>0, default=>0}],
18             'delay' => ['int' => {min=>0, default=>0}],
19             'success_statuses' => ['regex' => {default=>'^(2..|304)$'}],
20             'fatal_statuses' => 'regex',
21             'non_fatal_statuses' => 'regex',
22             'fatal_messages' => 'regex',
23             'non_fatal_messages' => 'regex',
24             }}],
25             ]}],
26             wrapper => {
27             meta => {
28             v => 2,
29             # very high, we want to trap errors as early as possible after eval,
30             # so we can retry it.
31             prio => 0,
32             convert => 1,
33             },
34             handler => sub {
35 22     22   2270167 my ($self, %args) = @_;
36              
37 22   33     129 my $v = $args{new} // $args{value};
38 22 100       97 $v = {n=>$v} unless ref($v) eq 'HASH';
39 22   50     85 $v->{n} //= 0;
40 22   100     130 $v->{delay} //= 0;
41 22   66     166 $v->{success_statuses} //= qr/^(2..|304)$/;
42              
43 22         59 for my $k (qw/success_statuses
44             fatal_statuses non_fatal_statuses
45             fatal_messages non_fatal_messages/) {
46 110 50 66     452 if (defined($v->{$k}) && ref($v->{$k}) ne 'Regexp') {
47 0         0 $v->{$k} = qr/$v->{$k}/;
48             }
49             }
50              
51 22 50       93 return unless $v->{n} > 0;
52              
53 22         79 $self->select_section('before_eval');
54 22         286 $self->push_lines(
55             '', 'my $_w_retries = 0;',
56             'RETRY: while (1) {');
57 22         1537 $self->indent;
58              
59             # pass special variable for function to let it know about retries
60 22         1740 $self->select_section('before_call_arg_validation');
61 22         248 my $args_as = $self->{_meta}{args_as};
62 22 50       74 if ($args_as eq 'hash') {
    0          
63 22         68 $self->push_lines('$args{-retries} = $_w_retries;');
64             } elsif ($args_as eq 'hashref') {
65 0         0 $self->push_lines('$args->{-retries} = $_w_retries;');
66             }
67              
68 22         642 $self->select_section('after_eval');
69 22 50       404 if ($self->{_arg}{meta}{result_naked}) {
70 0         0 $self->push_lines('if ($_w_eval_err) {');
71             } else {
72 22         109 $self->push_lines('if ($_w_eval_err || $_w_res->[0] !~ qr/'.
73             $v->{success_statuses}.'/) {');
74             }
75 22         652 $self->indent;
76 22 100       202 if ($v->{fatal_statuses}) {
77 2         12 $self->_errif('521', '"Can\'t retry (fatal status $_w_res->[0])"',
78             '$_w_res->[0] =~ qr/'.$v->{fatal_statuses}.'/');
79             }
80 22 100       174 if ($v->{non_fatal_statuses}) {
81 4         29 $self->_errif(
82             '521', '"Can\'t retry (not non-fatal status $_w_res->[0])"',
83             '$_w_res->[0] !~ qr/'.$v->{non_fatal_statuses}.'/');
84             }
85 22 100       300 if ($v->{fatal_messages}) {
86 2         13 $self->_errif(
87             '521', '"Can\'t retry (fatal message: $_w_res->[1])"',
88             '$_w_res->[1] =~ qr/'.$v->{fatal_messages}.'/');
89             }
90 22 100       172 if ($v->{non_fatal_messages}) {
91 4         26 $self->_errif(
92             '521', '"Can\'t retry (not non-fatal message $_w_res->[1])"',
93             '$_w_res->[1] !~ qr/'.$v->{non_fatal_messages}.'/');
94             }
95 22         352 $self->_errif('521', '"Maximum retries reached"',
96             '++$_w_retries > '.$v->{n});
97 22 100       1296 $self->push_lines('sleep '.int($v->{delay}).';')
98             if $v->{delay};
99 22         92 $self->push_lines('next RETRY;');
100 22         308 $self->unindent;
101 22         282 $self->push_lines('} else {');
102 22         317 $self->indent;
103             # return information on number of retries performed
104 22 50       180 unless ($self->{_meta}{result_naked}) {
105 22         1224 $self->push_lines('if ($_w_retries) {');
106 22         431 $self->push_lines($self->{_args}{indent} . '$_w_res->[3] //= {};');
107 22         527 $self->push_lines($self->{_args}{indent} . '$_w_res->[3]{retries}' .
108             ' = $_w_retries;');
109 22         469 $self->push_lines('}');
110             }
111 22         312 $self->push_lines('last RETRY;');
112 22         367 $self->unindent;
113 22         322 $self->push_lines('}');
114 22         307 $self->unindent;
115 22         174 $self->push_lines('', '# RETRY', '}', '');
116             },
117             },
118             );
119              
120             1;
121             # ABSTRACT: Specify automatic retry
122              
123             __END__