File Coverage

blib/lib/PPI/App/ppi_version/BDFOY.pm
Criterion Covered Total %
statement 58 126 46.0
branch 19 60 31.6
condition 0 9 0.0
subroutine 14 23 60.8
pod 10 10 100.0
total 101 228 44.3


line stmt bran cond sub pod time code
1             package PPI::App::ppi_version::BDFOY;
2 2     2   2590 use parent qw(PPI::App::ppi_version);
  2         586  
  2         12  
3              
4             =encoding utf8
5              
6             =head1 NAME
7              
8             PPI::App::ppi_version::BDFOY - 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 show
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 2     2   277981 use 5.008;
  2         8  
37 2     2   12 use strict;
  2         4  
  2         61  
38 2     2   12 use version;
  2         4  
  2         17  
39 2     2   151 use File::Spec ();
  2         4  
  2         30  
40 2     2   11 use PPI::Document ();
  2         4  
  2         24  
41 2     2   9 use File::Find::Rule ();
  2         5  
  2         25  
42 2     2   8 use File::Find::Rule::Perl ();
  2         4  
  2         29  
43 2     2   1524 use Term::ANSIColor;
  2         18127  
  2         388  
44              
45             our $VERSION = '1.001';
46              
47             #####################################################################
48             # Main Methods
49              
50             =item main
51              
52             =cut
53              
54 0         0 BEGIN {
55 2     2   9 my %commands = map { $_, 1 } qw( show change );
  4         2412  
56              
57             sub main {
58 0     0 1 0 my( $class, @args ) = @_;
59              
60 0         0 my $command = do {
61 2     2   22 no warnings 'uninitialized';
  2         4  
  2         261  
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 $class->print_info(
89             colored( ['green'], $version ),
90             " $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 ) = $class->get_version( $file );
139 0         0 $class->print_file_report( $file, $version, $message, $error_flag );
140 0 0       0 $count++ if defined $version;
141             }
142              
143 0         0 $class->print_info( "Found $count versions" );
144             }
145              
146             =item get_version
147              
148             =cut
149              
150             sub get_version {
151 2     2 1 9106 my( $class, $file ) = @_;
152              
153 2         28 my $Document = PPI::Document->new( $file );
154              
155 2 50       60853 return ( undef, " failed to parse file", 1 ) unless $Document;
156              
157             # Does the document contain a simple version number
158             my $elements = $Document->find( sub {
159             # Find a $VERSION symbol
160 500 100   500   6081 $_[1]->isa('PPI::Token::Symbol') or return '';
161 30 100       61 $_[1]->content =~ m/^\$(?:\w+::)*VERSION$/ or return '';
162              
163             # It is the first thing in the statement
164 2 100       59 if( my $sib = $_[1]->sprevious_sibling ) {
165 1 50       85 return 1 if $sib->content eq 'our';
166 0         0 return '';
167             }
168              
169             # Followed by an "equals"
170 1 50       39 my $equals = $_[1]->snext_sibling or return '';
171 1 50       34 $equals->isa('PPI::Token::Operator') or return '';
172 1 50       4 $equals->content eq '=' or return '';
173              
174             # Followed by a quote
175 1 50       9 my $quote = $equals->snext_sibling or return '';
176 1 50       30 $quote->isa('PPI::Token::Quote') or return '';
177              
178             # ... which is EITHER the end of the statement
179 1 50       24 my $next = $quote->snext_sibling or return 1;
180              
181             # ... or is a statement terminator
182 1 50       28 $next->isa('PPI::Token::Structure') or return '';
183 1 50       3 $next->content eq ';' or return '';
184              
185 1         8 return 1;
186 2         32 } );
187              
188 2 50       60 return ( undef, "no version", 0 ) unless $elements;
189              
190 2 50       24 if ( @$elements > 1 ) {
191 0         0 $class->error("$file contains more than one \$VERSION = 'something';");
192             }
193              
194 2         6 my $element = $elements->[0];
195 2         21 my $version = $element->snext_sibling->snext_sibling;
196 2         141 my $version_string = $version->string;
197              
198 2 50       24 $class->error("Failed to get version string")
199             unless defined $version_string;
200              
201 2         24 return ( $version_string, undef, undef );
202             }
203              
204             =item change
205              
206             =cut
207              
208             sub change {
209 0     0 1   my $class = shift;
210              
211 0           my $from = shift @_;
212              
213 0 0 0       unless ( $from and $from =~ /^v?[\d\._]+$/ ) {
214 0           $class->error("From is not a version [$from]");
215             }
216              
217 0           my $to = shift @_;
218 0 0 0       unless ( $to and $to =~ /^v?[\d\._]+$/ ) {
219 0           $class->error("Target is not a version [$to]");
220             }
221              
222             # Find all modules and scripts below the current directory
223 0           my $files = $class->get_file_list;
224              
225 0           my $count = 0;
226 0           foreach my $file ( @$files ) {
227 0 0         if ( ! -w $file ) {
228 0           $class->print_info( colored ['bold red'], " no write permission" );
229 0           next;
230             }
231              
232 0           my $rv = $class->changefile( $file, $from, $to );
233              
234 0 0         if ( $rv ) {
    0          
235 0           $class->print_info(
236             colored( ['cyan'], $from ),
237             " -> ",
238             colored( ['bold green'], $to ),
239             " $file"
240             );
241 0           $count++;
242             }
243             elsif ( defined $rv ) {
244 0           $class->print_info( colored( ['red'], " skipped" ), " $file" );
245             }
246             else {
247 0           $class->print_info( colored( ['red'], " failed to parse" ), " $file" );
248             }
249             }
250              
251 0           $class->print_info( "Updated " . scalar($count) . " file(s)" );
252 0           $class->print_info( "Done." );
253 0           return 0;
254             }
255              
256             =item changefile
257              
258             =cut
259              
260             sub changefile {
261 0     0 1   my( $self, $file, $from, $to ) = @_;
262              
263 0           my $document = eval { PPI::Document->new($file) };
  0            
264 0 0         unless( $document ) {
265 0           error( "Could not parse $file!" );
266 0           return '';
267             }
268              
269 0           my $rv = PPI::App::ppi_version::_change_document( $document, $from => $to );
270              
271 0 0         error("$file contains more than one \$VERSION assignment") unless defined $rv;
272              
273 0 0         return '' unless $rv;
274              
275 0 0         error("PPI::Document save failed") unless $document->save($file);
276              
277 0           return 1;
278             }
279              
280             =item error
281              
282             =cut
283              
284             sub error {
285 2     2   18 no warnings 'uninitialized';
  2         6  
  2         218  
286 0     0 1   print "\n", colored ['red'], " $_[1]\n\n";
287 0           return 255;
288             }
289              
290             1;
291              
292             =back
293              
294             =head1 SOURCE AVAILABILITY
295              
296             This source is part of a Github project:
297              
298             https://github.com/briandfoy/ppi-app-ppi_version-bdfoy
299              
300             =head1 AUTHOR
301              
302             Adam Kennedy wrote the original, and I stole some of the code. I even
303             inherit from the original.
304              
305             brian d foy, C<< >>
306              
307             =head1 COPYRIGHT
308              
309             Copyright © 2008-2021, brian d foy . All rights reserved.
310              
311             You may redistribute this under the same terms as the Artistic License 2.0.
312              
313             =cut