File Coverage

blib/lib/App/Framework/Base/SearchPath.pm
Criterion Covered Total %
statement 92 96 95.8
branch 16 26 61.5
condition 7 12 58.3
subroutine 10 10 100.0
pod 6 6 100.0
total 131 150 87.3


line stmt bran cond sub pod time code
1             package App::Framework::Base::SearchPath ;
2              
3             =head1 NAME
4              
5             App::Framework::Base::SearchPath - Searchable path
6              
7             =head1 SYNOPSIS
8              
9             use App::Framework::Base::SearchPath ;
10              
11              
12             =head1 DESCRIPTION
13              
14             Provides a simple searchable path under which to locate files or directories.
15              
16             When trying the read a file/dir, looks in each location in the path stopping at the first found.
17              
18             When writing a file/dir, attempts to write into each location in the path until can either (a) write, or (b) runs out of search path
19            
20              
21             =cut
22              
23 2     2   12536 use strict ;
  2         1  
  2         90  
24              
25             our $VERSION = "1.000" ;
26              
27             #============================================================================================
28             # USES
29             #============================================================================================
30 2     2   6 use File::Path ;
  2         3  
  2         123  
31              
32 2     2   7 use App::Framework::Base::Object::ErrorHandle ;
  2         2  
  2         1885  
33              
34              
35             #============================================================================================
36             # OBJECT HIERARCHY
37             #============================================================================================
38             our @ISA = qw(App::Framework::Base::Object::ErrorHandle) ;
39              
40             #============================================================================================
41             # GLOBALS
42             #============================================================================================
43              
44             =head2 FIELDS
45              
46             The following fields should be defined either in the call to 'new()', as part of a 'set()' call, or called by their accessor method
47             (which is the same name as the field):
48              
49              
50             =over 4
51              
52             =item B - directory creation mask
53              
54             When the write_path is searched, any directories created are created using this mask [default = 0755]
55              
56             =item B - environment HASH ref
57              
58             Any paths that contain variables have the variables expanded using the standard environment variables. Specifying
59             this HASH ref causes the variables to be replaced from this HASH before looking in the envrionment.
60              
61             =item B - search path
62              
63             A comma seperated list (in scalar context), or an ARRAY ref list of paths to be searched (for a file)
64              
65             =item B - search path for writing
66              
67             A comma seperated list (in scalar context), or an ARRAY ref list of paths to be searched (for a file) when writing. If not set, then
68             B is used.
69              
70              
71             =back
72              
73             =cut
74              
75              
76             my %FIELDS = (
77             # user settings
78             'dir_mask' => 0755,
79             'env' => {},
80            
81             # Object Data
82             'path' => undef, # dummy field - causes _path to be set
83             'write_path' => undef, # dummy field - casues _write_path to be set
84            
85             '_path' => [],
86             '_write_path' => undef,
87             ) ;
88              
89              
90              
91             #============================================================================================
92              
93             =head2 CONSTRUCTOR
94              
95             =over 4
96              
97             =cut
98              
99             #============================================================================================
100              
101             =item B< new([%args]) >
102              
103             Create a new SearchPath object.
104              
105             The %args are specified as they would be in the B method, for example:
106              
107             'mmap_handler' => $mmap_handler
108              
109             The full list of possible arguments are :
110              
111             'fields' => Either ARRAY list of valid field names, or HASH of field names with default values
112              
113             =cut
114              
115             sub new
116             {
117 3     3 1 11 my ($obj, %args) = @_ ;
118            
119 3   33     27 my $class = ref($obj) || $obj ;
120              
121             # Create object
122 3         21 my $this = $class->SUPER::new(%args) ;
123              
124             #$this->debug(2) ;
125 3         16 $this->_dbg_prt(["new this=", $this], 10) ;
126              
127 3         9 return($this) ;
128             }
129              
130              
131              
132             #============================================================================================
133              
134             =back
135              
136             =head2 CLASS METHODS
137              
138             =over 4
139              
140             =cut
141              
142             #============================================================================================
143              
144             #-----------------------------------------------------------------------------
145              
146             =item B< init_class([%args]) >
147              
148             Initialises the SearchPath object class variables.
149              
150             =cut
151              
152             sub init_class
153             {
154 3     3 1 4 my $class = shift ;
155 3         8 my (%args) = @_ ;
156              
157             # Add extra fields
158 3         15 $class->add_fields(\%FIELDS, \%args) ;
159              
160             # init class
161 3         16 $class->SUPER::init_class(%args) ;
162              
163             }
164              
165             #============================================================================================
166              
167             =back
168              
169             =head2 OBJECT METHODS
170              
171             =over 4
172              
173             =cut
174              
175             #============================================================================================
176              
177             #----------------------------------------------------------------------------
178              
179             =item B< path([$path]) >
180              
181             Get/set the search path. When setting, can either be:
182              
183             =over 4
184              
185             =item * comma/semicolon seperated list of directories
186              
187             =item * ARRAY ref to list of directories
188              
189             =back
190              
191             When getting in scalar context returns comma seperated list; otherwise returns an ARRAY.
192              
193             =cut
194              
195             sub path
196             {
197 8     8 1 8 my $this = shift ;
198 8         7 my ($path_ref) = @_ ;
199              
200 8   100     20 $path_ref ||= '' ;
201 8         21 $this->_dbg_prt(["path($path_ref)\n"]) ;
202 8         18 $this->_dbg_prt(["this=", $this], 10) ;
203            
204 8         14 my $list_aref = $this->_access_path('_path', $path_ref) ;
205              
206 8 100       45 return wantarray ? @$list_aref : join ',', @$list_aref ;
207             }
208              
209             #----------------------------------------------------------------------------
210              
211             =item B< write_path([$path]) >
212              
213             Get/set the write path. Set the path for writing file/dir. If this is not set then
214             uses 'path'. You can set this to something different to ensure that created files
215             are limited to user home directory (for example).
216              
217             When setting, can either be:
218              
219             =over 4
220              
221             =item * comma/semicolon seperated list of directories
222              
223             =item * ARRAY ref to list of directories
224              
225             =back
226              
227              
228             When getting in scalar context returns comma seperated list; otherwise returns an ARRAY.
229              
230             =cut
231              
232             sub write_path
233             {
234 7     7 1 8 my $this = shift ;
235 7         8 my ($path_ref) = @_ ;
236              
237 7   100     15 $path_ref ||= '' ;
238 7         16 $this->_dbg_prt(["write_path($path_ref)\n"]) ;
239 7         16 $this->_dbg_prt(["this=", $this], 10) ;
240            
241             # get write path..
242 7         13 my $list_aref = $this->_access_path('_write_path', $path_ref) ;
243            
244             # ..or use 'path'
245 7 100       16 $list_aref = $this->_access_path('_path') unless defined($list_aref) ;
246            
247 7 100       26 return wantarray ? @$list_aref : join ',', @$list_aref ;
248             }
249              
250              
251             #----------------------------------------------------------------------
252              
253             =item B< read_filepath($file) >
254              
255             Search through the search path attempting to read I<$file>. Returns the file
256             path to the readable file if found; otherwise returns undef
257              
258             =cut
259              
260             sub read_filepath
261             {
262 2     2 1 3 my $this = shift ;
263 2         3 my ($file) = @_ ;
264              
265 2         6 $this->_dbg_prt(["get read_filepath($file)\n"]) ;
266 2         9 $this->_dbg_prt(["this=", $this], 10) ;
267            
268 2         5 my @dirs = $this->path() ;
269 2         4 my $path = undef ;
270            
271 2         5 foreach my $d (@dirs)
272             {
273 2         25 my $f = File::Spec->catfile($d, $file) ;
274 2         9 $this->_dbg_prt([" + check $f\n"]) ;
275 2 50       68 if (-f "$f")
276             {
277 2         7 $this->_dbg_prt([" + + found file\n"]) ;
278 2         2 $path = $f ;
279 2         5 last ;
280             }
281             }
282              
283 2         6 return $path ;
284             }
285              
286             #----------------------------------------------------------------------
287              
288             =item B< write_filepath($file) >
289              
290             Search through the search path attempting to write I<$file>. Returns the file
291             path to the writeable file if found; otherwise returns undef
292              
293             =cut
294              
295             sub write_filepath
296             {
297 1     1 1 6 my $this = shift ;
298 1         2 my ($file) = @_ ;
299              
300 1         6 $this->_dbg_prt(["write_filepath($file)\n"]) ;
301 1         5 $this->_dbg_prt(["this=", $this], 10) ;
302            
303 1         3 my @dirs = $this->write_path() ;
304 1         2 my $path = undef ;
305              
306 1         5 $this->_dbg_prt(["Find dir to write to from $file ...\n"]) ;
307            
308 1         7 foreach my $d (@dirs)
309             {
310 1         2 my $found=1 ;
311              
312 1         4 $this->_dbg_prt([" + processing $d\n"]) ;
313              
314             # See if dir exists
315 1 50       36 if (!-d $d)
316             {
317             # See if this user can create the dir
318 1         2 eval {
319 1         4 mkpath([$d], $this->debug, $this->dir_mask) ;
320             };
321 1 50       4 $found=0 if $@ ;
322              
323 1         8 $this->_dbg_prt([" + $d does not exist - attempt to mkdir=$found : $@\n"]) ;
324             }
325              
326 1 50       16 if (-d $d)
327             {
328 1         5 $this->_dbg_prt([" + $d does exist ...\n"]) ;
329              
330             # See if this user can write to the dir
331 1 50       94 if (open my $fh, ">>$d/$file")
332             {
333 1         39 close $fh ;
334              
335 1         8 $this->_dbg_prt([" + + Write to $d/$file succeded\n"]) ;
336             }
337             else
338             {
339 0         0 $this->_dbg_prt([" + + Unable to write to $d/$file - aborting this dir\n"]) ;
340              
341 0         0 $found = 0;
342             }
343             }
344            
345 1 50       4 if ($found)
346             {
347 1         16 $path = File::Spec->catfile($d, $file) ;
348 1         2 last ;
349             }
350             }
351              
352 1 50       7 $this->_dbg_prt(["Searched $file : write path=".($path?$path:"")."\n"]) ;
353            
354 1         4 return $path ;
355             }
356              
357              
358              
359              
360             #============================================================================================
361             # PRIVATE METHODS
362             #============================================================================================
363              
364             #----------------------------------------------------------------------------
365             # get/set paths
366             sub _access_path
367             {
368 16     16   11 my $this = shift ;
369 16         17 my ($name, $path_ref) = @_ ;
370              
371 16   100     33 $path_ref ||= '' ;
372 16         39 $this->_dbg_prt(["_access_path($name, $path_ref)\n"]) ;
373              
374 16 100       27 if ($path_ref)
375             {
376             # Set new value
377 6         5 my @dirs ;
378 6 50       19 if (ref($path_ref) eq 'ARRAY')
379             {
380             # list
381 0         0 @dirs = @$path_ref ;
382             }
383             else
384             {
385             # comma/semicolon seperated list
386 6         20 @dirs = split /[,;]/, $path_ref ;
387             }
388              
389 6         14 $this->_dbg_prt([" + dirs=", \@dirs]) ;
390 6         15 $this->_dbg_prt(["this=", $this], 10) ;
391            
392 6         111 my $vars_href = $this->env ;
393 6         15 $this->_dbg_prt([" + env=", $vars_href]) ;
394            
395             ## expand directories
396 6         10 foreach my $d (@dirs)
397             {
398             # Replace any '~' with $HOME
399 9         11 $d =~ s/~/\$HOME/g ;
400            
401             # Now replace any vars with values from the environment
402 9 0 0     9 $d =~ s/\$(\w+)/$vars_href->{$1} || $ENV{$1} || $1/ge ;
  0         0  
403            
404             # Ensure path is clean
405 9         123 $d = File::Spec->rel2abs($d) ;
406              
407 9         29 $this->_dbg_prt([" + + dir=$d\n"]) ;
408              
409             }
410            
411             # save value
412 6         119 $this->$name(\@dirs) ;
413             }
414              
415 16         42 $this->_dbg_prt([" + now this=", $this], 2);
416              
417             ## return latest settings
418 16         308 return $this->$name() ;
419             }
420              
421              
422             # ============================================================================================
423             # END OF PACKAGE
424              
425             =back
426              
427             =head1 DIAGNOSTICS
428              
429             Setting the debug flag to level 1 prints out (to STDOUT) some debug messages, setting it to level 2 prints out more verbose messages.
430              
431             =head1 AUTHOR
432              
433             Steve Price C<< >>
434              
435             =head1 BUGS
436              
437             None that I know of!
438              
439             =cut
440              
441             1;
442              
443             __END__