File Coverage

blib/lib/Module/Locate.pm
Criterion Covered Total %
statement 70 83 84.3
branch 26 44 59.0
condition 6 11 54.5
subroutine 16 17 94.1
pod 6 7 85.7
total 124 162 76.5


line stmt bran cond sub pod time code
1             {
2             package Module::Locate;
3              
4 4     4   95910 use warnings;
  4         10  
  4         219  
5 4     4   79 use 5.8.8;
  4         106  
  4         3210  
6              
7             our $VERSION = '1.79';
8             our $Cache = 0;
9             our $Global = 1;
10              
11             my $ident_re = qr{[_a-z]\w*}i;
12             my $sep_re = qr{'|::};
13             our $PkgRe = qr{\A(?:$ident_re(?:$sep_re$ident_re)*)\z};
14              
15             my @All = qw(
16             locate get_source acts_like_fh
17             mod_to_path is_mod_loaded is_pkg_loaded
18             );
19              
20             sub import {
21 5     5   1584 my $pkg = caller;
22 5         23 my @args = @_[ 1 .. $#_ ];
23            
24 5         9424 while(local $_ = shift @args) {
25 6 100 50     29 *{ "$pkg\::$_" } = \&$_ and next
  5         1681  
26             if defined &$_;
27              
28 1 50       8 $Cache = shift @args, next
29             if /^cache$/i;
30              
31 0 0       0 $Global = shift @args, next
32             if /^global$/i;
33              
34 0 0       0 if(/^:all$/i) {
35 0         0 *{ "$pkg\::$_" } = \&$_
36 0         0 for @All;
37 0         0 next;
38             }
39              
40 0         0 warn("not in ".__PACKAGE__." import list: '$_'");
41             }
42             }
43              
44 4     4   45 use strict;
  4         17  
  4         522  
45              
46 4     4   5349 use IO::File;
  4         95298  
  4         1428  
47 4     4   3895 use overload ();
  4         2771  
  4         106  
48 4     4   27 use Carp 'croak';
  4         8  
  4         257  
49 4     4   6824 use File::Spec::Functions 'catfile';
  4         12776  
  4         3540  
50            
51             sub get_source {
52 0     0 1 0 my $pkg = $_[-1];
53              
54 0         0 my $f = locate($pkg);
55              
56             my $fh = ( acts_like_fh($f) ?
57             $f
58             :
59 0 0       0 do { my $tmp = IO::File->new($f)
  0 0       0  
60 0         0 or croak("invalid module '$pkg' [$f] - $!"); $tmp }
61             );
62              
63 0         0 local $/;
64 0         0 return <$fh>;
65             }
66            
67             sub locate {
68 13     13 1 21145 my $pkg = $_[-1];
69              
70 13 50       45 croak("Undefined filename provided")
71             unless defined $pkg;
72            
73 13         35 my $inc_path = mod_to_path($pkg);
74              
75 13 50 33     55 return $INC{$inc_path} if exists($INC{$inc_path}) && !wantarray;
76              
77             # On Windows the inc_path will use '/' for directory separator,
78             # but when looking for a module, we need to use the OS's separator.
79 13         34 my $partial_path = _mod_to_partial_path($pkg);
80              
81 13         32 my @paths;
82              
83 13         35 for(@INC) {
84 47 100       104 if(ref $_) {
85 9         56 my $ret = coderefs_in_INC($_, $inc_path);
86              
87             next
88 9 50       32 unless defined $ret;
89              
90 9 100       27 croak("invalid \@INC subroutine return $ret")
91             unless acts_like_fh($ret);
92              
93 6         2450 return $ret;
94             }
95              
96 38         162 my $fullpath = catfile($_, $partial_path);
97 38 100       1259 push(@paths, $fullpath) if -f $fullpath;
98             }
99              
100 4 100       22 return unless @paths > 0;
101              
102 2 50       13 return wantarray ? @paths : $paths[0];
103             }
104              
105             sub mod_to_path {
106 13     13 1 19 my $pkg = shift;
107 13         18 my $path = $pkg;
108              
109 13 50       272 croak("Invalid package name '$pkg'")
110             unless $pkg =~ $Module::Locate::PkgRe;
111              
112             # %INC always uses / as a directory separator, even on Windows
113 13         55 $path =~ s!::!/!g;
114 13 50       53 $path .= '.pm' unless $path =~ m!\.pm$!;
115              
116 13         34 return $path;
117             }
118              
119             sub coderefs_in_INC {
120 9     9 0 25 my($path, $c) = reverse @_;
121              
122 9 50       74 my $ret = ref($c) eq 'CODE' ?
    100          
    100          
123             $c->( $c, $path )
124             :
125             ref($c) eq 'ARRAY' ?
126             $c->[0]->( $c, $path )
127             :
128             UNIVERSAL::can($c, 'INC') ?
129             $c->INC( $path )
130             :
131             warn("invalid reference in \@INC '$c'")
132             ;
133              
134 9         874 return $ret;
135             }
136              
137             sub acts_like_fh {
138 4     4   40 no strict 'refs';
  4         10  
  4         2230  
139             return ( ref $_[0] and (
140             ( ref $_[0] eq 'GLOB' and defined *{$_[0]}{IO} )
141             or ( UNIVERSAL::isa($_[0], 'IO::Handle') )
142             or ( overload::Method($_[0], '<>') )
143 19   66 19 1 10100 ) or ref \$_[0] eq 'GLOB' and defined *{$_[0]}{IO} );
144             }
145              
146             sub is_mod_loaded {
147 3     3 1 13 my $mod = shift;
148            
149 3 50       32 croak("Invalid package name '$mod'")
150             unless $mod =~ $Module::Locate::PkgRe;
151            
152             ## it looks like %INC entries automagically use / as a separator
153 3         16 my $path = join '/', split '::' => "$mod.pm";
154              
155 3   66     30 return (exists $INC{$path} && defined $INC{$path});
156             }
157              
158             sub _mod_to_partial_path {
159 13     13   19 my $package = shift;
160              
161 13         232 return catfile(split(/::/, $package)).'.pm';
162             }
163              
164             sub is_pkg_loaded {
165 3     3 1 7 my $pkg = shift;
166              
167 3 50       31 croak("Invalid package name '$pkg'")
168             unless $pkg =~ $Module::Locate::PkgRe;
169              
170 3         24 my @tbls = map "${_}::", split('::' => $pkg);
171 3         7 my $tbl = \%main::;
172            
173 3         8 for(@tbls) {
174 5 100       28 return unless exists $tbl->{$_};
175 4         16 $tbl = $tbl->{$_};
176             }
177            
178 2         13 return !!$pkg;
179             }
180             }
181              
182             q[ That better be make-up, and it better be good ];
183              
184             =pod
185              
186             =head1 NAME
187              
188             Module::Locate - locate modules in the same fashion as C and C
189              
190             =head1 SYNOPSIS
191              
192             use Module::Locate qw/ locate get_source /;
193            
194             add_plugin( locate "This::Module" );
195             eval 'use strict; ' . get_source('legacy_code.plx');
196              
197             =head1 DESCRIPTION
198              
199             Using C, return the path that C would find for a given
200             module or filename (it can also return a filehandle if a reference in C<@INC>
201             has been used). This means you can test for the existence, or find the path
202             for, modules without having to evaluate the code they contain.
203              
204             This module also comes with accompanying utility functions that are used within
205             the module itself (except for C) and are available for import.
206              
207             =head1 FUNCTIONS
208              
209             =over 4
210              
211             =item C
212              
213             Given function names, the appropriate functions will be exported into the
214             caller's package.
215              
216             If C<:all> is passed then all subroutines are exported.
217              
218             The B and B options are no longer supported.
219             See the BUGS section below.
220              
221              
222             =item C
223              
224             Given a module name as a string (in standard perl bareword format) locate the
225             path of the module. If called in a scalar context the first path found will be
226             returned, if called in a list context a list of paths where the module was
227             found. Also, if references have been placed in C<@INC> then a filehandle will
228             be returned, as defined in the C documentation. An empty C is
229             used if the module couldn't be located.
230              
231             As of version C<1.7> a filename can also be provided to further mimic the lookup
232             behaviour of C/C.
233              
234             =item C
235              
236             When provided with a package name, gets the path using C.
237             If C returned a path, then the contents of that file are returned
238             by C in a scalar.
239              
240             =item C
241              
242             Given a scalar, check if it behaves like a filehandle. Firstly it checks if it
243             is a bareword filehandle, then if it inherits from C and lastly if
244             it overloads the CE> operator. If this is missing any other standard
245             filehandle behaviour, please send me an e-mail.
246              
247             =item C
248              
249             Given a module name,
250             converts it to a relative path e.g C would become C.
251              
252             Note that this path will always use '/' for the directory separator,
253             even on Windows,
254             as that's the format used in C<%INC>.
255              
256             =item C
257              
258             Given a module name, return true if the module has been
259             loaded (i.e exists in the C<%INC> hash).
260              
261             =item C
262              
263             Given a package name (like C), check if the package has an existing
264             symbol table loaded (checks by walking the C<%main::> stash).
265              
266             =back
267              
268             =head1 SEE ALSO
269              
270             A review of modules that can be used to get the path (and often other information)
271             for one or more modules: L.
272              
273             L and L.
274              
275             =head1 REPOSITORY
276              
277             L
278              
279             =head1 BUGS
280              
281             In previous versions of this module, if you specified C 1>
282             when use'ing this module,
283             then looking up a module's path would update C<%INC>,
284             even if the module hadn't actually been loaded (yet).
285             This meant that if you subsequently tried to load the module,
286             it would wrongly not be loaded.
287              
288             Bugs are tracked using RT (bug you can also raise Github issues if you prefer):
289              
290             L
291              
292             =head1 AUTHOR
293              
294             Dan Brook C<< >>
295              
296             =head1 LICENSE
297              
298             This is free software; you can redistribute it and/or modify it under the same terms as
299             Perl itself.
300              
301             =cut