File Coverage

blib/lib/App/Module/Lister.pm
Criterion Covered Total %
statement 19 66 28.7
branch 1 16 6.2
condition 0 5 0.0
subroutine 7 15 46.6
pod 5 5 100.0
total 32 107 29.9


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2             package App::Module::Lister;
3 1     1   714 use strict;
  1         2  
  1         28  
4              
5 1     1   521 use subs qw();
  1         26  
  1         24  
6 1     1   5 use vars qw($VERSION);
  1         1  
  1         65  
7              
8             $VERSION = '0.153';
9              
10             =encoding utf8
11              
12             =head1 NAME
13              
14             App::Module::Lister - List the Perl modules in @INC
15              
16             =head1 SYNOPSIS
17              
18             # run the .pm file
19             prompt> perl Lister.pm
20              
21             ---OR---
22             # rename this file to something your webserver will treat as a
23             # CGI script and upload it. Run it to see the module list
24             prompt> cp Lister.pm lister.cgi
25             ... modify the shebang line if you must
26             prompt> ftp www.example.com
27             ... upload file
28             prompt> wget http://www.example.com/cgi-bin/lister.cgi
29              
30              
31             =head1 DESCRIPTION
32              
33             This is a program to list all of the Perl modules it finds in C<@INC>
34             for a no-shell web hosting account. It has these explicit design goals:
35              
36             =over 4
37              
38             =item * Is a single file FTP upload such that it's ready to run (no archives)
39              
40             =item * Runs as a CGI script
41              
42             =item * Runs on a standard Perl 5.004 system with no non-core modules
43              
44             =item * Does not use CPAN.pm (which can't easly be configured without the shell)
45              
46             =back
47              
48             If you have a shell account, you should just use C's autobundle
49             feature.
50              
51             You do not need to install this module. You just need the C<.pm> file.
52             The rest of the distribution is there to help me give it to other
53             people and test it.
54              
55             You might have to modify the shebang line (the first line in the file)
56             to point to Perl. Your web hoster probably has instructions on what
57             that should be. As shipped, this program uses the C trick described
58             in L. If that doesn't work for you, you'll probably see an
59             error like:
60              
61             /usr/bin/env: bad interpreter: No such file or directory
62              
63             That's similar to the error you'll see if you have the wrong path
64             to C.
65              
66             The program searches each entry in C<@INC> individually and outputs
67             modules as it finds them.
68              
69             =cut
70              
71 1     1   5 use File::Find qw(find);
  1         2  
  1         49  
72 1     1   5 use File::Spec;
  1         1  
  1         214  
73              
74             run(\*STDOUT) unless caller;
75              
76             sub run {
77 0   0 0 1   my $fh = shift || \*STDOUT;
78              
79 0           my( $wanted, $reporter, $clear ) = generator();
80              
81 0           print $fh "This is Perl $]\n";
82              
83 0           foreach my $inc ( @INC ) {
84 0           find( { wanted => $wanted }, $inc );
85              
86 0           my $count = 0;
87 0           foreach my $file ( $reporter->() ) {
88 0           my $version = parse_version_safely( $file );
89              
90 0           my $module_name = path_to_module( $inc, $file );
91              
92 0           print $fh "$module_name\t$version\n";
93              
94             #last if $count++ > 5;
95             }
96              
97 0           $clear->();
98             }
99             }
100              
101             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
102             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
103             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
104              
105             BEGIN {
106 1 50   1   508 print "Content-type: text/plain\n\n" if exists $ENV{REQUEST_METHOD};
107             }
108              
109             =head2 Subroutines
110              
111             =over 4
112              
113             =item run( FILEHANDLE )
114              
115             Do the magic, sending the output to C. By default, it sends
116             the output to C.
117              
118             =item generator
119              
120             Returns three closures to find, report, and clear a list of modules.
121             See their use in C.
122              
123             =cut
124              
125             sub generator {
126 0     0 1   my @files = ();
127              
128 0 0   0     sub { push @files,
129             File::Spec->canonpath( $File::Find::name )
130             if m/\A\w+\.pm\z/ },
131 0     0     sub { @files },
132 0     0     sub { @files = () }
133 0           }
134              
135             =item parse_version_safely( FILENAME )
136              
137             Find the C<$VERSION> in C and return its value. The entire
138             statement in the file must be on a single line with nothing else (just
139             like for the PAUSE indexer). If the version is undefined, it returns the
140             string C<'undef'>.
141              
142             =cut
143              
144             sub parse_version_safely { # stolen from PAUSE's mldistwatch, but refactored
145 0     0 1   my( $file ) = @_;
146              
147 0           local $/ = "\n";
148 0           local $_; # don't mess with the $_ in the map calling this
149              
150 0 0         return unless open FILE, "<$file";
151              
152 0           my $in_pod = 0;
153 0           my $version;
154 0           while( ) {
155 0           chomp;
156 0 0         $in_pod = /^=(?!cut)/ ? 1 : /^=cut/ ? 0 : $in_pod;
    0          
157 0 0 0       next if $in_pod || /^\s*#/;
158              
159 0 0         next unless /([\$*])(([\w\:\']*)\bVERSION)\b.*\=/;
160 0           my( $sigil, $var ) = ( $1, $2 );
161              
162 0           $version = eval_version( $_, $sigil, $var );
163 0           last;
164             }
165 0           close FILE;
166              
167 0 0         return 'undef' unless defined $version;
168              
169 0           return $version;
170             }
171              
172             =item eval_version( STATEMENT, SIGIL, VAR )
173              
174             Used by C to evaluate the C<$VERSION> line
175             and return a number.
176              
177             The C is the single statement containing the assignment
178             to C<$VERSION>.
179              
180             The C may be either a C<$> (for a scalar) or a C<*> for a
181             typeglob.
182              
183             The C is the variable identifier.
184              
185             =cut
186              
187             sub eval_version {
188 0     0 1   my( $line, $sigil, $var ) = @_;
189              
190 0           my $eval = qq{
191             package # hide from PAUSE
192             ExtUtils::MakeMaker::_version;
193              
194             local $sigil$var;
195             \$$var=undef; do {
196             $line
197             }; \$$var
198             };
199              
200 0           my $version = do {
201 0           local $^W = 0;
202 1     1   7 no strict;
  1         2  
  1         234  
203 0           eval( $eval );
204             };
205              
206 0           return $version;
207             }
208              
209             =item path_to_module( INC_DIR, PATH )
210              
211             Turn a C into a Perl module name, ignoring the C<@INC> directory
212             specified in C.
213              
214             =cut
215              
216             sub path_to_module {
217 0     0 1   my( $inc, $path ) = @_;
218              
219 0           my $module_path = substr( $path, length $inc );
220 0           $module_path =~ s/\.pm\z//;
221              
222             # XXX: this is cheating and doesn't handle everything right
223 0           my @dirs = grep { ! /\W/ } File::Spec->splitdir( $module_path );
  0            
224 0           shift @dirs;
225              
226 0           my $module_name = join "::", @dirs;
227              
228 0           return $module_name;
229             }
230              
231             1;
232              
233             =back
234              
235             =head1 TO DO
236              
237             =over 4
238              
239             =item *
240              
241             Guessing the module name from the full path name isn't perfect. If I
242             run into directories that aren't part of the module name in one of the
243             C<@INC> directories, this program shows the wrong thing.
244              
245             For example, I have in C<@INC> the directory
246             C. Inside that directory, I expect to find
247             something like C, which
248             translates in the module C. If I find a directory like
249             C, where I created the
250             extra C by hand, this program guesses the module name is
251             C. That's not a great tradegy, but I don't have a
252             simple way around that right now.
253              
254             =item *
255              
256             This program finds all modules, even those installed in multiple
257             locations. It makes no attempt to figure out which ones Perl will
258             choose first.
259              
260             =back
261              
262             =head1 SEE ALSO
263              
264             The C module
265              
266             =head1 SOURCE AVAILABILITY
267              
268             This source is in GitHub:
269              
270             https://github.com/briandfoy/app-module-lister
271              
272             =head1 AUTHOR
273              
274             brian d foy, C<< >>
275              
276             The idea and some of the testing came from Adam Wohld.
277              
278             Some bits stolen from C in the PAUSE code, by Andreas König.
279              
280             =head1 COPYRIGHT AND LICENSE
281              
282             Copyright © 2007-2018, brian d foy . All rights reserved.
283              
284             You may redistribute this under the terms of the Artistic 2 license.
285              
286             =cut
287              
288             1;