File Coverage

blib/lib/PPI/App/ppi_version/BRIANDFOY.pm
Criterion Covered Total %
statement 58 132 43.9
branch 19 64 29.6
condition 0 14 0.0
subroutine 14 23 60.8
pod 10 10 100.0
total 101 243 41.5


line stmt bran cond sub pod time code
1             package PPI::App::ppi_version::BRIANDFOY;
2 3     3   615219 use parent qw(PPI::App::ppi_version);
  3         6  
  3         32  
3              
4             =encoding utf8
5              
6             =head1 NAME
7              
8             PPI::App::ppi_version::BRIANDFOY - brian d foy's rip off of Adam's ppi_version
9              
10             =head1 SYNOPSIS
11              
12             # call it like PPI::App::ppi_version
13             % ppi_version show
14              
15             % ppi_version change 1.23 1.24
16              
17             # call it with less typing. With no arguments, it assumes 'show'.
18             % ppi_version
19              
20             # with arguments that are not 'show' or 'change', assume 'change'
21             % ppi_version 1.23 1.24
22              
23             =head1 DESCRIPTION
24              
25             I like what PPI::App::Version does, mostly, but I had to be different.
26             Life would just be easier if Adam did things my way from the start.
27              
28             =cut
29              
30             =head2 Methods
31              
32             =over 4
33              
34             =cut
35              
36 3     3   847516 use 5.010;
  3         11  
37 3     3   15 use strict;
  3         7  
  3         73  
38 3     3   15 use version;
  3         8  
  3         26  
39 3     3   251 use File::Spec ();
  3         7  
  3         43  
40 3     3   14 use PPI::Document ();
  3         4  
  3         37  
41 3     3   13 use File::Find::Rule ();
  3         5  
  3         36  
42 3     3   13 use File::Find::Rule::Perl ();
  3         7  
  3         45  
43 3     3   1932 use Term::ANSIColor;
  3         29837  
  3         490  
44              
45             our $VERSION = '1.007';
46              
47             #####################################################################
48             # Main Methods
49              
50             =item main
51              
52             =cut
53              
54 0         0 BEGIN {
55 3     3   10 my %commands = map { $_, 1 } qw( show change );
  6         4178  
56              
57             sub main {
58 0     0 1 0 my( $class, @args ) = @_;
59              
60 0         0 my $command = do {
61 3     3   25 no warnings 'uninitialized';
  3         6  
  3         325  
62 0 0       0 if( exists $commands{ $args[0] } ) { shift @args }
  0 0       0  
63 0         0 elsif( @args == 0 ) { 'show' }
64 0         0 else { 'change' }
65             };
66              
67 0         0 $class->$command( @args );
68             }
69             }
70              
71             =item print_my_version
72              
73             =cut
74              
75             sub print_my_version {
76 0     0 1 0 print "brian's ppi_version $VERSION - Copright 2009-2021 brian d foy\n";
77             }
78              
79             =item print_file_report
80              
81             =cut
82              
83             sub print_file_report {
84 0     0 1 0 my $class = shift;
85 0         0 my( $file, $version, $message, $error ) = @_;
86              
87 0 0       0 if( defined $version ) {
    0          
88 0 0       0 $version = $version =~ m/\A
89             $version : colored( ['green'], sprintf '%12s', $version );
90 0         0 $class->print_info( "$version $file" );
91             }
92             elsif( $error ) {
93 0         0 $class->print_info( "$file... ", colored ['red'], $message );
94             }
95             else {
96 0         0 $class->print_info( "$file... ", $message );
97             }
98             }
99              
100             =item print_info
101              
102             =cut
103              
104             sub print_info {
105 0     0 1 0 my $class = shift;
106              
107 0         0 print @_, "\n";
108             }
109              
110             =item get_file_list
111              
112             =cut
113              
114             sub get_file_list {
115 0     0 1 0 my( $class, $dir ) = @_;
116              
117 0   0     0 my @files = grep { ! /\bblib\b/ } File::Find::Rule->perl_file
  0         0  
118             ->in( $dir || File::Spec->curdir );
119              
120 0         0 print "Found " . scalar(@files) . " file(s)\n";
121              
122 0         0 return \@files;
123             }
124              
125             =item show
126              
127             =cut
128              
129             sub show {
130 0     0 1 0 my $class = shift;
131              
132 0         0 my @args = @_;
133              
134 0         0 my $files = $class->get_file_list( $args[0] );
135              
136 0         0 my $count = 0;
137 0         0 foreach my $file ( @$files ) {
138 0         0 my( $version, $message, $error_flag ) = eval { $class->get_version( $file ) };
  0         0  
139 0 0       0 if( $@ ) {
140 0         0 $error_flag = 1;
141 0   0     0 $message //= $@;
142             }
143 0   0     0 $version //= '';
144 0         0 $class->print_file_report( $file, $version, $message, $error_flag );
145 0 0       0 $count++ if defined $version;
146             }
147              
148 0         0 $class->print_info( "Found $count versions" );
149             }
150              
151             =item get_version
152              
153             =cut
154              
155             sub get_version {
156 2     2 1 6869 my( $class, $file ) = @_;
157              
158 2         16 my $Document = PPI::Document->new( $file );
159              
160 2 50       113751 return ( undef, " failed to parse file", 1 ) unless $Document;
161              
162             # Does the document contain a simple version number
163             my $elements = $Document->find( sub {
164             # Find a $VERSION symbol
165 500 100   500   5746 $_[1]->isa('PPI::Token::Symbol') or return '';
166 30 100       73 $_[1]->content =~ m/^\$(?:\w+::)*VERSION$/ or return '';
167              
168             # It is the first thing in the statement
169 2 100       36 if( my $sib = $_[1]->sprevious_sibling ) {
170 1 50       31 return 1 if $sib->content eq 'our';
171 0         0 return '';
172             }
173              
174             # Followed by an "equals"
175 1 50       71 my $equals = $_[1]->snext_sibling or return '';
176 1 50       43 $equals->isa('PPI::Token::Operator') or return '';
177 1 50       19 $equals->content eq '=' or return '';
178              
179             # Followed by a quote
180 1 50       11 my $quote = $equals->snext_sibling or return '';
181 1 50       40 $quote->isa('PPI::Token::Quote') or return '';
182              
183             # ... which is EITHER the end of the statement
184 1 50       22 my $next = $quote->snext_sibling or return 1;
185              
186             # ... or is a statement terminator
187 1 50       32 $next->isa('PPI::Token::Structure') or return '';
188 1 50       7 $next->content eq ';' or return '';
189              
190 1         9 return 1;
191 2         24 } );
192              
193 2 50       46 return ( undef, "no version", 0 ) unless $elements;
194              
195 2 50       8 if ( @$elements > 1 ) {
196 0         0 $class->error("$file contains more than one \$VERSION = 'something';");
197             }
198              
199 2         4 my $element = $elements->[0];
200 2         33 my $version = $element->snext_sibling->snext_sibling;
201 2         101 my $version_string = $version->string;
202              
203 2 50       19 $class->error("Failed to get version string")
204             unless defined $version_string;
205              
206 2         13 return ( $version_string, undef, undef );
207             }
208              
209             =item change
210              
211             =cut
212              
213             sub change {
214 0     0 1   my $class = shift;
215              
216 0           my $from = shift @_;
217              
218 0 0 0       unless ( $from and $from =~ /^v?[\d\._]+$/ ) {
219 0           $class->error("From is not a version [$from]");
220             }
221              
222 0           my $to = shift @_;
223 0 0 0       unless ( $to and $to =~ /^v?[\d\._]+$/ ) {
224 0           $class->error("Target is not a version [$to]");
225             }
226              
227             # Find all modules and scripts below the current directory
228 0           my $files = $class->get_file_list;
229              
230 0           my $count = 0;
231 0           foreach my $file ( @$files ) {
232 0 0         if ( ! -w $file ) {
233 0           $class->print_info( colored ['bold red'], " no write permission" );
234 0           next;
235             }
236              
237 0           my $rv = $class->changefile( $file, $from, $to );
238              
239 0 0         if ( $rv ) {
    0          
240 0           $class->print_info(
241             colored( ['cyan'], $from ),
242             " -> ",
243             colored( ['bold green'], $to ),
244             " $file"
245             );
246 0           $count++;
247             }
248             elsif ( defined $rv ) {
249 0           $class->print_info( colored( ['red'], " skipped" ), " $file" );
250             }
251             else {
252 0           $class->print_info( colored( ['red'], " failed to parse" ), " $file" );
253             }
254             }
255              
256 0           $class->print_info( "Updated " . scalar($count) . " file(s)" );
257 0           $class->print_info( "Done." );
258 0           return 0;
259             }
260              
261             =item changefile
262              
263             =cut
264              
265             sub changefile {
266 0     0 1   my( $self, $file, $from, $to ) = @_;
267              
268 0           my $document = eval { PPI::Document->new($file) };
  0            
269 0 0         unless( $document ) {
270 0           error( "Could not parse $file!" );
271 0           return '';
272             }
273              
274 0           my $rv = PPI::App::ppi_version::_change_document( $document, $from => $to );
275              
276 0 0         error("$file contains more than one \$VERSION assignment") unless defined $rv;
277              
278 0 0         return '' unless $rv;
279              
280 0 0         error("PPI::Document save failed") unless $document->save($file);
281              
282 0           return 1;
283             }
284              
285             =item error
286              
287             =cut
288              
289             sub error {
290 3     3   23 no warnings 'uninitialized';
  3         7  
  3         322  
291 0     0 1   print "\n", colored ['red'], " $_[1]\n\n";
292 0           return 255;
293             }
294              
295             1;
296              
297             =back
298              
299             =head1 SOURCE AVAILABILITY
300              
301             This source is part of a Github project:
302              
303             https://github.com/briandfoy/ppi-app-ppi_version-briandfoy
304              
305             =head1 AUTHOR
306              
307             Adam Kennedy wrote the original, and I stole some of the code. I even
308             inherit from the original.
309              
310             brian d foy, C<< >>
311              
312             =head1 COPYRIGHT
313              
314             Copyright © 2008-2025, brian d foy C. All rights reserved.
315              
316             You may redistribute this under the same terms as the Artistic License 2.0.
317              
318             =cut