File Coverage

blib/lib/App/Module/Lister.pm
Criterion Covered Total %
statement 13 60 21.6
branch 1 16 6.2
condition 0 5 0.0
subroutine 5 13 38.4
pod 5 5 100.0
total 24 99 24.2


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