File Coverage

blib/lib/App/Prove/Watch.pm
Criterion Covered Total %
statement 64 82 78.0
branch 9 22 40.9
condition 5 11 45.4
subroutine 14 19 73.6
pod 0 4 0.0
total 92 138 66.6


line stmt bran cond sub pod time code
1             package App::Prove::Watch;
2             $App::Prove::Watch::VERSION = '0.2';
3              
4 2     2   102017 use strict;
  2         4  
  2         66  
5 2     2   8 use warnings;
  2         2  
  2         123  
6              
7 2     2   1149 use App::Prove;
  2         73551  
  2         75  
8 2     2   1219 use Filesys::Notify::Simple;
  2         3592  
  2         53  
9 2     2   12 use File::Basename;
  2         2  
  2         143  
10 2     2   10 use Getopt::Long qw(GetOptionsFromArray);
  2         2  
  2         14  
11              
12              
13             =head1 NAME
14              
15             App::Prove::Watch - Run tests whenever changes occur.
16              
17             =head1 VERSION
18              
19             version 0.2
20              
21             =head1 SYNOPSIS
22              
23             $ provewatcher
24              
25             =head1 DESCRIPTION
26              
27             Watches for changes in the current directroy tree and runs prove when there are
28             changes.
29              
30             =head1 ARGUMENTS
31              
32             C takes all the arguments that C takes with two additions:
33              
34             =head2 --watch
35              
36             Specifies what directories should be watched:
37              
38             # just watch lib
39             $ provewatcher --watch lib
40            
41             # watch lib and t
42             $ provewatcher --watch lib --watch t
43            
44             This defaults to C<.> if not given.
45              
46             =head2 --run
47              
48             Allows you to run something other than prove when changes happen. For example if
49             you where using L
50              
51             $ provewatcher --run 'dzil test'
52            
53             =head1 NOTIFICATIONS
54              
55             If you install L, desktop notifications will
56             be sent whenever the overall state of the tests change (failing to passing or
57             passing to failing).
58              
59             L is not listed as a prereq for this module,
60             it will not be installed by default when you install this module.
61              
62             =cut
63              
64             sub new {
65 2     2 0 1868 my $class = shift;
66 2         6 my ($args, $prove_args) = $class->_split_args(@_);
67            
68              
69 2         13 my $watcher = Filesys::Notify::Simple->new($args->{watch});
70 2         397 my $prove = $class->_get_prove_sub($args, $prove_args);
71              
72 2         11 return bless {
73             watcher => $watcher,
74             prove => $prove,
75             }, $class;
76             }
77              
78 2     2 0 8 sub prove { return $_[0]->{prove}->() };
79 0     0 0 0 sub watcher { return $_[0]->{watcher} };
80              
81             sub run {
82 1     1 0 4 my ($self, $count) = @_;
83            
84 1         3 $self->prove;
85              
86 1   50     4 $count ||= -1;
87 1         3 while ($count != 0) {
88             $self->watcher->wait(sub {
89 1     1   30 my $doit;
90 1         3 foreach my $event (@_) {
91 1         46 my $file = basename($event->{path});
92 1 50       6 next if $file =~ m/^(?:\.~)/;
93            
94 1         3 $doit++;
95            
96             }
97            
98 1 50       2 if ($doit) {
99 1         2 $self->prove();
100 1         20 $count--;
101             }
102 1         7 });
103             }
104             }
105              
106              
107             sub _split_args {
108 2     2   4 my ($class, @args) = @_;
109            
110 2         3 my (@ours, @theirs);
111            
112 2         6 while (@args) {
113 1         5 local $_ = shift @args;
114 1 50 33     7 if ($_ eq '--watch' || $_ eq '--run') {
115 1         3 push(@ours, $_, shift @args);
116             }
117             else {
118 0         0 push(@theirs, $_);
119             }
120             }
121            
122 2         3 my %ours;
123 2         8 GetOptionsFromArray(\@ours, \%ours,
124             'watch=s@',
125             'run=s',
126             );
127            
128 2 50 33     433 if (!$ours{watch} || !@{$ours{watch}}) {
  0         0  
129 2         6 $ours{watch} = ['.']
130             }
131            
132 2         11 return (\%ours, \@theirs);
133             }
134              
135             sub _get_prove_sub {
136 2     2   2 my ($class, $args, $prove_args) = @_;
137            
138 2         5 my $handle_alert = $class->_get_notification_sub;
139            
140 2         3 my $last;
141             my $prove;
142            
143 2 100       5 if ($args->{run}) {
144 1 50       3 if (ref $args->{run}) {
145 1         10 $prove = $args->{run};
146             }
147             else {
148             $prove = sub {
149 0     0   0 my $ret = system($args->{run});
150            
151 0 0       0 return $ret == 0 ? 1 : 0;
152 0         0 };
153             }
154             }
155             else {
156             $prove = sub {
157 0     0   0 my $app = App::Prove->new;
158            
159 0         0 $app->process_args(@$prove_args);
160            
161 0 0       0 return $app->run ? 1 : 0;
162 1         4 };
163             }
164            
165             return sub {
166 2     2   4 my $ret = $prove->();
167            
168 2 50 66     432 if (defined $last && $ret != $last) {
169 0         0 my $msg;
170 0 0       0 if ($ret) {
171 0         0 $msg = "Tests are now passing.";
172             }
173             else {
174 0         0 $msg = "Tests are now failing.";
175             }
176            
177 0         0 $handle_alert->($msg);
178             }
179 2         4 $last = $ret;
180            
181 2         2 return $ret;
182 2         7 };
183             }
184              
185              
186             sub _get_notification_sub {
187 2     2   3 my $has_desk_note = eval {
188 2         265 require Log::Dispatch::DesktopNotification;
189             };
190            
191 2 50       7 if ($has_desk_note) {
192 0         0 my $notify = Log::Dispatch::DesktopNotification->new(
193             name => 'notify',
194             min_level => 'notice',
195             app_name => 'provewatcher',
196             );
197            
198             return sub {
199 0     0   0 $notify->log(
200             level => 'notice',
201             message => shift,
202             );
203             }
204 0         0 }
205             else {
206 2     0   8 return sub {};
  0            
207             }
208             }
209              
210             =head1 TODO
211              
212             =over 2
213              
214             =item *
215              
216             Ironically, for a TDD tool, there's not enough tests.
217              
218             =back
219              
220             =head1 AUTHORS
221              
222             Chris Reinhardt
223             crein@cpan.org
224            
225             =head1 COPYRIGHT
226              
227             This program is free software; you can redistribute
228             it and/or modify it under the same terms as Perl itself.
229              
230             The full text of the license can be found in the
231             LICENSE file included with this module.
232              
233             =head1 SEE ALSO
234              
235             L, L, perl(1)
236              
237             =cut
238            
239              
240             1;