File Coverage

lib/App/GitFind/PathClassMicro.pm
Criterion Covered Total %
statement 32 343 9.3
branch 1 178 0.5
condition 1 50 2.0
subroutine 12 80 15.0
pod n/a
total 46 651 7.0


line stmt bran cond sub pod time code
1             # App::GitFind::PathClassMicro.pm: Only the bits of Path::Class used in App::GitFind
2             # Licensed Artistic 1.
3              
4             package App::GitFind::PathClassMicro;
5              
6             our $VERSION = '0.000002';
7              
8             ##############################################################################
9             # Overall docs {{1
10              
11             =head1 NAME
12              
13             App::GitFind::PathClassMicro.pm - Only the bits of Path::Class used in App::GitFind
14              
15             =head1 SYNOPSIS
16              
17             This combines pieces of L<Path::Class::Entity>, L<Path::Class::File>, and
18             L<Path::Class::Dir> by Ken Williams. Those are licensed under the same terms
19             as Perl itself. This file is licensed under the Artistic license, and these
20             modifications are believed to be permissible under clause 3(a) of the
21             Artistic License. This file is available for use and modification under the
22             terms of the Artistic License.
23              
24             B<Modifications>: This file was modified by Christopher White
25             C<< <cxw@cpan.org> >> to combine files and remove functions I don't use in
26             L<App::GitFind>.
27              
28             The remainder of the documentation comes from the individual modules.
29             Multiple packages are combined in this file.
30              
31             =cut
32              
33             # Path::Class is not included - we use the functions directly
34              
35             # }}}1
36             ##############################################################################
37             # Entity {{1
38              
39             package App::GitFind::PathClassMicro::Entity;
40 2     2   12 use strict;
  2         3  
  2         83  
41             {
42             $App::GitFind::PathClassMicro::Entity::VERSION = '0.37';
43             }
44              
45 2     2   8 use File::Spec 3.26;
  2         40  
  2         36  
46             #use File::stat ();
47 2     2   8 use Cwd;
  2         3  
  2         167  
48             #use Carp();
49 0     0   0 sub croak { require Carp; goto &Carp::croak; }
  0         0  
50              
51             use overload
52             (
53 2         66 q[""] => 'stringify',
54             'bool' => 'boolify',
55             fallback => 1,
56 2     2   10 );
  2         3  
57              
58             sub new {
59 0     0   0 my $from = shift;
60             my ($class, $fs_class) = (ref($from)
61             ? (ref $from, $from->{file_spec_class})
62 0 0       0 : ($from, $App::GitFind::PathClassMicro::Foreign));
63 0         0 return bless {file_spec_class => $fs_class}, $class;
64             }
65              
66 0     0   0 sub is_dir { 0 }
67              
68             sub _spec_class {
69 0     0   0 my ($class, $type) = @_;
70              
71 0 0       0 die "Invalid system type '$type'" unless ($type) = $type =~ /^(\w+)$/; # Untaint
72 0         0 my $spec = "File::Spec::$type";
73             ## no critic
74 0 0       0 eval "require $spec; 1" or die $@;
75 0         0 return $spec;
76             }
77              
78             sub new_foreign {
79 0     0   0 my ($class, $type) = (shift, shift);
80 0         0 local $App::GitFind::PathClassMicro::Foreign = $class->_spec_class($type);
81 0         0 return $class->new(@_);
82             }
83              
84 4 50 33 4   40 sub _spec { (ref($_[0]) && $_[0]->{file_spec_class}) || 'File::Spec' }
85              
86 0     0     sub boolify { 1 }
87              
88             sub is_absolute {
89             # 5.6.0 has a bug with regexes and stringification that's ticked by
90             # file_name_is_absolute(). Help it along with an explicit stringify().
91 0     0     $_[0]->_spec->file_name_is_absolute($_[0]->stringify)
92             }
93              
94 0     0     sub is_relative { ! $_[0]->is_absolute }
95              
96             sub cleanup {
97 0     0     my $self = shift;
98 0           my $cleaned = $self->new( $self->_spec->canonpath("$self") );
99 0           %$self = %$cleaned;
100 0           return $self;
101             }
102              
103             sub resolve {
104 0     0     my $self = shift;
105 0 0         croak($! . " $self") unless -e $self; # No such file or directory
106 0           my $cleaned = $self->new( scalar Cwd::realpath($self->stringify) );
107              
108             # realpath() always returns absolute path, kind of annoying
109 0 0         $cleaned = $cleaned->relative if $self->is_relative;
110              
111 0           %$self = %$cleaned;
112 0           return $self;
113             }
114              
115             sub absolute {
116 0     0     my $self = shift;
117 0 0         return $self if $self->is_absolute;
118 0           return $self->new($self->_spec->rel2abs($self->stringify, @_));
119             }
120              
121             sub relative {
122 0     0     my $self = shift;
123 0           return $self->new($self->_spec->abs2rel($self->stringify, @_));
124             }
125              
126 0     0     sub stat { [stat("$_[0]")] }
127 0     0     sub lstat { [lstat("$_[0]")] }
128              
129 0     0     sub PRUNE { return \&PRUNE; }
130              
131             1;
132             # End of App::GitFind::PathClassMicro::Entity
133              
134             =head1 NAME
135              
136             App::GitFind::PathClassMicro::Entity - Base class for files and directories
137              
138             =head1 VERSION
139              
140             version 0.37
141              
142             =head1 DESCRIPTION
143              
144             This class is the base class for C<App::GitFind::PathClassMicro::File> and
145             C<App::GitFind::PathClassMicro::Dir>, it is not used directly by callers.
146              
147             =head1 AUTHOR
148              
149             Ken Williams, kwilliams@cpan.org
150              
151             =head1 SEE ALSO
152              
153             L<Path::Class>
154              
155             =cut
156              
157             # }}}1
158             ##############################################################################
159             # File {{{1
160              
161             package App::GitFind::PathClassMicro::File;
162             {
163             $App::GitFind::PathClassMicro::File::VERSION = '0.37';
164             }
165              
166 2     2   1380 use strict;
  2         4  
  2         61  
167              
168             #use App::GitFind::PathClassMicro::Dir;
169             # In the same file and has no import() - don't need to `use` it
170 2     2   11 use parent -norequire, qw(App::GitFind::PathClassMicro::Entity);
  2         4  
  2         11  
171             #use Carp;
172 0     0     sub croak { require Carp; goto &Carp::croak; }
  0            
173              
174 2     2   1004 use IO::File ();
  2         9134  
  2         615  
175              
176             sub new {
177 0     0     my $self = shift->SUPER::new;
178 0           my $file = pop();
179 0           my @dirs = @_;
180              
181 0           my ($volume, $dirs, $base) = $self->_spec->splitpath($file);
182              
183 0 0         if (length $dirs) {
184 0           push @dirs, $self->_spec->catpath($volume, $dirs, '');
185             }
186              
187 0 0         $self->{dir} = @dirs ? $self->dir_class->new(@dirs) : undef;
188 0           $self->{file} = $base;
189              
190 0           return $self;
191             }
192              
193 0     0     sub dir_class { "App::GitFind::PathClassMicro::Dir" }
194              
195             sub as_foreign {
196 0     0     my ($self, $type) = @_;
197 0           local $App::GitFind::PathClassMicro::Foreign = $self->_spec_class($type);
198 0           my $foreign = ref($self)->SUPER::new;
199 0 0         $foreign->{dir} = $self->{dir}->as_foreign($type) if defined $self->{dir};
200 0           $foreign->{file} = $self->{file};
201 0           return $foreign;
202             }
203              
204             sub stringify {
205 0     0     my $self = shift;
206 0 0         return $self->{file} unless defined $self->{dir};
207 0           return $self->_spec->catfile($self->{dir}->stringify, $self->{file});
208             }
209              
210             sub dir {
211 0     0     my $self = shift;
212 0 0         return $self->{dir} if defined $self->{dir};
213 0           return $self->dir_class->new($self->_spec->curdir);
214             }
215 2     2   2402 BEGIN { *parent = \&dir; }
216              
217             sub volume {
218 0     0     my $self = shift;
219 0 0         return '' unless defined $self->{dir};
220 0           return $self->{dir}->volume;
221             }
222              
223             sub components {
224 0     0     my $self = shift;
225 0 0         croak "Arguments are not currently supported by File->components()" if @_;
226 0           return ($self->dir->components, $self->basename);
227             }
228              
229 0     0     sub basename { shift->{file} }
230 0     0     sub open { IO::File->new(@_) }
231              
232 0 0   0     sub openr { $_[0]->open('r') or croak "Can't read $_[0]: $!" }
233 0 0   0     sub openw { $_[0]->open('w') or croak "Can't write to $_[0]: $!" }
234 0 0   0     sub opena { $_[0]->open('a') or croak "Can't append to $_[0]: $!" }
235              
236             sub touch {
237 0     0     my $self = shift;
238 0 0         if (-e $self) {
239 0           utime undef, undef, $self;
240             } else {
241 0           $self->openw;
242             }
243             }
244              
245             sub slurp {
246 0     0     my ($self, %args) = @_;
247 0   0       my $iomode = $args{iomode} || 'r';
248 0 0         my $fh = $self->open($iomode) or croak "Can't read $self: $!";
249              
250 0 0         if (wantarray) {
251 0           my @data = <$fh>;
252 0 0 0       chomp @data if $args{chomped} or $args{chomp};
253              
254 0 0         if ( my $splitter = $args{split} ) {
255 0           @data = map { [ split $splitter, $_ ] } @data;
  0            
256             }
257              
258 0           return @data;
259             }
260              
261              
262             croak "'split' argument can only be used in list context"
263 0 0         if $args{split};
264              
265              
266 0 0 0       if ($args{chomped} or $args{chomp}) {
267 0           chomp( my @data = <$fh> );
268 0           return join '', @data;
269             }
270              
271              
272 0           local $/;
273 0           return <$fh>;
274             }
275              
276             sub spew {
277 0     0     my $self = shift;
278 0           my %args = splice( @_, 0, @_-1 );
279              
280 0   0       my $iomode = $args{iomode} || 'w';
281 0 0         my $fh = $self->open( $iomode ) or croak "Can't write to $self: $!";
282              
283 0 0         if (ref($_[0]) eq 'ARRAY') {
284             # Use old-school for loop to avoid copying.
285 0           for (my $i = 0; $i < @{ $_[0] }; $i++) {
  0            
286 0 0         print $fh $_[0]->[$i]
287             or croak "Can't write to $self: $!";
288             }
289             }
290             else {
291 0 0         print $fh $_[0]
292             or croak "Can't write to $self: $!";
293             }
294              
295 0 0         close $fh
296             or croak "Can't write to $self: $!";
297              
298 0           return;
299             }
300              
301             sub spew_lines {
302 0     0     my $self = shift;
303 0           my %args = splice( @_, 0, @_-1 );
304              
305 0           my $content = $_[0];
306              
307             # If content is an array ref, appends $/ to each element of the array.
308             # Otherwise, if it is a simple scalar, just appends $/ to that scalar.
309              
310             $content
311             = ref( $content ) eq 'ARRAY'
312 0 0         ? [ map { $_, $/ } @$content ]
  0            
313             : "$content$/";
314              
315 0           return $self->spew( %args, $content );
316             }
317              
318             sub remove {
319 0     0     my $file = shift->stringify;
320 0 0         return unlink $file unless -e $file; # Sets $! correctly
321 0           1 while unlink $file;
322 0           return not -e $file;
323             }
324              
325             sub copy_to {
326 0     0     my ($self, $dest) = @_;
327 0 0         if ( eval{ $dest->isa("App::GitFind::PathClassMicro::File")} ) {
  0 0          
    0          
328 0           $dest = $dest->stringify;
329 0 0         croak "Can't copy to file $dest: it is a directory" if -d $dest;
330 0           } elsif ( eval{ $dest->isa("App::GitFind::PathClassMicro::Dir") } ) {
331 0           $dest = $dest->stringify;
332 0 0         croak "Can't copy to directory $dest: it is a file" if -f $dest;
333 0 0         croak "Can't copy to directory $dest: no such directory" unless -d $dest;
334             } elsif ( ref $dest ) {
335 0           croak "Don't know how to copy files to objects of type '".ref($self)."'";
336             }
337              
338 0           require Perl::OSType;
339 0 0         if ( !Perl::OSType::is_os_type('Unix') ) {
340              
341 0           require File::Copy;
342 0 0         return unless File::Copy::cp($self->stringify, "${dest}");
343              
344             } else {
345              
346 0 0         return unless (system('cp', $self->stringify, "${dest}") == 0);
347              
348             }
349              
350 0           return $self->new($dest);
351             }
352              
353             sub move_to {
354 0     0     my ($self, $dest) = @_;
355 0           require File::Copy;
356 0 0         if (File::Copy::move($self->stringify, "${dest}")) {
357              
358 0           my $new = $self->new($dest);
359              
360 0           $self->{$_} = $new->{$_} foreach (qw/ dir file /);
361              
362 0           return $self;
363              
364             } else {
365              
366 0           return;
367              
368             }
369             }
370              
371             sub traverse {
372 0     0     my $self = shift;
373 0           my ($callback, @args) = @_;
374 0     0     return $self->$callback(sub { () }, @args);
  0            
375             }
376              
377             sub traverse_if {
378 0     0     my $self = shift;
379 0           my ($callback, $condition, @args) = @_;
380 0     0     return $self->$callback(sub { () }, @args);
  0            
381             }
382              
383             1;
384             # End of App::GitFind::PathClassMicro::File
385              
386             =head1 NAME
387              
388             App::GitFind::PathClassMicro::File - Objects representing files
389              
390             =head1 VERSION
391              
392             version 0.37
393              
394             =head1 SYNOPSIS
395              
396             use App::GitFind::PathClassMicro; # Exports file() by default
397              
398             my $file = file('foo', 'bar.txt'); # App::GitFind::PathClassMicro::File object
399             my $file = App::GitFind::PathClassMicro::File->new('foo', 'bar.txt'); # Same thing
400              
401             # Stringifies to 'foo/bar.txt' on Unix, 'foo\bar.txt' on Windows, etc.
402             print "file: $file\n";
403              
404             if ($file->is_absolute) { ... }
405             if ($file->is_relative) { ... }
406              
407             my $v = $file->volume; # Could be 'C:' on Windows, empty string
408             # on Unix, 'Macintosh HD:' on Mac OS
409              
410             $file->cleanup; # Perform logical cleanup of pathname
411             $file->resolve; # Perform physical cleanup of pathname
412              
413             my $dir = $file->dir; # A App::GitFind::PathClassMicro::Dir object
414              
415             my $abs = $file->absolute; # Transform to absolute path
416             my $rel = $file->relative; # Transform to relative path
417              
418             =head1 DESCRIPTION
419              
420             The C<App::GitFind::PathClassMicro::File> class contains functionality for manipulating
421             file names in a cross-platform way.
422              
423             =head1 METHODS
424              
425             =over 4
426              
427             =item $file = App::GitFind::PathClassMicro::File->new( <dir1>, <dir2>, ..., <file> )
428              
429             =item $file = file( <dir1>, <dir2>, ..., <file> )
430              
431             Creates a new C<App::GitFind::PathClassMicro::File> object and returns it. The
432             arguments specify the path to the file. Any volume may also be
433             specified as the first argument, or as part of the first argument.
434             You can use platform-neutral syntax:
435              
436             my $file = file( 'foo', 'bar', 'baz.txt' );
437              
438             or platform-native syntax:
439              
440             my $file = file( 'foo/bar/baz.txt' );
441              
442             or a mixture of the two:
443              
444             my $file = file( 'foo/bar', 'baz.txt' );
445              
446             All three of the above examples create relative paths. To create an
447             absolute path, either use the platform native syntax for doing so:
448              
449             my $file = file( '/var/tmp/foo.txt' );
450              
451             or use an empty string as the first argument:
452              
453             my $file = file( '', 'var', 'tmp', 'foo.txt' );
454              
455             If the second form seems awkward, that's somewhat intentional - paths
456             like C</var/tmp> or C<\Windows> aren't cross-platform concepts in the
457             first place, so they probably shouldn't appear in your code if you're
458             trying to be cross-platform. The first form is perfectly fine,
459             because paths like this may come from config files, user input, or
460             whatever.
461              
462             =item $file->stringify
463              
464             This method is called internally when a C<App::GitFind::PathClassMicro::File> object is
465             used in a string context, so the following are equivalent:
466              
467             $string = $file->stringify;
468             $string = "$file";
469              
470             =item $file->volume
471              
472             Returns the volume (e.g. C<C:> on Windows, C<Macintosh HD:> on Mac OS,
473             etc.) of the object, if any. Otherwise, returns the empty string.
474              
475             =item $file->basename
476              
477             Returns the name of the file as a string, without the directory
478             portion (if any).
479              
480             =item $file->components
481              
482             Returns a list of the directory components of this file, followed by
483             the basename.
484              
485             Note: unlike C<< $dir->components >>, this method currently does not
486             accept any arguments to select which elements of the list will be
487             returned. It may do so in the future. Currently it throws an
488             exception if such arguments are present.
489              
490              
491             =item $file->is_dir
492              
493             Returns a boolean value indicating whether this object represents a
494             directory. Not surprisingly, C<App::GitFind::PathClassMicro::File> objects always
495             return false, and L<App::GitFind::PathClassMicro::Dir> objects always return true.
496              
497             =item $file->is_absolute
498              
499             Returns true or false depending on whether the file refers to an
500             absolute path specifier (like C</usr/local/foo.txt> or C<\Windows\Foo.txt>).
501              
502             =item $file->is_relative
503              
504             Returns true or false depending on whether the file refers to a
505             relative path specifier (like C<lib/foo.txt> or C<.\Foo.txt>).
506              
507             =item $file->cleanup
508              
509             Performs a logical cleanup of the file path. For instance:
510              
511             my $file = file('/foo//baz/./foo.txt')->cleanup;
512             # $file now represents '/foo/baz/foo.txt';
513              
514             =item $dir->resolve
515              
516             Performs a physical cleanup of the file path. For instance:
517              
518             my $file = file('/foo/baz/../foo.txt')->resolve;
519             # $file now represents '/foo/foo.txt', assuming no symlinks
520              
521             This actually consults the filesystem to verify the validity of the
522             path.
523              
524             =item $dir = $file->dir
525              
526             Returns a C<App::GitFind::PathClassMicro::Dir> object representing the directory
527             containing this file.
528              
529             =item $dir = $file->parent
530              
531             A synonym for the C<dir()> method.
532              
533             =item $abs = $file->absolute
534              
535             Returns a C<App::GitFind::PathClassMicro::File> object representing C<$file> as an
536             absolute path. An optional argument, given as either a string or a
537             L<App::GitFind::PathClassMicro::Dir> object, specifies the directory to use as the base
538             of relativity - otherwise the current working directory will be used.
539              
540             =item $rel = $file->relative
541              
542             Returns a C<App::GitFind::PathClassMicro::File> object representing C<$file> as a
543             relative path. An optional argument, given as either a string or a
544             C<App::GitFind::PathClassMicro::Dir> object, specifies the directory to use as the base
545             of relativity - otherwise the current working directory will be used.
546              
547             =item $foreign = $file->as_foreign($type)
548              
549             Returns a C<App::GitFind::PathClassMicro::File> object representing C<$file> as it would
550             be specified on a system of type C<$type>. Known types include
551             C<Unix>, C<Win32>, C<Mac>, C<VMS>, and C<OS2>, i.e. anything for which
552             there is a subclass of C<File::Spec>.
553              
554             Any generated objects (subdirectories, files, parents, etc.) will also
555             retain this type.
556              
557             =item $foreign = App::GitFind::PathClassMicro::File->new_foreign($type, @args)
558              
559             Returns a C<App::GitFind::PathClassMicro::File> object representing a file as it would
560             be specified on a system of type C<$type>. Known types include
561             C<Unix>, C<Win32>, C<Mac>, C<VMS>, and C<OS2>, i.e. anything for which
562             there is a subclass of C<File::Spec>.
563              
564             The arguments in C<@args> are the same as they would be specified in
565             C<new()>.
566              
567             =item $fh = $file->open($mode, $permissions)
568              
569             Passes the given arguments, including C<$file>, to C<< IO::File->new >>
570             (which in turn calls C<< IO::File->open >> and returns the result
571             as an L<IO::File> object. If the opening
572             fails, C<undef> is returned and C<$!> is set.
573              
574             =item $fh = $file->openr()
575              
576             A shortcut for
577              
578             $fh = $file->open('r') or croak "Can't read $file: $!";
579              
580             =item $fh = $file->openw()
581              
582             A shortcut for
583              
584             $fh = $file->open('w') or croak "Can't write to $file: $!";
585              
586             =item $fh = $file->opena()
587              
588             A shortcut for
589              
590             $fh = $file->open('a') or croak "Can't append to $file: $!";
591              
592             =item $file->touch
593              
594             Sets the modification and access time of the given file to right now,
595             if the file exists. If it doesn't exist, C<touch()> will I<make> it
596             exist, and - YES! - set its modification and access time to now.
597              
598             =item $file->slurp()
599              
600             In a scalar context, returns the contents of C<$file> in a string. In
601             a list context, returns the lines of C<$file> (according to how C<$/>
602             is set) as a list. If the file can't be read, this method will throw
603             an exception.
604              
605             If you want C<chomp()> run on each line of the file, pass a true value
606             for the C<chomp> or C<chomped> parameters:
607              
608             my @lines = $file->slurp(chomp => 1);
609              
610             You may also use the C<iomode> parameter to pass in an IO mode to use
611             when opening the file, usually IO layers (though anything accepted by
612             the MODE argument of C<open()> is accepted here). Just make sure it's
613             a I<reading> mode.
614              
615             my @lines = $file->slurp(iomode => ':crlf');
616             my $lines = $file->slurp(iomode => '<:encoding(UTF-8)');
617              
618             The default C<iomode> is C<r>.
619              
620             Lines can also be automatically split, mimicking the perl command-line
621             option C<-a> by using the C<split> parameter. If this parameter is used,
622             each line will be returned as an array ref.
623              
624             my @lines = $file->slurp( chomp => 1, split => qr/\s*,\s*/ );
625              
626             The C<split> parameter can only be used in a list context.
627              
628             =item $file->spew( $content );
629              
630             The opposite of L</slurp>, this takes a list of strings and prints them
631             to the file in write mode. If the file can't be written to, this method
632             will throw an exception.
633              
634             The content to be written can be either an array ref or a plain scalar.
635             If the content is an array ref then each entry in the array will be
636             written to the file.
637              
638             You may use the C<iomode> parameter to pass in an IO mode to use when
639             opening the file, just like L</slurp> supports.
640              
641             $file->spew(iomode => '>:raw', $content);
642              
643             The default C<iomode> is C<w>.
644              
645             =item $file->spew_lines( $content );
646              
647             Just like C<spew>, but, if $content is a plain scalar, appends $/
648             to it, or, if $content is an array ref, appends $/ to each element
649             of the array.
650              
651             Can also take an C<iomode> parameter like C<spew>. Again, the
652             default C<iomode> is C<w>.
653              
654             =item $file->traverse(sub { ... }, @args)
655              
656             Calls the given callback on $file. This doesn't do much on its own,
657             but see the associated documentation in L<App::GitFind::PathClassMicro::Dir>.
658              
659             =item $file->remove()
660              
661             This method will remove the file in a way that works well on all
662             platforms, and returns a boolean value indicating whether or not the
663             file was successfully removed.
664              
665             C<remove()> is better than simply calling Perl's C<unlink()> function,
666             because on some platforms (notably VMS) you actually may need to call
667             C<unlink()> several times before all versions of the file are gone -
668             the C<remove()> method handles this process for you.
669              
670             =item $st = $file->stat()
671              
672             Invokes C<< File::stat::stat() >> on this file and returns a
673             L<File::stat> object representing the result.
674              
675             MODIFIED: returns an arrayref of C<stat()> results.
676              
677             =item $st = $file->lstat()
678              
679             Same as C<stat()>, but if C<$file> is a symbolic link, C<lstat()>
680             stats the link instead of the file the link points to.
681              
682             MODIFIED: returns an arrayref of C<lstat()> results.
683              
684             =item $class = $file->dir_class()
685              
686             Returns the class which should be used to create directory objects.
687              
688             Generally overridden whenever this class is subclassed.
689              
690             =item $copy = $file->copy_to( $dest );
691              
692             Copies the C<$file> to C<$dest>. It returns a L<App::GitFind::PathClassMicro::File>
693             object when successful, C<undef> otherwise.
694              
695             =item $moved = $file->move_to( $dest );
696              
697             Moves the C<$file> to C<$dest>, and updates C<$file> accordingly.
698              
699             It returns C<$file> is successful, C<undef> otherwise.
700              
701             =back
702              
703             =head1 AUTHOR
704              
705             Ken Williams, kwilliams@cpan.org
706              
707             =head1 SEE ALSO
708              
709             L<Path::Class>, L<Path::Class::Dir>, L<File::Spec>
710              
711             =cut
712              
713             # }}}1
714             ##############################################################################
715             # Dir {{{1
716              
717             package App::GitFind::PathClassMicro::Dir;
718             {
719             $App::GitFind::PathClassMicro::Dir::VERSION = '0.37';
720             }
721              
722 2     2   14 use strict;
  2         2  
  2         116  
723              
724             #use App::GitFind::PathClassMicro::File;
725             # In the same file and has no import() - don't need to `use` it
726             #use Carp();
727 0     0     sub croak { require Carp; goto &Carp::croak; }
  0            
728 2     2   31 use parent -norequire, qw(App::GitFind::PathClassMicro::Entity);
  2         4  
  2         11  
729              
730             #use IO::Dir ();
731             #use File::Path ();
732             #use File::Temp ();
733 2     2   89 use Scalar::Util ();
  2         17  
  2         3779  
734              
735             # updir & curdir on the local machine, for screening them out in
736             # children(). Note that they don't respect 'foreign' semantics.
737             my $Updir = __PACKAGE__->_spec->updir;
738             my $Curdir = __PACKAGE__->_spec->curdir;
739              
740             sub new {
741 0     0     my $self = shift->SUPER::new();
742              
743             # If the only arg is undef, it's probably a mistake. Without this
744             # special case here, we'd return the root directory, which is a
745             # lousy thing to do to someone when they made a mistake. Return
746             # undef instead.
747 0 0 0       return if @_==1 && !defined($_[0]);
748              
749 0           my $s = $self->_spec;
750              
751 0 0 0       my $first = (@_ == 0 ? $s->curdir :
    0          
752             !ref($_[0]) && $_[0] eq '' ? (shift, $s->rootdir) :
753             shift()
754             );
755              
756 0           $self->{dirs} = [];
757 0 0 0       if ( Scalar::Util::blessed($first) && $first->isa("App::GitFind::PathClassMicro::Dir") ) {
758 0           $self->{volume} = $first->{volume};
759 0           push @{$self->{dirs}}, @{$first->{dirs}};
  0            
  0            
760             }
761             else {
762 0           ($self->{volume}, my $dirs) = $s->splitpath( $s->canonpath("$first") , 1);
763 0 0         push @{$self->{dirs}}, $dirs eq $s->rootdir ? "" : $s->splitdir($dirs);
  0            
764             }
765              
766 0           push @{$self->{dirs}}, map {
767 0           Scalar::Util::blessed($_) && $_->isa("App::GitFind::PathClassMicro::Dir")
768 0 0 0       ? @{$_->{dirs}}
  0            
769             : $s->splitdir( $s->canonpath($_) )
770             } @_;
771              
772              
773 0           return $self;
774             }
775              
776 0     0     sub file_class { "App::GitFind::PathClassMicro::File" }
777              
778 0     0     sub is_dir { 1 }
779              
780             sub as_foreign {
781 0     0     my ($self, $type) = @_;
782              
783 0           my $foreign = do {
784 0           local $self->{file_spec_class} = $self->_spec_class($type);
785 0           $self->SUPER::new;
786             };
787              
788             # Clone internal structure
789 0           $foreign->{volume} = $self->{volume};
790 0           my ($u, $fu) = ($self->_spec->updir, $foreign->_spec->updir);
791 0 0         $foreign->{dirs} = [ map {$_ eq $u ? $fu : $_} @{$self->{dirs}}];
  0            
  0            
792 0           return $foreign;
793             }
794              
795             sub stringify {
796 0     0     my $self = shift;
797 0           my $s = $self->_spec;
798             return $s->catpath($self->{volume},
799 0           $s->catdir(@{$self->{dirs}}),
  0            
800             '');
801             }
802              
803 0     0     sub volume { shift()->{volume} }
804              
805             sub file {
806 0 0   0     local $App::GitFind::PathClassMicro::Foreign = $_[0]->{file_spec_class} if $_[0]->{file_spec_class};
807 0           return $_[0]->file_class->new(@_);
808             }
809              
810 0     0     sub basename { shift()->{dirs}[-1] }
811              
812             sub dir_list {
813 0     0     my $self = shift;
814 0           my $d = $self->{dirs};
815 0 0         return @$d unless @_;
816              
817 0           my $offset = shift;
818 0 0         if ($offset < 0) { $offset = $#$d + $offset + 1 }
  0            
819              
820 0 0         return wantarray ? @$d[$offset .. $#$d] : $d->[$offset] unless @_;
    0          
821              
822 0           my $length = shift;
823 0 0         if ($length < 0) { $length = $#$d + $length + 1 - $offset }
  0            
824 0           return @$d[$offset .. $length + $offset - 1];
825             }
826              
827             sub components {
828 0     0     my $self = shift;
829 0           return $self->dir_list(@_);
830             }
831              
832             sub subdir {
833 0     0     my $self = shift;
834 0           return $self->new($self, @_);
835             }
836              
837             sub parent {
838 0     0     my $self = shift;
839 0           my $dirs = $self->{dirs};
840 0           my ($curdir, $updir) = ($self->_spec->curdir, $self->_spec->updir);
841              
842 0 0         if ($self->is_absolute) {
    0          
    0          
    0          
843 0           my $parent = $self->new($self);
844 0 0         pop @{$parent->{dirs}} if @$dirs > 1;
  0            
845 0           return $parent;
846              
847             } elsif ($self eq $curdir) {
848 0           return $self->new($updir);
849              
850 0           } elsif (!grep {$_ ne $updir} @$dirs) { # All updirs
851 0           return $self->new($self, $updir); # Add one more
852              
853             } elsif (@$dirs == 1) {
854 0           return $self->new($curdir);
855              
856             } else {
857 0           my $parent = $self->new($self);
858 0           pop @{$parent->{dirs}};
  0            
859 0           return $parent;
860             }
861             }
862              
863             sub relative {
864             # File::Spec->abs2rel before version 3.13 returned the empty string
865             # when the two paths were equal - work around it here.
866 0     0     my $self = shift;
867 0           my $rel = $self->_spec->abs2rel($self->stringify, @_);
868 0 0         return $self->new( length $rel ? $rel : $self->_spec->curdir );
869             }
870              
871             #sub open { IO::Dir->new(@_) }
872             #sub mkpath { File::Path::mkpath(shift()->stringify, @_) }
873             #sub rmtree { File::Path::rmtree(shift()->stringify, @_) }
874              
875             sub remove {
876 0     0     rmdir( shift() );
877             }
878              
879             sub traverse {
880 0     0     my $self = shift;
881 0           my ($callback, @args) = @_;
882 0           my @children = $self->children;
883             return $self->$callback(
884             sub {
885 0     0     my @inner_args = @_;
886 0           return map { $_->traverse($callback, @inner_args) } @children;
  0            
887             },
888             @args
889 0           );
890             }
891              
892             sub traverse_if {
893 0     0     my $self = shift;
894 0           my ($callback, $condition, @args) = @_;
895 0           my @children = grep { $condition->($_) } $self->children;
  0            
896             return $self->$callback(
897             sub {
898 0     0     my @inner_args = @_;
899 0           return map { $_->traverse_if($callback, $condition, @inner_args) } @children;
  0            
900             },
901             @args
902 0           );
903             }
904              
905             sub recurse {
906 0     0     my $self = shift;
907 0           my %opts = (preorder => 1, depthfirst => 0, @_);
908              
909             my $callback = $opts{callback}
910 0 0         or croak( "Must provide a 'callback' parameter to recurse()" );
911              
912 0           my @queue = ($self);
913              
914 0           my $visit_entry;
915             my $visit_dir =
916             $opts{depthfirst} && $opts{preorder}
917             ? sub {
918 0     0     my $dir = shift;
919 0           my $ret = $callback->($dir);
920 0 0 0       unless( ($ret||'') eq $self->PRUNE ) {
921 0           unshift @queue, $dir->children;
922             }
923             }
924             : $opts{preorder}
925             ? sub {
926 0     0     my $dir = shift;
927 0           my $ret = $callback->($dir);
928 0 0 0       unless( ($ret||'') eq $self->PRUNE ) {
929 0           push @queue, $dir->children;
930             }
931             }
932             : sub {
933 0     0     my $dir = shift;
934 0           $visit_entry->($_) foreach $dir->children;
935 0           $callback->($dir);
936 0 0 0       };
    0          
937              
938             $visit_entry = sub {
939 0     0     my $entry = shift;
940 0 0         if ($entry->is_dir) { $visit_dir->($entry) } # Will call $callback
  0            
941 0           else { $callback->($entry) }
942 0           };
943              
944 0           while (@queue) {
945 0           $visit_entry->( shift @queue );
946             }
947             }
948              
949             sub children {
950 0     0     my ($self, %opts) = @_;
951              
952 0 0         my $dh = $self->open or croak( "Can't open directory $self: $!" );
953              
954 0           my @out;
955 0           while (defined(my $entry = $dh->read)) {
956 0 0 0       next if !$opts{all} && $self->_is_local_dot_dir($entry);
957 0 0 0       next if ($opts{no_hidden} && $entry =~ /^\./);
958 0           push @out, $self->file($entry);
959 0 0         $out[-1] = $self->subdir($entry) if -d $out[-1];
960             }
961 0           return @out;
962             }
963              
964             sub _is_local_dot_dir {
965 0     0     my $self = shift;
966 0           my $dir = shift;
967              
968 0   0       return ($dir eq $Updir or $dir eq $Curdir);
969             }
970              
971             sub next {
972 0     0     my $self = shift;
973 0 0         unless ($self->{dh}) {
974 0 0         $self->{dh} = $self->open or croak( "Can't open directory $self: $!" );
975             }
976              
977 0           my $next = $self->{dh}->read;
978 0 0         unless (defined $next) {
979 0           delete $self->{dh};
980             ## no critic
981 0           return undef;
982             }
983              
984             # Figure out whether it's a file or directory
985 0           my $file = $self->file($next);
986 0 0         $file = $self->subdir($next) if -d $file;
987 0           return $file;
988             }
989              
990             sub subsumes {
991 0 0   0     croak "Too many arguments given to subsumes()" if $#_ > 2;
992 0           my ($self, $other) = @_;
993 0 0         croak( "No second entity given to subsumes()" ) unless defined $other;
994              
995 0 0         $other = $self->new($other) unless eval{$other->isa( "App::GitFind::PathClassMicro::Entity")};
  0            
996 0 0         $other = $other->dir unless $other->is_dir;
997              
998 0 0         if ($self->is_absolute) {
    0          
999 0           $other = $other->absolute;
1000             } elsif ($other->is_absolute) {
1001 0           $self = $self->absolute;
1002             }
1003              
1004 0           $self = $self->cleanup;
1005 0           $other = $other->cleanup;
1006              
1007 0 0 0       if ($self->volume || $other->volume) {
1008 0 0         return 0 unless $other->volume eq $self->volume;
1009             }
1010              
1011             # The root dir subsumes everything (but ignore the volume because
1012             # we've already checked that)
1013 0 0         return 1 if "@{$self->{dirs}}" eq "@{$self->new('')->{dirs}}";
  0            
  0            
1014              
1015             # The current dir subsumes every relative path (unless starting with updir)
1016 0 0         if ($self eq $self->_spec->curdir) {
1017 0           return $other->{dirs}[0] ne $self->_spec->updir;
1018             }
1019              
1020 0           my $i = 0;
1021 0           while ($i <= $#{ $self->{dirs} }) {
  0            
1022 0 0         return 0 if $i > $#{ $other->{dirs} };
  0            
1023 0 0         return 0 if $self->{dirs}[$i] ne $other->{dirs}[$i];
1024 0           $i++;
1025             }
1026 0           return 1;
1027             }
1028              
1029             sub contains {
1030 0 0   0     croak "Too many arguments given to contains()" if $#_ > 2;
1031 0           my ($self, $other) = @_;
1032 0 0         croak "No second entity given to contains()" unless defined $other;
1033 0 0 0       return unless -d $self and (-e $other or -l $other);
      0        
1034              
1035             # We're going to resolve the path, and don't want side effects on the objects
1036             # so clone them. This also handles strings passed as $other.
1037 0           $self= $self->new($self)->resolve;
1038 0           $other= $self->new($other)->resolve;
1039              
1040 0           return $self->subsumes($other);
1041             }
1042              
1043             =for comment
1044              
1045             sub tempfile {
1046             my $self = shift;
1047             return File::Temp::tempfile(@_, DIR => $self->stringify);
1048             }
1049              
1050             =cut
1051              
1052             1;
1053             # End of App::GitFind::PathClassMicro::Dir
1054              
1055             =head1 NAME
1056              
1057             App::GitFind::PathClassMicro::Dir - Objects representing directories
1058              
1059             =head1 VERSION
1060              
1061             version 0.37
1062              
1063             =head1 SYNOPSIS
1064              
1065             use App::GitFind::PathClassMicro; # Exports dir() by default
1066              
1067             my $dir = dir('foo', 'bar'); # App::GitFind::PathClassMicro::Dir object
1068             my $dir = App::GitFind::PathClassMicro::Dir->new('foo', 'bar'); # Same thing
1069              
1070             # Stringifies to 'foo/bar' on Unix, 'foo\bar' on Windows, etc.
1071             print "dir: $dir\n";
1072              
1073             if ($dir->is_absolute) { ... }
1074             if ($dir->is_relative) { ... }
1075              
1076             my $v = $dir->volume; # Could be 'C:' on Windows, empty string
1077             # on Unix, 'Macintosh HD:' on Mac OS
1078              
1079             $dir->cleanup; # Perform logical cleanup of pathname
1080             $dir->resolve; # Perform physical cleanup of pathname
1081              
1082             my $file = $dir->file('file.txt'); # A file in this directory
1083             my $subdir = $dir->subdir('george'); # A subdirectory
1084             my $parent = $dir->parent; # The parent directory, 'foo'
1085              
1086             my $abs = $dir->absolute; # Transform to absolute path
1087             my $rel = $abs->relative; # Transform to relative path
1088             my $rel = $abs->relative('/foo'); # Relative to /foo
1089              
1090             print $dir->as_foreign('Mac'); # :foo:bar:
1091             print $dir->as_foreign('Win32'); # foo\bar
1092              
1093             # Iterate with IO::Dir methods:
1094             my $handle = $dir->open;
1095             while (my $file = $handle->read) {
1096             $file = $dir->file($file); # Turn into App::GitFind::PathClassMicro::File object
1097             ...
1098             }
1099              
1100             # Iterate with App::GitFind::PathClassMicro methods:
1101             while (my $file = $dir->next) {
1102             # $file is a App::GitFind::PathClassMicro::File or App::GitFind::PathClassMicro::Dir object
1103             ...
1104             }
1105              
1106              
1107             =head1 DESCRIPTION
1108              
1109             The C<App::GitFind::PathClassMicro::Dir> class contains functionality for manipulating
1110             directory names in a cross-platform way.
1111              
1112             =head1 METHODS
1113              
1114             =over 4
1115              
1116             =item $dir = App::GitFind::PathClassMicro::Dir->new( <dir1>, <dir2>, ... )
1117              
1118             =item $dir = dir( <dir1>, <dir2>, ... )
1119              
1120             Creates a new C<App::GitFind::PathClassMicro::Dir> object and returns it. The
1121             arguments specify names of directories which will be joined to create
1122             a single directory object. A volume may also be specified as the
1123             first argument, or as part of the first argument. You can use
1124             platform-neutral syntax:
1125              
1126             my $dir = dir( 'foo', 'bar', 'baz' );
1127              
1128             or platform-native syntax:
1129              
1130             my $dir = dir( 'foo/bar/baz' );
1131              
1132             or a mixture of the two:
1133              
1134             my $dir = dir( 'foo/bar', 'baz' );
1135              
1136             All three of the above examples create relative paths. To create an
1137             absolute path, either use the platform native syntax for doing so:
1138              
1139             my $dir = dir( '/var/tmp' );
1140              
1141             or use an empty string as the first argument:
1142              
1143             my $dir = dir( '', 'var', 'tmp' );
1144              
1145             If the second form seems awkward, that's somewhat intentional - paths
1146             like C</var/tmp> or C<\Windows> aren't cross-platform concepts in the
1147             first place (many non-Unix platforms don't have a notion of a "root
1148             directory"), so they probably shouldn't appear in your code if you're
1149             trying to be cross-platform. The first form is perfectly natural,
1150             because paths like this may come from config files, user input, or
1151             whatever.
1152              
1153             As a special case, since it doesn't otherwise mean anything useful and
1154             it's convenient to define this way, C<< App::GitFind::PathClassMicro::Dir->new() >> (or
1155             C<dir()>) refers to the current directory (C<< File::Spec->curdir >>).
1156             To get the current directory as an absolute path, do C<<
1157             dir()->absolute >>.
1158              
1159             Finally, as another special case C<dir(undef)> will return undef,
1160             since that's usually an accident on the part of the caller, and
1161             returning the root directory would be a nasty surprise just asking for
1162             trouble a few lines later.
1163              
1164             =item $dir->stringify
1165              
1166             This method is called internally when a C<App::GitFind::PathClassMicro::Dir> object is
1167             used in a string context, so the following are equivalent:
1168              
1169             $string = $dir->stringify;
1170             $string = "$dir";
1171              
1172             =item $dir->volume
1173              
1174             Returns the volume (e.g. C<C:> on Windows, C<Macintosh HD:> on Mac OS,
1175             etc.) of the directory object, if any. Otherwise, returns the empty
1176             string.
1177              
1178             =item $dir->basename
1179              
1180             Returns the last directory name of the path as a string.
1181              
1182             =item $dir->is_dir
1183              
1184             Returns a boolean value indicating whether this object represents a
1185             directory. Not surprisingly, L<App::GitFind::PathClassMicro::File> objects always
1186             return false, and C<App::GitFind::PathClassMicro::Dir> objects always return true.
1187              
1188             =item $dir->is_absolute
1189              
1190             Returns true or false depending on whether the directory refers to an
1191             absolute path specifier (like C</usr/local> or C<\Windows>).
1192              
1193             =item $dir->is_relative
1194              
1195             Returns true or false depending on whether the directory refers to a
1196             relative path specifier (like C<lib/foo> or C<./dir>).
1197              
1198             =item $dir->cleanup
1199              
1200             Performs a logical cleanup of the file path. For instance:
1201              
1202             my $dir = dir('/foo//baz/./foo')->cleanup;
1203             # $dir now represents '/foo/baz/foo';
1204              
1205             =item $dir->resolve
1206              
1207             Performs a physical cleanup of the file path. For instance:
1208              
1209             my $dir = dir('/foo//baz/../foo')->resolve;
1210             # $dir now represents '/foo/foo', assuming no symlinks
1211              
1212             This actually consults the filesystem to verify the validity of the
1213             path.
1214              
1215             =item $file = $dir->file( <dir1>, <dir2>, ..., <file> )
1216              
1217             Returns a L<App::GitFind::PathClassMicro::File> object representing an entry in C<$dir>
1218             or one of its subdirectories. Internally, this just calls C<<
1219             App::GitFind::PathClassMicro::File->new( @_ ) >>.
1220              
1221             =item $subdir = $dir->subdir( <dir1>, <dir2>, ... )
1222              
1223             Returns a new C<App::GitFind::PathClassMicro::Dir> object representing a subdirectory
1224             of C<$dir>.
1225              
1226             =item $parent = $dir->parent
1227              
1228             Returns the parent directory of C<$dir>. Note that this is the
1229             I<logical> parent, not necessarily the physical parent. It really
1230             means we just chop off entries from the end of the directory list
1231             until we cain't chop no more. If the directory is relative, we start
1232             using the relative forms of parent directories.
1233              
1234             The following code demonstrates the behavior on absolute and relative
1235             directories:
1236              
1237             $dir = dir('/foo/bar');
1238             for (1..6) {
1239             print "Absolute: $dir\n";
1240             $dir = $dir->parent;
1241             }
1242              
1243             $dir = dir('foo/bar');
1244             for (1..6) {
1245             print "Relative: $dir\n";
1246             $dir = $dir->parent;
1247             }
1248              
1249             ########### Output on Unix ################
1250             Absolute: /foo/bar
1251             Absolute: /foo
1252             Absolute: /
1253             Absolute: /
1254             Absolute: /
1255             Absolute: /
1256             Relative: foo/bar
1257             Relative: foo
1258             Relative: .
1259             Relative: ..
1260             Relative: ../..
1261             Relative: ../../..
1262              
1263             =item @list = $dir->children
1264              
1265             Returns a list of L<App::GitFind::PathClassMicro::File> and/or C<App::GitFind::PathClassMicro::Dir>
1266             objects listed in this directory, or in scalar context the number of
1267             such objects. Obviously, it is necessary for C<$dir> to
1268             exist and be readable in order to find its children.
1269              
1270             Note that the children are returned as subdirectories of C<$dir>,
1271             i.e. the children of F<foo> will be F<foo/bar> and F<foo/baz>, not
1272             F<bar> and F<baz>.
1273              
1274             Ordinarily C<children()> will not include the I<self> and I<parent>
1275             entries C<.> and C<..> (or their equivalents on non-Unix systems),
1276             because that's like I'm-my-own-grandpa business. If you do want all
1277             directory entries including these special ones, pass a true value for
1278             the C<all> parameter:
1279              
1280             @c = $dir->children(); # Just the children
1281             @c = $dir->children(all => 1); # All entries
1282              
1283             In addition, there's a C<no_hidden> parameter that will exclude all
1284             normally "hidden" entries - on Unix this means excluding all entries
1285             that begin with a dot (C<.>):
1286              
1287             @c = $dir->children(no_hidden => 1); # Just normally-visible entries
1288              
1289              
1290             =item $abs = $dir->absolute
1291              
1292             Returns a C<App::GitFind::PathClassMicro::Dir> object representing C<$dir> as an
1293             absolute path. An optional argument, given as either a string or a
1294             C<App::GitFind::PathClassMicro::Dir> object, specifies the directory to use as the base
1295             of relativity - otherwise the current working directory will be used.
1296              
1297             =item $rel = $dir->relative
1298              
1299             Returns a C<App::GitFind::PathClassMicro::Dir> object representing C<$dir> as a
1300             relative path. An optional argument, given as either a string or a
1301             C<App::GitFind::PathClassMicro::Dir> object, specifies the directory to use as the base
1302             of relativity - otherwise the current working directory will be used.
1303              
1304             =item $boolean = $dir->subsumes($other)
1305              
1306             Returns true if this directory spec subsumes the other spec, and false
1307             otherwise. Think of "subsumes" as "contains", but we only look at the
1308             I<specs>, not whether C<$dir> actually contains C<$other> on the
1309             filesystem.
1310              
1311             The C<$other> argument may be a C<App::GitFind::PathClassMicro::Dir> object, a
1312             L<App::GitFind::PathClassMicro::File> object, or a string. In the latter case, we
1313             assume it's a directory.
1314              
1315             # Examples:
1316             dir('foo/bar' )->subsumes(dir('foo/bar/baz')) # True
1317             dir('/foo/bar')->subsumes(dir('/foo/bar/baz')) # True
1318             dir('foo/..')->subsumes(dir('foo/../bar)) # True
1319             dir('foo/bar' )->subsumes(dir('bar/baz')) # False
1320             dir('/foo/bar')->subsumes(dir('foo/bar')) # False
1321             dir('foo/..')->subsumes(dir('bar')) # False! Use C<contains> to resolve ".."
1322              
1323              
1324             =item $boolean = $dir->contains($other)
1325              
1326             Returns true if this directory actually contains C<$other> on the
1327             filesystem. C<$other> doesn't have to be a direct child of C<$dir>,
1328             it just has to be subsumed after both paths have been resolved.
1329              
1330             =item $foreign = $dir->as_foreign($type)
1331              
1332             Returns a C<App::GitFind::PathClassMicro::Dir> object representing C<$dir> as it would
1333             be specified on a system of type C<$type>. Known types include
1334             C<Unix>, C<Win32>, C<Mac>, C<VMS>, and C<OS2>, i.e. anything for which
1335             there is a subclass of C<File::Spec>.
1336              
1337             Any generated objects (subdirectories, files, parents, etc.) will also
1338             retain this type.
1339              
1340             =item $foreign = App::GitFind::PathClassMicro::Dir->new_foreign($type, @args)
1341              
1342             Returns a C<App::GitFind::PathClassMicro::Dir> object representing C<$dir> as it would
1343             be specified on a system of type C<$type>. Known types include
1344             C<Unix>, C<Win32>, C<Mac>, C<VMS>, and C<OS2>, i.e. anything for which
1345             there is a subclass of C<File::Spec>.
1346              
1347             The arguments in C<@args> are the same as they would be specified in
1348             C<new()>.
1349              
1350             =item @list = $dir->dir_list([OFFSET, [LENGTH]])
1351              
1352             Returns the list of strings internally representing this directory
1353             structure. Each successive member of the list is understood to be an
1354             entry in its predecessor's directory list. By contract, C<<
1355             App::GitFind::PathClassMicro->new( $dir->dir_list ) >> should be equivalent to C<$dir>.
1356              
1357             The semantics of this method are similar to Perl's C<splice> or
1358             C<substr> functions; they return C<LENGTH> elements starting at
1359             C<OFFSET>. If C<LENGTH> is omitted, returns all the elements starting
1360             at C<OFFSET> up to the end of the list. If C<LENGTH> is negative,
1361             returns the elements from C<OFFSET> onward except for C<-LENGTH>
1362             elements at the end. If C<OFFSET> is negative, it counts backward
1363             C<OFFSET> elements from the end of the list. If C<OFFSET> and
1364             C<LENGTH> are both omitted, the entire list is returned.
1365              
1366             In a scalar context, C<dir_list()> with no arguments returns the
1367             number of entries in the directory list; C<dir_list(OFFSET)> returns
1368             the single element at that offset; C<dir_list(OFFSET, LENGTH)> returns
1369             the final element that would have been returned in a list context.
1370              
1371             =item $dir->components
1372              
1373             Identical to C<dir_list()>. It exists because there's an analogous
1374             method C<dir_list()> in the C<App::GitFind::PathClassMicro::File> class that also
1375             returns the basename string, so this method lets someone call
1376             C<components()> without caring whether the object is a file or a
1377             directory.
1378              
1379             =item (REMOVED) $fh = $dir->open()
1380              
1381             Passes C<$dir> to C<< IO::Dir->open >> and returns the result as an
1382             L<IO::Dir> object. If the opening fails, C<undef> is returned and
1383             C<$!> is set.
1384              
1385             =item (REMOVED) $dir->mkpath($verbose, $mode)
1386              
1387             Passes all arguments, including C<$dir>, to C<< File::Path::mkpath()
1388             >> and returns the result (a list of all directories created).
1389              
1390             =item (REMOVED) $dir->rmtree($verbose, $cautious)
1391              
1392             Passes all arguments, including C<$dir>, to C<< File::Path::rmtree()
1393             >> and returns the result (the number of files successfully deleted).
1394              
1395             =item $dir->remove()
1396              
1397             Removes the directory, which must be empty. Returns a boolean value
1398             indicating whether or not the directory was successfully removed.
1399             This method is mainly provided for consistency with
1400             C<App::GitFind::PathClassMicro::File>'s C<remove()> method.
1401              
1402             =item (REMOVED) $dir->tempfile(...)
1403              
1404             An interface to L<File::Temp>'s C<tempfile()> function. Just like
1405             that function, if you call this in a scalar context, the return value
1406             is the filehandle and the file is C<unlink>ed as soon as possible
1407             (which is immediately on Unix-like platforms). If called in a list
1408             context, the return values are the filehandle and the filename.
1409              
1410             The given directory is passed as the C<DIR> parameter.
1411              
1412             Here's an example of pretty good usage which doesn't allow race
1413             conditions, won't leave yucky tempfiles around on your filesystem,
1414             etc.:
1415              
1416             my $fh = $dir->tempfile;
1417             print $fh "Here's some data...\n";
1418             seek($fh, 0, 0);
1419             while (<$fh>) { do something... }
1420              
1421             Or in combination with a C<fork>:
1422              
1423             my $fh = $dir->tempfile;
1424             print $fh "Here's some more data...\n";
1425             seek($fh, 0, 0);
1426             if ($pid=fork()) {
1427             wait;
1428             } else {
1429             something($_) while <$fh>;
1430             }
1431              
1432              
1433             =item $dir_or_file = $dir->next()
1434              
1435             A convenient way to iterate through directory contents. The first
1436             time C<next()> is called, it will C<open()> the directory and read the
1437             first item from it, returning the result as a C<App::GitFind::PathClassMicro::Dir> or
1438             L<App::GitFind::PathClassMicro::File> object (depending, of course, on its actual
1439             type). Each subsequent call to C<next()> will simply iterate over the
1440             directory's contents, until there are no more items in the directory,
1441             and then the undefined value is returned. For example, to iterate
1442             over all the regular files in a directory:
1443              
1444             while (my $file = $dir->next) {
1445             next unless -f $file;
1446             my $fh = $file->open('r') or die "Can't read $file: $!";
1447             ...
1448             }
1449              
1450             If an error occurs when opening the directory (for instance, it
1451             doesn't exist or isn't readable), C<next()> will throw an exception
1452             with the value of C<$!>.
1453              
1454             =item $dir->traverse( sub { ... }, @args )
1455              
1456             Calls the given callback for the root, passing it a continuation
1457             function which, when called, will call this recursively on each of its
1458             children. The callback function should be of the form:
1459              
1460             sub {
1461             my ($child, $cont, @args) = @_;
1462             # ...
1463             }
1464              
1465             For instance, to calculate the number of files in a directory, you
1466             can do this:
1467              
1468             my $nfiles = $dir->traverse(sub {
1469             my ($child, $cont) = @_;
1470             return sum($cont->(), ($child->is_dir ? 0 : 1));
1471             });
1472              
1473             or to calculate the maximum depth of a directory:
1474              
1475             my $depth = $dir->traverse(sub {
1476             my ($child, $cont, $depth) = @_;
1477             return max($cont->($depth + 1), $depth);
1478             }, 0);
1479              
1480             You can also choose not to call the callback in certain situations:
1481              
1482             $dir->traverse(sub {
1483             my ($child, $cont) = @_;
1484             return if -l $child; # don't follow symlinks
1485             # do something with $child
1486             return $cont->();
1487             });
1488              
1489             =item $dir->traverse_if( sub { ... }, sub { ... }, @args )
1490              
1491             traverse with additional "should I visit this child" callback.
1492             Particularly useful in case examined tree contains inaccessible
1493             directories.
1494              
1495             Canonical example:
1496              
1497             $dir->traverse_if(
1498             sub {
1499             my ($child, $cont) = @_;
1500             # do something with $child
1501             return $cont->();
1502             },
1503             sub {
1504             my ($child) = @_;
1505             # Process only readable items
1506             return -r $child;
1507             });
1508              
1509             Second callback gets single parameter: child. Only children for
1510             which it returns true will be processed by the first callback.
1511              
1512             Remaining parameters are interpreted as in traverse, in particular
1513             C<traverse_if(callback, sub { 1 }, @args> is equivalent to
1514             C<traverse(callback, @args)>.
1515              
1516             =item $dir->recurse( callback => sub {...} )
1517              
1518             Iterates through this directory and all of its children, and all of
1519             its children's children, etc., calling the C<callback> subroutine for
1520             each entry. This is a lot like what the L<File::Find> module does,
1521             and of course C<File::Find> will work fine on L<App::GitFind::PathClassMicro> objects,
1522             but the advantage of the C<recurse()> method is that it will also feed
1523             your callback routine C<App::GitFind::PathClassMicro> objects rather than just pathname
1524             strings.
1525              
1526             The C<recurse()> method requires a C<callback> parameter specifying
1527             the subroutine to invoke for each entry. It will be passed the
1528             C<App::GitFind::PathClassMicro> object as its first argument.
1529              
1530             C<recurse()> also accepts two boolean parameters, C<depthfirst> and
1531             C<preorder> that control the order of recursion. The default is a
1532             preorder, breadth-first search, i.e. C<< depthfirst => 0, preorder => 1 >>.
1533             At the time of this writing, all combinations of these two parameters
1534             are supported I<except> C<< depthfirst => 0, preorder => 0 >>.
1535              
1536             C<callback> is normally not required to return any value. If it
1537             returns special constant C<App::GitFind::PathClassMicro::Entity::PRUNE()> (more easily
1538             available as C<< $item->PRUNE >>), no children of analyzed
1539             item will be analyzed (mostly as if you set C<$File::Find::prune=1>). Of course
1540             pruning is available only in C<preorder>, in postorder return value
1541             has no effect.
1542              
1543             =item $st = $file->stat()
1544              
1545             Invokes C<< File::stat::stat() >> on this directory and returns a
1546             C<File::stat> object representing the result.
1547              
1548             MODIFIED: returns an arrayref of C<stat()> results.
1549              
1550             =item $st = $file->lstat()
1551              
1552             Same as C<stat()>, but if C<$file> is a symbolic link, C<lstat()>
1553             stats the link instead of the directory the link points to.
1554              
1555             MODIFIED: returns an arrayref of C<lstat()> results.
1556              
1557             =item $class = $file->file_class()
1558              
1559             Returns the class which should be used to create file objects.
1560              
1561             Generally overridden whenever this class is subclassed.
1562              
1563             =back
1564              
1565             =head1 AUTHOR
1566              
1567             Ken Williams, kwilliams@cpan.org
1568              
1569             =head1 SEE ALSO
1570              
1571             L<Path::Class>, L<Path::Class::File>, L<File::Spec>
1572              
1573             =cut
1574              
1575              
1576             # }}}1
1577             # vi: set fdm=marker: #