File Coverage

blib/lib/Module/List.pm
Criterion Covered Total %
statement 74 95 77.8
branch 27 42 64.2
condition 18 27 66.6
subroutine 8 8 100.0
pod 1 1 100.0
total 128 173 73.9


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Module::List - module `directory' listing
4              
5             =head1 SYNOPSIS
6              
7             use Module::List qw(list_modules);
8              
9             $id_modules = list_modules("Data::ID::", { list_modules => 1});
10             $prefixes = list_modules("",
11             { list_prefixes => 1, recurse => 1 });
12              
13             =head1 DESCRIPTION
14              
15             This module deals with the examination of the namespace of Perl modules.
16             The contents of the module namespace is split across several physical
17             directory trees, but this module hides that detail, providing instead
18             a view of the abstract namespace.
19              
20             =cut
21              
22             package Module::List;
23              
24 2     2   56348 { use 5.006; }
  2         8  
25 2     2   13 use warnings;
  2         3  
  2         61  
26 2     2   10 use strict;
  2         7  
  2         44  
27              
28 2     2   8 use Carp qw(croak);
  2         2  
  2         99  
29 2     2   10 use File::Spec;
  2         6  
  2         63  
30 2     2   521 use IO::Dir 1.03;
  2         37583  
  2         168  
31              
32             our $VERSION = "0.004";
33              
34 2     2   716 use parent "Exporter";
  2         563  
  2         11  
35             our @EXPORT_OK = qw(list_modules);
36              
37             =head1 FUNCTIONS
38              
39             =over
40              
41             =item list_modules(PREFIX, OPTIONS)
42              
43             This function generates a listing of the contents of part of the module
44             namespace. The part of the namespace under the module name prefix PREFIX
45             is examined, and information about it returned as specified by OPTIONS.
46              
47             Module names are handled by this function in standard bareword syntax.
48             They are always fully-qualified; isolated name components are never used.
49             A module name prefix is the part of a module name that comes before
50             a component of the name, and so either ends with "::" or is the empty
51             string.
52              
53             OPTIONS is a reference to a hash, the elements of which specify what is
54             to be returned. The options are:
55              
56             =over
57              
58             =item list_modules
59              
60             Truth value, default false. If true, return names of modules in the relevant
61             part of the namespace.
62              
63             =item list_prefixes
64              
65             Truth value, default false. If true, return module name prefixes in the
66             relevant part of the namespace. Note that prefixes are returned if the
67             corresponding directory exists, even if there is nothing in it.
68              
69             =item list_pod
70              
71             Truth value, default false. If true, return names of POD documentation
72             files that are in the module namespace.
73              
74             =item trivial_syntax
75              
76             Truth value, default false. If false, only valid bareword names are
77             permitted. If true, bareword syntax is ignored, and any "::"-separated
78             name that can be turned into a correct filename by interpreting name
79             components as filename components is permitted. This is of no use in
80             listing actual Perl modules, because the illegal names can't be used in
81             Perl, but some programs such as B<perldoc> use a "::"-separated name for
82             the sake of appearance without really using bareword syntax. The loosened
83             syntax applies both to the names returned and to the I<PREFIX> parameter.
84              
85             Precisely, the `trivial syntax' is that each "::"-separated component
86             cannot be "." or "..", cannot contain "::" or "/", and (except for the
87             final component of a leaf name) cannot end with ":". This is precisely
88             what is required to achieve a unique interconvertible "::"-separated path
89             syntax on Unix. This criterion might change in the future on non-Unix
90             systems, where the filename syntax differs.
91              
92             =item recurse
93              
94             Truth value, default false. If false, only names at the next level down
95             from PREFIX (having one more component) are returned. If true, names
96             at all lower levels are returned.
97              
98             =item use_pod_dir
99              
100             Truth value, default false. If false, POD documentation files are
101             expected to be in the same directory that the corresponding module file
102             would be in. If true, POD files may also be in a subdirectory of that
103             named "C<pod>". (Any POD files in such a subdirectory will therefore be
104             visible under two module names, one treating the "C<pod>" subdirectory
105             level as part of the module name.)
106              
107             =item return_path
108              
109             Truth value, default false. If false, only the existence of requested
110             items is reported. If true, the pathnames of the files in which they
111             exist are reported.
112              
113             =back
114              
115             Note that the default behaviour, if an empty options hash is supplied, is
116             to return nothing. You I<must> specify what kind of information you want.
117              
118             The function returns a reference to a hash, the keys of which are the
119             names of interest. By default, the value associated with each of these
120             keys is undef. If additional information about each item was requested,
121             the value for each item is a reference to a hash, containing some subset
122             of these items:
123              
124             =over
125              
126             =item module_path
127              
128             Pathname of the module of this name. Specifically, this identifies
129             the file that would be read in order to load the module. This may be
130             a C<.pmc> file if one is available. Absent if there is no module.
131              
132             =item pod_path
133              
134             Pathname of the POD document of this name. Absent if there is no
135             discrete POD document. (POD in a module file doesn't constitute a
136             discrete POD document.)
137              
138             =item prefix_paths
139              
140             Reference to an array of the pathnames of the directories referenced
141             by this prefix. The directories are listed in the order corresponding
142             to @INC. Absent if this is not a prefix.
143              
144             =back
145              
146             =cut
147              
148             sub list_modules($$) {
149 8     8 1 26304 my($prefix, $options) = @_;
150 8         27 my $trivial_syntax = $options->{trivial_syntax};
151 8         31 my($root_leaf_rx, $root_notleaf_rx);
152 8         0 my($notroot_leaf_rx, $notroot_notleaf_rx);
153 8 50       31 if($trivial_syntax) {
154 0         0 $root_leaf_rx = $notroot_leaf_rx = qr#:?(?:[^/:]+:)*[^/:]+:?#;
155 0         0 $root_notleaf_rx = $notroot_notleaf_rx = qr#:?(?:[^/:]+:)*[^/:]+#;
156             } else {
157 8         50 $root_leaf_rx = $root_notleaf_rx = qr/[a-zA-Z_][0-9a-zA-Z_]*/;
158 8         35 $notroot_leaf_rx = $notroot_notleaf_rx = qr/[0-9a-zA-Z_]+/;
159             }
160 8 50 33     172 croak "bad module name prefix `$prefix'"
161             unless $prefix =~ /\A(?:${root_notleaf_rx}::
162             (?:${notroot_notleaf_rx}::)*)?\z/x &&
163             $prefix !~ /(?:\A|[^:]::)\.\.?::/;
164 8         29 my $list_modules = $options->{list_modules};
165 8         19 my $list_prefixes = $options->{list_prefixes};
166 8         20 my $list_pod = $options->{list_pod};
167 8         19 my $use_pod_dir = $options->{use_pod_dir};
168 8 100 100     47 return {} unless $list_modules || $list_prefixes || $list_pod;
      66        
169 6         17 my $recurse = $options->{recurse};
170 6         16 my $return_path = $options->{return_path};
171 6         16 my @prefixes = ($prefix);
172 6         43 my %seen_prefixes;
173             my %results;
174 6         23 while(@prefixes) {
175 6         16 my $prefix = pop(@prefixes);
176 6         26 my @dir_suffix = split(/::/, $prefix);
177 6 100       29 my $module_rx = $prefix eq "" ? $root_leaf_rx : $notroot_leaf_rx;
178 6         102 my $pmc_rx = qr/\A($module_rx)\.pmc\z/;
179 6         74 my $pm_rx = qr/\A($module_rx)\.pm\z/;
180 6         83 my $pod_rx = qr/\A($module_rx)\.pod\z/;
181 6 100       26 my $dir_rx = $prefix eq "" ? $root_notleaf_rx : $notroot_notleaf_rx;
182 6         67 $dir_rx = qr/\A$dir_rx\z/;
183 6         23 foreach my $incdir (@INC) {
184 72         2844 my $dir = File::Spec->catdir($incdir, @dir_suffix);
185 72 100       376 my $dh = IO::Dir->new($dir) or next;
186 44         3353 my @entries = $dh->read;
187 44         2616 $dh->close;
188 44 100       678 if($list_modules) {
189 32         86 foreach my $pmish_rx ($pmc_rx, $pm_rx) {
190 64         135 foreach my $entry (@entries) {
191 1624 100       5865 if($entry =~ $pmish_rx) {
192 236         659 my $name = $prefix.$1;
193 236 100       464 if($return_path) {
194 12         161 my $path = File::Spec->catfile($dir, $entry);
195 12   100     94 $results{$name} ||= {};
196             $results{$name}->{module_path} = $path
197             unless
198 12 100       66 exists($results{$name}->{module_path});
199             } else {
200 224         617 $results{$name} = undef;
201             }
202             }
203             }
204             }
205             }
206 44 50       119 if($list_pod) {
207 0         0 my @poddirs = [ $dir, \@entries ];
208 0 0       0 if($use_pod_dir) {
209 0         0 my $pdir = File::Spec->catdir($dir, "pod");
210 0         0 my $pdh = IO::Dir->new($pdir);
211 0 0       0 if($pdh) {
212 0         0 push @poddirs, [ $pdir, [$pdh->read] ];
213 0         0 $pdh->close;
214             }
215             }
216 0         0 foreach(@poddirs) {
217 0         0 my($dir, $entries) = @$_;
218 0         0 foreach my $entry (@$entries) {
219 0 0       0 if($entry =~ $pod_rx) {
220 0         0 my $name = $prefix.$1;
221 0 0       0 if($return_path) {
222 0         0 my $path = File::Spec->catfile($dir, $entry);
223 0   0     0 $results{$name} ||= {};
224             $results{$name}->{pod_path} = $path
225 0 0       0 unless exists($results{$name}->{pod_path});
226             } else {
227 0         0 $results{$name} = undef;
228             }
229             }
230             }
231             }
232             }
233 44 100 66     203 if($list_prefixes || $recurse) {
234 32         76 foreach my $entry (@entries) {
235 812 100 100     17889 if(File::Spec->no_upwards($entry) && $entry =~ $dir_rx &&
      100        
236             -d File::Spec->catdir($dir, $entry)) {
237 430         1654 my $newpfx = $prefix.$entry."::";
238 430 50 33     1164 if($recurse && !exists($seen_prefixes{$newpfx})) {
239 0         0 push @prefixes, $newpfx;
240 0         0 $seen_prefixes{$newpfx} = undef;
241             }
242 430 50       980 if($list_prefixes) {
243 430 100       996 if($return_path) {
244 5   50     48 $results{$newpfx} ||= { prefix_paths => [] };
245 5         11 push @{$results{$newpfx}->{prefix_paths}},
  5         76  
246             File::Spec->catfile($dir, $entry);
247             } else {
248 425         1883 $results{$newpfx} = undef;
249             }
250             }
251             }
252             }
253             }
254             }
255             }
256 6         360 return \%results;
257             }
258              
259             =back
260              
261             =head1 SEE ALSO
262              
263             L<Module::Runtime>
264              
265             =head1 AUTHOR
266              
267             Andrew Main (Zefram) <zefram@fysh.org>
268              
269             =head1 COPYRIGHT
270              
271             Copyright (C) 2004, 2006, 2009, 2011, 2017
272             Andrew Main (Zefram) <zefram@fysh.org>
273              
274             =head1 LICENSE
275              
276             This module is free software; you can redistribute it and/or modify it
277             under the same terms as Perl itself.
278              
279             =cut
280              
281             1;