File Coverage

blib/lib/App/CPAN/Mini/Visit.pm
Criterion Covered Total %
statement 107 118 90.6
branch 37 50 74.0
condition 1 3 33.3
subroutine 20 20 100.0
pod 1 1 100.0
total 166 192 86.4


line stmt bran cond sub pod time code
1 1     1   2283 use 5.006;
  1         4  
  1         48  
2 1     1   7 use strict;
  1         2  
  1         40  
3 1     1   6 use warnings;
  1         2  
  1         84  
4              
5             package App::CPAN::Mini::Visit;
6             # ABSTRACT: explore each distribution in a minicpan repository
7             our $VERSION = '0.008'; # VERSION
8              
9 1     1   1055 use CPAN::Mini 0.572 ();
  1         181572  
  1         35  
10 1     1   12 use Exception::Class::TryCatch 1.12 qw/ try catch /;
  1         22  
  1         81  
11 1     1   6 use File::Basename qw/ basename /;
  1         2  
  1         51  
12 1     1   5 use File::Find qw/ find /;
  1         2  
  1         47  
13 1     1   1114 use File::pushd qw/ tempd /;
  1         2610  
  1         91  
14 1     1   10 use Path::Class qw/ dir file /;
  1         2  
  1         78  
15 1     1   1621 use Getopt::Lucid 0.16 qw/ :all /;
  1         11142  
  1         444  
16 1     1   1713 use Pod::Usage 1.35 qw/ pod2usage /;
  1         102868  
  1         105  
17              
18 1     1   1627 use Archive::Extract 0.28 ();
  1         246589  
  1         1425  
19              
20             my @option_spec = (
21             Switch("help|h"), Switch("version|V"),
22             Switch("quiet|q"), Param( "append|a", qr/(?:^$|(?:^path|dist$))/ )->default(''),
23             Param("e|E"), Param("minicpan|m"),
24             Param("output|o"),
25             );
26              
27             sub run {
28 20     20 1 156382 my ( $self, @args ) = @_;
29              
30             # get command line options
31 20         48 my $opt = try eval { Getopt::Lucid->getopt( \@option_spec, \@args ) };
  20         455  
32 20         24037 for (catch) {
33 0 0       0 if ( $_->isa('Getopt::Lucid::Exception::ARGV') ) {
34 0         0 print "$_\n";
35             # usage stuff
36 0         0 return 1;
37             }
38             else {
39 0         0 die $_;
40             }
41             }
42              
43             # handle "help" and "version" options
44 20 100       636 return _exit_usage() if $opt->get_help;
45 18 100       804 return _exit_version() if $opt->get_version;
46              
47             # Set Archive::Extract globals
48             # if quiet suppress warnings from Archive::Tar, etc.
49 16         501 local $Archive::Extract::DEBUG = 0;
50 16         32 local $Archive::Extract::PREFER_BIN = 1;
51 16 50       94 local $Archive::Extract::WARN = $opt->get_quiet ? 0 : 1;
52              
53             # if -e/-E, then prepend to command
54 16 100       522 if ( $opt->get_e ) {
55 2 50       178 unshift @args, $^X, ( $^X > 5.009 ? '-E' : '-e' ), $opt->get_e;
56             }
57              
58             # locate minicpan directory
59 16 100       631 if ( !$opt->get_minicpan ) {
60 13         697 my %config = CPAN::Mini->read_config;
61 13 100       5332 if ( $config{local} ) {
62 12         124 $opt->merge_defaults( { minicpan => $config{local} } );
63             }
64             }
65              
66             # confirm minicpan directory that looks like minicpan
67 16 100       2576 return _exit_no_minicpan() if !$opt->get_minicpan;
68 15 100       563 return _exit_bad_minicpan( $opt->get_minicpan ) if !-d $opt->get_minicpan;
69              
70 14         811 my $id_dir = dir( $opt->get_minicpan, qw/authors id/ );
71 14 100       3787 return _exit_bad_minicpan( $opt->get_minicpan ) if !-d $id_dir;
72              
73             # process all distribution tarballs in authors/id/...
74 12         695 my $archive_re = qr{\.(?:tar\.(?:bz2|gz|Z)|t(?:gz|bz)|zip|pm\.gz)$}i;
75              
76 12         85 my $minicpan = dir( $opt->get_minicpan )->absolute;
77              
78             # save output by redirecting STDOUT if requested
79 12         2775 my ( $out_fh, $orig_stdout );
80 12 100       77 if ( $opt->get_output ) {
81 1         65 open $out_fh, ">", $opt->get_output;
82 1         168 open $orig_stdout, "<&=STDOUT";
83 1         33 open STDOUT, ">&=" . fileno $out_fh;
84             }
85              
86             find(
87             {
88             no_chdir => 1,
89             follow => 0,
90 204     204   2399 preprocess => sub { my @files = sort @_; return @files },
  204         11883  
91             wanted => sub {
92 408 100   408   242918 return unless /$archive_re/;
93             # run code if program/args given otherwise print name
94 84 100       334 if (@args) {
95 49 50       708 return if $_ =~ /pm\.gz$/io; # not an archive, just a file
96 49         207 my @cmd = @args;
97 49 100       2910 if ( $opt->get_append ) {
98 28 100       2932 if ( $opt->get_append eq 'dist' ) {
99 14         747 my $distname = $_;
100 14         343 my $prefix = dir( $minicpan, qw/authors id/ );
101 14         27767 $distname =~ s{^$prefix[\\/].[\\/]..[\\/]}{};
102 14         1086 push @cmd, $distname;
103             }
104             else {
105 14         567 push @cmd, $_;
106             }
107             }
108 49         5478 _visit( $_, @cmd );
109             }
110             else {
111 35         1109 print "$_\n";
112             }
113             },
114             },
115 12         21263 $minicpan
116             );
117              
118             # restore STDOUT and close output file
119 12 100       745 if ( $opt->get_output ) {
120 1         81 open STDOUT, ">&=" . fileno $orig_stdout;
121 1         8 close $out_fh;
122             }
123              
124 12         2067 return 0; # exit code
125             }
126              
127             sub _exit_no_minicpan {
128 1     1   178 print STDERR << "END_NO_MINICPAN";
129             No minicpan configured.
130              
131             END_NO_MINICPAN
132 1         17 return 1;
133             }
134              
135             sub _exit_bad_minicpan {
136 3     3   249 my ($dir) = @_;
137 3 50       9 die "requires directory argument" unless defined $dir;
138 3         120 print STDERR << "END_BAD_MINICPAN";
139             Directory '$dir' does not appear to be a CPAN repository.
140              
141             END_BAD_MINICPAN
142 3         63 return 1;
143             }
144              
145             sub _exit_usage {
146 2     2   157 my $exe = basename($0);
147 2         83 print STDERR << "END_USAGE";
148             Usage:
149             $exe [OPTIONS] [PROGRAM]
150              
151             $exe [OPTIONS] -- [PROGRAM] [ARGS]
152              
153             Options:
154              
155             --append|-a --append=dist -> append distname after ARGS
156             --append=path -> append tarball path after ARGS
157              
158             -e|-E run next argument via 'perl -E'
159            
160             --help|-h this usage info
161              
162             --minicpan|-m directory of a minicpan (defaults to local minicpan
163             from CPAN::Mini config file)
164              
165             --output|-o file to save output instead of sending to terminal
166              
167             --quiet|-q silence warnings and suppress STDERR from tar
168              
169             --version|-V $exe program version
170              
171             -- indicates the end of options for $exe
172              
173              
174             END_USAGE
175 2         35 return 1;
176             }
177              
178             sub _exit_version {
179 2     2   240 print STDERR basename($0) . ": $VERSION\n";
180 2         41 return 1;
181             }
182              
183             sub _visit {
184 49     49   159 my ( $archive, @cmd_line ) = @_;
185              
186 49         625 my $tempd = tempd;
187              
188 49         43117 my $ae = Archive::Extract->new( archive => $archive );
189              
190 49         48506 my $olderr;
191              
192             # stderr > /dev/null if quiet
193 49 50       219 if ( !$Archive::Extract::WARN ) {
194 0         0 open $olderr, ">&STDERR";
195 0         0 open STDERR, ">", File::Spec->devnull;
196             }
197              
198 49         563 my $extract_ok = $ae->extract;
199              
200             # restore stderr if quiet
201 49 50       4006858 if ( !$Archive::Extract::WARN ) {
202 0         0 open STDERR, ">&", $olderr;
203 0         0 close $olderr;
204             }
205              
206 49 50       371 if ( !$extract_ok ) {
207 0 0       0 warn "Couldn't extract '$archive'\n" if $Archive::Extract::WARN;
208 0         0 return;
209             }
210              
211             # most distributions unpack a single directory that we must enter
212             # but some behave poorly and unpack to the current directory
213 49         1319 my @children = dir()->children;
214 49 50 33     59400 if ( @children == 1 && -d $children[0] ) {
215 49         2824 chdir $children[0];
216             }
217              
218             # execute command
219 49         1560226 my $rc = system(@cmd_line);
220 49 50       1388 if ( $rc == -1 ) {
221 0         0 warn "Error running '@cmd_line': $!\n";
222             }
223              
224 49         12710 return;
225             }
226              
227             1;
228              
229             __END__