File Coverage

blib/lib/Module/Util.pm
Criterion Covered Total %
statement 84 84 100.0
branch 33 34 97.0
condition 2 3 66.6
subroutine 22 22 100.0
pod 12 12 100.0
total 153 155 98.7


line stmt bran cond sub pod time code
1             package Module::Util;
2              
3 2     2   77201 use strict;
  2         4  
  2         119  
4 2     2   12 use warnings;
  2         4  
  2         112  
5              
6             our $VERSION = '1.09';
7              
8             =encoding UTF-8
9              
10             =head1 NAME
11              
12             Module::Util - Module name tools and transformations
13              
14             =head1 SYNOPSIS
15              
16             use Module::Util qw( :all );
17              
18             $valid = is_valid_module_name $potential_module;
19              
20             $relative_path = module_path $module_name;
21              
22             $file_system_path = module_fs_path $module_name;
23              
24             # load module at runtime
25             require module_path $module_name;
26              
27             # (see perldoc -f require for limitations of this approach.)
28              
29             =head1 DESCRIPTION
30              
31             This module provides a few useful functions for manipulating module names. Its
32             main aim is to centralise some of the functions commonly used by modules that
33             manipulate other modules in some way, like converting module names to relative
34             paths.
35              
36             =cut
37              
38 2     2   10 use Exporter;
  2         10  
  2         122  
39 2     2   12 use File::Spec::Functions qw( catfile rel2abs abs2rel splitpath splitdir );
  2         3  
  2         155  
40 2     2   11 use File::Find;
  2         3  
  2         1553  
41              
42             =head1 EXPORTS
43              
44             Nothing by default.
45              
46             Use the tag :all to import all functions.
47              
48             =head1 FUNCTIONS
49              
50             =cut
51              
52             our @ISA = qw( Exporter );
53             our @EXPORT = ();
54             our @EXPORT_OK = qw(
55             is_valid_module_name
56             module_is_loaded
57             find_installed
58             all_installed
59             find_in_namespace
60             module_path
61             module_fs_path
62             module_path_parts
63             path_to_module
64             fs_path_to_module
65             canonical_module_name
66             module_name_parts
67             );
68              
69             our %EXPORT_TAGS = (
70             all => [ @EXPORT_OK ]
71             );
72              
73             my $SEPARATOR = qr/ :: | ' /x;
74              
75             # leading underscores are technically valid as module names
76             # but no CPAN module has one.
77             our $module_re = qr/[[:alpha:]_] \w* (?: $SEPARATOR \w+ )*/xo;
78              
79             =head2 is_valid_module_name
80              
81             $bool = is_valid_module_name($module)
82              
83             Returns true if $module looks like a module name, false otherwise.
84              
85             =cut
86              
87             sub is_valid_module_name ($) {
88 44     44 1 870 my $module = shift;
89              
90 44         525 return $module =~ /\A $module_re \z/xo;
91             }
92              
93             =head2 module_is_loaded
94              
95             $abs_path_or_hook = module_is_loaded($module)
96              
97             Returns the %INC entry for the given module. This is usually the absolute path
98             of the module, but sometimes it is the hook object that loaded it.
99              
100             See perldoc -f require
101              
102             Equivalent to:
103              
104             $INC{module_path($module)};
105              
106             Except that invalid module names simply return false without generating
107             warnings.
108              
109             =cut
110              
111             sub module_is_loaded ($) {
112 2     2 1 320 my $module = shift;
113              
114 2 100       7 my $path = module_path($module) or return;
115              
116 1         7 return $INC{$path};
117             }
118              
119             =head2 find_installed
120              
121             $path = find_installed($module, [@inc])
122              
123             Returns the first found installed location of the given module. This is always
124             an absolute filesystem path, even if it is derived from a relative path in the
125             include list.
126              
127             By default, @INC is searched, but this can be overridden by providing extra
128             arguments.
129              
130             # look in @INC
131             $path = find_installed("Module::Util")
132              
133             # look only in lib and blib/lib, not in @INC
134             $path = find_installed("Module::Util", 'lib', 'blib/lib')
135              
136             Note that this will ignore any references in the search path, so it doesn't
137             necessarily follow that the module cannot be successfully Cd if this
138             returns nothing.
139              
140             =cut
141              
142             sub find_installed ($;@) {
143 8     8 1 15 my $module = shift;
144 8 100       49 my @inc = @_ ? @_ : @INC;
145              
146 8         20 for my $path (_abs_paths($module, @inc)) {
147 5 100       295 return $path if -e $path;
148             }
149              
150 6         35 return;
151             }
152              
153             =head2 all_installed
154              
155             @paths = all_installed($module, [@inc])
156              
157             Like find_installed, but will return multiple results if the module is installed
158             in multiple locations.
159              
160             =cut
161              
162             sub all_installed ($;@) {
163 2     2 1 5 my $module = shift;
164 2 100       12 my @inc = @_ ? @_ : @INC;
165              
166 2         12 return grep { -e } _abs_paths($module, @inc);
  1         69  
167             }
168              
169             =head2 find_in_namespace
170              
171             @modules = find_in_namespace($namespace, [ @inc ])
172              
173             Searches for modules under a given namespace in the search path (@INC by
174             default).
175              
176             find_in_namespace("My::Namespace");
177              
178             Returns unique installed module names under the namespace. Note that this does
179             not include the passed-in name, even if it is the name of an installed module.
180              
181             Use of an empty string as the namespace returns all modules in @inc.
182              
183             =cut
184              
185             sub find_in_namespace ($;@) {
186 7     7 1 3090 my $ns = shift;
187 7 100       35 my @inc = @_ ? @_ : @INC;
188 7         8 my (@out, $ns_path);
189              
190 7 100       15 if ($ns ne '') {
191 5 100       11 $ns_path = module_fs_path($ns) or return;
192 1         5 $ns_path =~ s/\.pm\z//;
193             }
194             else {
195 2         4 $ns_path = '';
196             }
197              
198 3         6 for my $root (@inc) {
199 3         10 my $ns_root = rel2abs($ns_path, $root);
200              
201 3         97 for my $path (_find_modules($ns_root)) {
202 3         11 my $rel_path = abs2rel($path, rel2abs($root));
203 3         303 push @out, fs_path_to_module($rel_path);
204             }
205             }
206              
207 3         7 my %seen;
208 3         5 return grep { !$seen{$_}++ } @out;
  3         16  
209             }
210              
211             sub _find_modules {
212 3     3   7 my @roots = @_;
213              
214             # versions of File::Find from earlier perls don't have this feature
215 2 50   2   1551 BEGIN { unimport warnings qw( File::Find ) if $] >= 5.008 }
216              
217 3         5 my @out;
218             File::Find::find({
219             no_chdir => 1,
220 7 100 66 7   549 wanted => sub { push @out, $_ if -f $_ && /\.pm\z/ }
221 3         396 }, @roots);
222              
223 3         18 return @out;
224             }
225              
226             # munge a module name into multiple possible installed locations
227             sub _abs_paths {
228 10     10   32 my ($module, @inc) = @_;
229              
230 10 100       23 my $path = module_fs_path($module) or return;
231              
232             return
233 26         449 map { rel2abs($path, $_) }
  26         41  
234 4         9 grep { !ref }
235             @inc;
236             }
237              
238             =head2 module_path
239              
240             $path = module_path($module)
241              
242             Returns a relative path in the form used in %INC. Which I am led to believe is
243             always a unix file path, regardless of the platform.
244              
245             If the argument is not a valid module name, nothing is returned.
246              
247             =cut
248              
249             sub module_path ($) {
250 8     8 1 14 my $module = shift;
251              
252 8 100       14 my @parts = module_path_parts($module) or return;
253              
254 2         14 return join('/', @parts);
255             }
256              
257             =head2 module_fs_path
258              
259             $path = module_fs_path($module)
260              
261             Like module_path, but returns the path in the native filesystem format.
262              
263             On unix systems, this should be identical to module_path.
264              
265             =cut
266              
267             sub module_fs_path ($) {
268 21     21 1 341 my $module = shift;
269              
270 21 100       37 my @parts = module_path_parts($module) or return;
271              
272 7         65 return catfile(@parts);
273             }
274              
275             =head2 path_to_module
276              
277             $module = path_to_module($path)
278              
279             Transforms a relative unix file path into a module name.
280              
281             # Print loaded modules as module names instead of paths:
282             print join("\n", map { path_to_module($_) } keys %INC
283              
284             Returns undef if the resulting module name is not valid.
285              
286             =cut
287              
288             sub path_to_module {
289 3     3 1 7 my $path = shift;
290              
291 3         16 return _join_parts(split('/', $path));
292             }
293              
294             =head2 fs_path_to_module
295              
296             $module = fs_path_to_module($fs_path)
297              
298             Transforms relative filesystem paths into module names.
299              
300             # on windows:
301             fs_path_to_module("Module\\Util.pm")
302             # returns Module::Util
303              
304             Returns undef if the resulting module is not valid.
305              
306             =cut
307              
308             sub fs_path_to_module {
309 4     4 1 298 my $path = shift;
310              
311 4         15 my (undef, $dir, $file) = splitpath($path);
312 4         58 my @dirs = grep { length } splitdir($dir);
  8         47  
313              
314 4         9 return _join_parts(@dirs, $file);
315             }
316              
317             # opposite of module_path_parts, keep private
318             sub _join_parts {
319 7     7   19 my @parts = @_;
320 7 100       45 $parts[-1] =~ s/\.pm\z// or return;
321 6         14 my $module = join('::', @parts);
322 6 100       13 return unless is_valid_module_name($module);
323 5         30 return $module;
324             }
325              
326             =head2 module_path_parts
327              
328             @parts = module_path_parts($module_name)
329              
330             Returns the module name split into parts suitable for feeding to
331             File::Spec->catfile.
332              
333             module_path_parts('Module::Util')
334             # returns ('Module', 'Util.pm')
335              
336             If the module name is invalid, nothing is returned.
337              
338             =cut
339              
340             sub module_path_parts ($) {
341 30     30 1 341 my $module = shift;
342              
343 30 100       48 my @parts = module_name_parts($module) or return;
344 10         19 $parts[-1] .= '.pm';
345              
346 10         48 return @parts;
347             }
348              
349             =head2 canonical_module_name
350              
351             $module = canonical_module_name($module);
352              
353             Returns the canonical module name for the given module. This basically consists
354             of eliminating any apostrophe symbols and replacing them with '::'.
355              
356             canonical_module_name("Acme::Don't"); # Acme::Don::t
357              
358             Returns undef if the name is not valid.
359              
360             =cut
361              
362             sub canonical_module_name ($) {
363 32     32 1 40 my $module = shift;
364              
365 32 100       47 return unless is_valid_module_name($module);
366              
367             # $module = _join_parts(module_path_parts($module));
368 12         24 $module =~ s/'/::/g;
369              
370 12         41 return $module;
371             }
372              
373             =head2 module_name_parts
374              
375             @parts = module_name_parts($module);
376              
377             Returns a list of name parts for the given module.
378              
379             module_name_parts('Acme::Example); # ('Acme', 'Example')
380              
381             =cut
382              
383             sub module_name_parts ($) {
384 31     31 1 41 my $module = shift;
385              
386 31 100       48 $module = canonical_module_name($module) or return;
387              
388 11         122 return split($SEPARATOR, $module);
389             }
390              
391             1;
392              
393             __END__