File Coverage

blib/lib/File/PathList.pm
Criterion Covered Total %
statement 47 48 97.9
branch 21 24 87.5
condition 9 9 100.0
subroutine 11 11 100.0
pod 4 4 100.0
total 92 96 95.8


line stmt bran cond sub pod time code
1             package File::PathList;
2              
3             =pod
4              
5             =head1 NAME
6              
7             File::PathList - Find a file within a set of paths (like @INC or Java classpaths)
8              
9             =head1 SYNOPSIS
10              
11             # Create a basic pathset
12             my $inc = File::PathList->new( \@INC );
13            
14             # Again, but with more explicit params
15             my $inc2 = File::PathList->new(
16             paths => \@INC,
17             cache => 1,
18             );
19            
20             # Get the full (localised) path for a unix-style relative path
21             my $file = "foo/bar/baz.txt";
22             my $path = $inc->find_file( $file );
23            
24             if ( $path ) {
25             print "Found '$file' at '$path'\n";
26             } else {
27             print "Failed to find '$file'\n";
28             }
29              
30             =head1 DESCRIPTION
31              
32             Many systems that map generic relative paths to absolute paths do so with a
33             set of base paths.
34              
35             For example, perl itself when loading classes first turn a C
36             into a path like C, and thens looks through each element of
37             C<@INC> to find the actual file.
38              
39             To aid in portability, all relative paths are provided as unix-style
40             relative paths, and converted to the localised version in the process of
41             looking up the path.
42              
43             =head1 EXTENDING
44              
45             The recommended method for extending C is to add additional
46             topic-specific find methods.
47              
48             For example, a subclass that was attempting to duplicate the functionality
49             of perl's C<@INC> and module location may wish to add a C
50             method.
51              
52             =head1 METHODS
53              
54             =cut
55              
56 2     2   30358 use 5.005;
  2         9  
  2         111  
57 2     2   13 use strict;
  2         5  
  2         68  
58 2     2   23 use File::Spec ();
  2         3  
  2         26  
59 2     2   10 use File::Spec::Unix ();
  2         13  
  2         59  
60 2     2   3141 use Params::Util qw{_ARRAY _CODE};
  2         11925  
  2         340  
61              
62 2     2   20 use vars qw{$VERSION};
  2         4  
  2         106  
63             BEGIN {
64 2     2   1172 $VERSION = '1.04';
65             }
66              
67              
68              
69              
70              
71             #####################################################################
72             # Constructor and Accessors
73              
74             =pod
75              
76             =head2 new \@path | param => $value, ...
77              
78             The C constructor creates a new C.
79              
80             It takes the following options as key/value pairs.
81              
82             =over 4
83              
84             =item paths
85              
86             The compulsory C param should be a reference to an C of local
87             filesystem paths.
88              
89             =item cache
90              
91             If the optional C param is set to true, the object will internally
92             cache the results of the file lookups. (false by default)
93              
94             =back
95              
96             If the C contructor is provided only a single param, this will be
97             take to mean C $param>.
98              
99             Returns a new C object, or C if a valid path set
100             was not provided.
101              
102             =cut
103              
104             sub new {
105 12 50   12 1 2727 my $class = ref $_[0] ? ref shift : shift;
106              
107             # Handle the one argument shorthand case
108 12 100       50 my %params = (@_ == 1)
109             ? (paths => shift)
110             : @_;
111              
112             # Check the paths
113 12 100       67 _ARRAY($params{paths}) or return undef;
114              
115             # Create the basic object
116 7         48 my $self = bless {
117 7 100       13 paths => [ @{$params{paths}} ],
118             # code => !! $params{code},
119             $params{cache}
120             ? ( cache => {} )
121             : (),
122             }, $class;
123              
124             # Make sure there are no CODE refs if we can't have them
125             # unless ( $self->code ) {
126 7 50       21 if ( grep { _CODE($_[0]) } $self->paths ) {
  14         50  
127 0         0 return undef;
128             }
129             # }
130              
131 7         26 $self;
132             }
133              
134             =pod
135              
136             =head2 paths
137              
138             The C accessor returns the list of paths use to create the
139             C object.
140              
141             Returns a list of localised path strings.
142              
143             =cut
144              
145 22     22 1 1623 sub paths { @{$_[0]->{paths}} }
  22         87  
146              
147             =pod
148              
149             =head2 cache
150              
151             The C accessor indicates whether or not the C object
152             is caching the results of the file lookups.
153              
154             =cut
155              
156 4     4 1 30 sub cache { exists $_[0]->{cache} }
157              
158              
159              
160              
161              
162             #####################################################################
163             # File::PathList Methods
164              
165             =pod
166              
167             =head2 find_file $unix_path
168              
169             The C method takes a unix-style relative file path, and
170             iterates through the list of paths, checking for the file in it.
171              
172             Returns the full path to the file, the false null string C<''> if the file
173             could not be found, or C if passed a bad file name.
174              
175             =cut
176              
177             sub find_file {
178 23     23 1 50 my ($self, $rel) = @_;
179              
180             # Check the file name is valid
181 23 100 100     875 defined $rel and ! ref $rel and length $rel or return undef;
      100        
182 15 50       95 File::Spec::Unix->no_upwards($rel) or return undef;
183 15 100       79 File::Spec::Unix->file_name_is_absolute($rel) and return undef;
184              
185             # Is it in the cache?
186 14 100 100     50 if ( $self->{cache} and exists $self->{cache}->{$rel} ) {
187 2         16 return $self->{cache}->{$rel};
188             }
189              
190             # Split up the filename into parts
191 12         122 my (undef, $dir, $file) = File::Spec::Unix->splitpath($rel);
192 12         99 my @parts = ( File::Spec::Unix->splitdir( $dir ), $file );
193              
194             # File name cannot contain upwards parts
195 12 100       64 if ( @parts != File::Spec::Unix->no_upwards(@parts) ) {
196 1         5 return undef;
197             }
198              
199             # Attempt to locate the file in each path
200 11         23 foreach my $inc ( $self->paths ) {
201 18         164 my $path = File::Spec->catfile( $inc, @parts );
202 18 100       309 next unless -f $path;
203              
204             # Cache if needed
205 7 100       18 if ( $self->{cache} ) {
206 1         4 $self->{cache}->{$rel} = $path;
207             }
208              
209 7         60 return $path;
210             }
211              
212             # File not found
213 4         20 '';
214             }
215              
216             1;
217              
218             =pod
219              
220             =head1 SUPPORT
221              
222             Bugs should always be submitted via the CPAN bug tracker
223              
224             L
225              
226             For other issues, contact the maintainer
227              
228             =head1 AUTHOR
229              
230             Adam Kennedy Eadamk@cpan.orgE
231              
232             =head1 ACKNOWLEDGEMENTS
233              
234             Thank you to Phase N (L) for permitting
235             the open sourcing and release of this distribution.
236              
237             =head1 COPYRIGHT
238              
239             Copyright 2005 - 2008 Adam Kennedy.
240              
241             This program is free software; you can redistribute
242             it and/or modify it under the same terms as Perl itself.
243              
244             The full text of the license can be found in the
245             LICENSE file included with this module.
246              
247             =cut