File Coverage

blib/lib/Module/Path.pm
Criterion Covered Total %
statement 32 34 94.1
branch 10 16 62.5
condition 2 6 33.3
subroutine 7 7 100.0
pod 0 1 0.0
total 51 64 79.6


line stmt bran cond sub pod time code
1             package Module::Path;
2             # ABSTRACT: get the full path to a locally installed module
3             $Module::Path::VERSION = '0.19';
4 2     2   15801 use 5.006;
  2         6  
  2         63  
5 2     2   8 use strict;
  2         2  
  2         55  
6 2     2   15 use warnings;
  2         2  
  2         57  
7 2     2   8 use File::Basename 'dirname';
  2         3  
  2         169  
8 2     2   8 use Cwd qw/ abs_path /;
  2         2  
  2         507  
9              
10             require Exporter;
11              
12             our @ISA = qw(Exporter);
13             our @EXPORT_OK = qw(module_path);
14              
15             my $SEPARATOR;
16              
17             BEGIN {
18 2 50   2   23 if ($^O =~ /^(dos|os2)/i) {
    50          
19 0         0 $SEPARATOR = '\\';
20             } elsif ($^O =~ /^MacOS/i) {
21 0         0 $SEPARATOR = ':';
22             } else {
23 2         385 $SEPARATOR = '/';
24             }
25             }
26              
27             sub module_path
28             {
29 3     3 0 82 my $module = shift;
30 3         3 my $relpath;
31             my $fullpath;
32              
33 3         8 ($relpath = $module) =~ s/::/$SEPARATOR/g;
34 3 100       12 $relpath .= '.pm' unless $relpath =~ m!\.pm$!;
35              
36             DIRECTORY:
37 3         6 foreach my $dir (@INC) {
38 31 50       44 next DIRECTORY if not defined($dir);
39              
40             # see 'perldoc -f require' on why you might find
41             # a reference in @INC
42 31 50       40 next DIRECTORY if ref($dir);
43              
44 31 50 33     624 next unless -d $dir && -x $dir;
45              
46             # The directory path might have a symlink somewhere in it,
47             # so we get an absolute path (ie resolve any symlinks).
48             # The previous attempt at this only dealt with the case
49             # where the final directory in the path was a symlink,
50             # now we're trying to deal with symlinks anywhere in the path.
51 31         32 my $abs_dir = $dir;
52 31         23 eval { $abs_dir = abs_path($abs_dir); };
  31         963  
53 31 50 33     100 next DIRECTORY if $@ || !defined($abs_dir);
54              
55 31         38 $fullpath = $abs_dir.$SEPARATOR.$relpath;
56 31 100       300 return $fullpath if -f $fullpath;
57             }
58              
59 1         6 return undef;
60             }
61              
62             1;
63              
64             =head1 NAME
65              
66             Module::Path - get the full path to a locally installed module
67              
68             =head1 SYNOPSIS
69              
70             use Module::Path 'module_path';
71            
72             $path = module_path('Test::More');
73             if (defined($path)) {
74             print "Test::More found at $path\n";
75             } else {
76             print "Danger Will Robinson!\n";
77             }
78              
79             =head1 DESCRIPTION
80              
81             This module provides a single function, C,
82             which takes a module name and finds the first directory in your C<@INC> path
83             where the module is installed locally.
84             It returns the full path to that file, resolving any symlinks.
85             It is portable and only depends on core modules.
86              
87             It works by looking in all the directories in C<@INC>
88             for an appropriately named file:
89              
90             =over 4
91              
92             =item
93              
94             Foo::Bar becomes C, using the correct directory path
95             separator for your operating system.
96              
97             =item
98              
99             Iterate over C<@INC>, ignoring any references
100             (see L<"perlfunc"/"require"> if you're surprised to hear
101             that you might find references in C<@INC>).
102              
103             =item
104              
105             For each directory in C<@INC>, append the partial path (C),
106             again using the correct directory path separator.
107             If the resulting file exists, return this path.
108              
109             =item
110              
111             If a directory in C<@INC> is a symlink, then we resolve the path,
112             and return a path containing the linked-to directory.
113              
114             =item
115              
116             If no file was found, return C.
117              
118             =back
119              
120             I wrote this module because I couldn't find an alternative
121             which dealt with the points listed above, and didn't pull in
122             what seemed like too many dependencies to me.
123              
124             The distribution for C includes the C
125             script, which lets you get the path for a module from the command-line:
126              
127             % mpath Module::Path
128              
129             The C function will also cope if the module name includes C<.pm>;
130             this means you can pass a partial path, such as used as the keys in C<%INC>:
131              
132             module_path('Test/More.pm') eq $INC{'Test/More.pm'}
133              
134             The above is the basis for one of the tests.
135              
136             =head1 BUGS
137              
138             Obviously this only works where the module you're after has its own C<.pm>
139             file. If a file defines multiple packages, this won't work.
140              
141             This also won't find any modules that are being loaded in some special
142             way, for example using a code reference in C<@INC>, as described
143             in L<"perlfunc"/"require">.
144              
145              
146             =head1 SEE ALSO
147              
148             There are a number of other modules on CPAN which provide the
149             same or similar functionality:
150             L,
151             L,
152             L,
153             L,
154             L,
155             L,
156             L,
157             L,
158             L,
159             L,
160             L,
161             and L.
162              
163             I've written a review of all such modules that I'm aware of:
164              
165             =over 4
166              
167             L
168              
169             =back
170              
171             Module::Path was written to be fast, portable, and have a low number of
172             core-only runtime dependencies. It you only want to look up the path to
173             a module, it's a good choice.
174              
175             If you want more information, such as the module's version, what functions
176             are provided, etc, then start by looking at L,
177             L, and L.
178              
179             The following scripts can also give you the path:
180             L,
181             L.
182              
183              
184             =head1 REPOSITORY
185              
186             L
187              
188             =head1 AUTHOR
189              
190             Neil Bowers Eneilb@cpan.orgE
191              
192             =head1 COPYRIGHT AND LICENSE
193              
194             This software is copyright (c) 2012 by Neil Bowers .
195              
196             This is free software; you can redistribute it and/or modify it under
197             the same terms as the Perl 5 programming language system itself.
198