File Coverage

blib/lib/Log/Progress.pm
Criterion Covered Total %
statement 63 77 81.8
branch 34 50 68.0
condition 13 20 65.0
subroutine 16 19 84.2
pod 7 8 87.5
total 133 174 76.4


line stmt bran cond sub pod time code
1             package Log::Progress;
2             $Log::Progress::VERSION = '0.11';
3 6     6   5616 use Moo 2;
  6         26868  
  6         22  
4 6     6   3653 use Carp;
  6         11  
  6         325  
5 6     6   1322 use IO::Handle; # for 'autoflush'
  6         13004  
  6         209  
6 6     6   22 use JSON;
  6         6  
  6         26  
7              
8             # ABSTRACT: Conveniently write progress messages to logger or file handle
9              
10              
11             has to => ( is => 'rw', isa => \&_assert_valid_output, default => sub { \*STDERR },
12             trigger => sub { delete $_[0]{_writer} } );
13             sub squelch {
14 1284     1284 1 860 my $self= shift;
15 1284 50       1725 if (@_) { $self->_squelch(shift); $self->_calc_precision_squelch() }
  0         0  
  0         0  
16 1284         1488 $self->{squelch};
17             }
18             sub precision {
19 1295     1295 1 871 my $self= shift;
20 1295 50       1546 if (@_) { $self->_precision(shift); $self->_calc_precision_squelch() }
  0         0  
  0         0  
21 1295         4261 $self->{precision};
22             }
23             has step_id => ( is => 'rw', default => sub { $ENV{PROGRESS_STEP_ID} },
24             trigger => sub { delete $_[0]{_writer} } );
25              
26             has current => ( is => 'rw' );
27             has total => ( is => 'rw' );
28              
29             has _writer => ( is => 'lazy' );
30             has _squelch => ( is => 'rw', init_arg => 'squelch' );
31             has _precision => ( is => 'rw', init_arg => 'precision' );
32             has _last_progress => ( is => 'rw' );
33              
34             sub BUILD {
35 33     33 0 174 shift->_calc_precision_squelch();
36             }
37              
38             sub _calc_precision_squelch {
39 33     33   29 my $self= shift;
40 33         76 my $squelch= $self->_squelch;
41 33         39 my $precision= $self->_precision;
42 33 100 100     118 if (!defined $squelch && !defined $precision) {
43 16         10 $squelch= .01;
44 16         21 $precision= 2;
45             } else {
46             # calculation for digit length of number of steps
47 17 100       48 defined $precision or $precision= int(log(1/$squelch)/log(10) + .99999);
48 17 100       28 defined $squelch or $squelch= 1/(10**$precision);
49             }
50 33         43 $self->{squelch}= $squelch;
51 33         118 $self->{precision}= $precision;
52             }
53              
54             sub _assert_valid_output {
55 33     33   346 my $to= shift;
56 33         59 my $type= ref $to;
57 33 50 66     775 $type && (
      33        
58             $type eq 'GLOB'
59             or $type eq 'CODE'
60             or $type->can('print')
61             or $type->can('info')
62             ) or die "$to is not a file handle, logger object, or code reference";
63             }
64              
65             sub _build__writer {
66 17     17   90 my $self= shift;
67            
68 17 100       215 my $prefix= "progress: ".(defined $self->step_id? $self->step_id.' ' : '');
69 17         399 my $to= $self->to;
70 17         61 my $type= ref $to;
71 17 100 100     198 $to->autoflush(1) if $type eq 'GLOB' or $type->can('autoflush');
72 2     2   9 return ($type eq 'GLOB')? sub { print $to $prefix.join('', @_)."\n"; }
73 24     24   104 : ($type eq 'CODE')? sub { $to->($prefix.join('', @_)); }
74 118     118   1744 : ($type->can('print'))? sub { $to->print($prefix.join('', @_)."\n"); }
75 1     1   4 : ($type->can('info'))? sub { $to->info($prefix.join('', @_)); }
76 17 50       690 : die "unhandled case";
    100          
    100          
    100          
77             }
78              
79              
80             sub at {
81 1281     1281 1 3412028 my ($self, $current, $total, $message)= @_;
82 1281 100       1594 if (defined $total) {
83 123         584 $self->total($total);
84             } else {
85 1158         955 $total= $self->total;
86 1158 50       1399 $total= 1 unless defined $total;
87             }
88 1281         1388 $self->current($current);
89 1281 50       1531 my $progress= $total? $current / $total : 0;
90 1281         1330 my $sq= $self->squelch;
91 1281         1336 my $formatted= sprintf("%.*f", $self->precision, int($progress/$sq + .0000000001)*$sq);
92 1281 100 100     5761 return if defined $self->_last_progress
93             and abs($formatted - $self->_last_progress)+.0000000001 < $sq;
94 138         419 $self->_last_progress($formatted);
95 138 100       276 if ($total != 1) {
96 101 50       345 $formatted= (int($current) == $current)? "$current/$total"
97             : sprintf("%.*f/%d", $self->precision, $current, $total);
98             }
99 138 100       5081 $self->_writer->($formatted . ($message? " - $message":''));
100             }
101              
102             # backward compatibility with version <= 0.03
103             sub progress {
104 0     0 1 0 my ($self, $current, $total, $message)= @_;
105 0 0       0 $total= 1 unless defined $total;
106 0         0 $self->at($current, $total, $message);
107             }
108              
109              
110             sub inc {
111 0     0 1 0 my ($self, $offset, $message)= @_;
112 0 0       0 $offset= 1 unless defined $offset;
113 0   0     0 $self->at(($self->current || 0) + $offset, undef, $message);
114             }
115              
116              
117             sub data {
118 0     0 1 0 my ($self, $data)= @_;
119 0 0       0 ref $data eq 'HASH' or croak "data must be a hashref";
120 0         0 $self->_writer->(JSON->new->ascii->encode($data));
121             }
122              
123              
124             sub substep {
125 7     7 1 21 my ($self, $step_id, $step_contribution, $title)= @_;
126 7 50       21 length $title or croak "sub-step title is required";
127            
128 7 50 33     101 $step_id= $self->step_id . '.' . $step_id
129             if defined $self->step_id and length $self->step_id;
130            
131 7         131 my $sub_progress= ref($self)->new(
132             to => $self->to,
133             squelch => $self->_squelch,
134             precision => $self->_precision,
135             step_id => $step_id,
136             );
137            
138 7 50       15 if ($step_contribution) {
139 7         15 $sub_progress->_writer->(sprintf("(%.*f) %s", $self->precision, $step_contribution, $title));
140             } else {
141 0         0 $sub_progress->_writer->("- $title");
142             }
143            
144 7         240 return $sub_progress;
145             }
146              
147             1;
148              
149             __END__