File Coverage

blib/lib/Progress/Any/Output/TermSpin.pm
Criterion Covered Total %
statement 55 87 63.2
branch 9 32 28.1
condition 4 8 50.0
subroutine 10 15 66.6
pod 2 4 50.0
total 80 146 54.7


line stmt bran cond sub pod time code
1             package Progress::Any::Output::TermSpin;
2              
3             our $DATE = '2015-01-28'; # DATE
4             our $VERSION = '0.01'; # VERSION
5              
6 1     1   1205 use 5.010001;
  1         3  
  1         33  
7 1     1   5 use strict;
  1         1  
  1         30  
8 1     1   554 use utf8;
  1         9  
  1         3  
9 1     1   30 use warnings;
  1         2  
  1         34  
10              
11             #use Color::ANSI::Util qw(ansifg ansibg);
12             #use Text::ANSI::Util qw(ta_mbtrunc ta_mbswidth ta_length);
13 1     1   4 use Time::HiRes qw(time);
  1         2  
  1         7  
14             #require Win32::Console::ANSI if $^O =~ /Win/;
15              
16             $|++;
17              
18             our %STYLES = (
19             line => {utf8=>0, chars=>'|/-\\'},
20             bubble => {utf8=>0, chars=>'.oOo'},
21             pie_utf8 => {utf8=>1, chars=>'○◔◑◕●'},
22             );
23              
24             # patch handles
25             my ($ph1, $ph2, $ph3);
26              
27             my $laa_obj;
28             my $printed_log;
29              
30             sub _patch {
31 2     2   2 my $out = shift;
32              
33 2 50       6 return if $ph1;
34 2         425 require Monkey::Patch::Action;
35             $ph1 = Monkey::Patch::Action::patch_package(
36             'Log::Any::Adapter::ScreenColoredLevel', 'hook_after_log', 'replace',
37             sub {
38 0     0   0 my $self = shift;
39 0         0 undef $out->{_lastlen};
40              
41             # don't print newline after each log, do it before instead. so we
42             # can print spinning cursor
43              
44 0         0 $laa_obj = $self;
45 0         0 $printed_log++;
46             }
47 2 50       2529 ) if defined &{"Log::Any::Adapter::ScreenColoredLevel::hook_after_log"};
  2         12  
48              
49             $ph2 = Monkey::Patch::Action::patch_package(
50             'Log::Any::Adapter::ScreenColoredLevel', 'hook_before_log', 'replace',
51             sub {
52 0     0   0 my $self = shift;
53              
54             # clean spinning cursor, if exists
55 0         0 $out->cleanup;
56              
57             # print newline before log (see above comment)
58 0 0       0 return unless $printed_log;
59 0         0 print { $self->{_fh} } "\n";
  0         0  
60              
61 0 0       0 $out->keep_delay_showing if $out->{show_delay};
62             }
63 2 50       2 ) if defined &{"Log::Any::Adapter::ScreenColoredLevel::hook_before_log"};
  2         10  
64              
65             $ph3 = Monkey::Patch::Action::patch_package(
66             'Log::Any::Adapter::ScreenColoredLevel', 'DESTROY', 'add_or_replace',
67             sub {
68 0     0   0 my $self = shift;
69              
70 0 0       0 return unless $printed_log;
71 0         0 print { $self->{_fh} } "\n";
  0         0  
72             }
73 2         14 );
74             }
75              
76             sub _unpatch {
77 1     1   2 undef $ph1;
78 1         15 undef $ph2;
79 1         2 undef $ph3;
80             }
81              
82             sub new {
83 2     2 1 1906 my ($class, %args0) = @_;
84              
85 2         4 my %args;
86              
87 2   50     13 $args{style} = delete($args0{style}) // 'line';
88 2 50       8 $STYLES{$args{style}} or die "Unknown style '$args{style}'";
89              
90 2   100     10 $args{fh} = delete($args0{fh}) // \*STDOUT;
91              
92 2   50     10 $args{speed} = delete($args0{speed}) // 0.2;
93              
94 2         3 $args{show_delay} = delete($args0{show_delay});
95              
96 2 50       5 keys(%args0) and die "Unknown output parameter(s): ".
97             join(", ", keys(%args0));
98              
99 2         7 $args{_last_hide_time} = time();
100              
101 2         6 my $self = bless \%args, $class;
102 2         4 $self->_patch;
103              
104             # XXX hackish
105 2         110 $Progress::Any::output_data{"$self"}{freq} = -$args{speed};
106              
107 2         39 $self;
108             }
109              
110             sub update {
111 2     2 0 2472 my ($self, %args) = @_;
112              
113 2         6 my $now = time();
114              
115             # if there is show_delay, don't display until we've surpassed it
116 2 50       8 if (defined $self->{show_delay}) {
117 0 0       0 return if $now - $self->{show_delay} < $self->{_last_hide_time};
118             }
119              
120             # "erase" previous display
121 2         3 my $ll = $self->{_lastlen};
122 2 50       6 if (defined $self->{_lastlen}) {
123 0         0 print { $self->{fh} } "\b" x $self->{_lastlen};
  0         0  
124 0         0 undef $self->{_lastlen};
125             }
126              
127 2         5 my $chars = $STYLES{$self->{style}}{chars};
128 2 50       6 if (!defined($self->{_char_index})) {
129 2         2 $self->{_char_index} = 0;
130 2         4 $self->{_last_change_char_time} = $now;
131             } else {
132 0 0       0 if (($now - $self->{_last_change_char_time}) > $self->{speed}) {
133 0         0 $self->{_last_change_char_time} = $now;
134 0         0 $self->{_char_index}++;
135 0 0       0 $self->{_char_index} = 0 if $self->{_char_index} >= length($chars);
136             }
137             }
138 2         5 my $char = substr($chars, $self->{_char_index}, 1);
139 2         3 print { $self->{fh} } " ", $char;
  2         81  
140              
141 2         10 $self->{_lastlen} = 2;
142             }
143              
144             sub cleanup {
145 0     0 0 0 my ($self) = @_;
146              
147             # sometimes (e.g. when a subtask's target is undefined) we don't get
148             # state=finished at the end. but we need to cleanup anyway at the end of
149             # app, so this method is provided and will be called by e.g.
150             # Perinci::CmdLine
151              
152 0         0 my $ll = $self->{_lastlen};
153 0 0       0 return unless $ll;
154 0         0 print { $self->{fh} } "\b" x $ll, " " x $ll, "\b" x $ll;
  0         0  
155             }
156              
157             sub keep_delay_showing {
158 0     0 1 0 my $self = shift;
159              
160 0         0 $self->{_last_hide_time} = time();
161             }
162              
163             sub DESTROY {
164 1     1   8 my $self = shift;
165 1         4 $self->_unpatch;
166              
167 1 50       2 return unless $printed_log;
168 0   0       print { $laa_obj->{_fh} // \*STDOUT } "\n";
  0            
169 0           undef $laa_obj;
170             }
171              
172             1;
173             # ABSTRACT: Output progress to terminal as spinning cursor
174              
175             __END__