1#========================================================================
2#
3# Badger::Filesystem
4#
5# DESCRIPTION
6#   OO representation of a filesystem.
7#
8# AUTHOR
9#   Andy Wardley   <abw@wardley.org>
10#
11#========================================================================
12
13package Badger::Filesystem;
14
15use File::Spec;
16use Cwd 'getcwd';
17use Badger::Class
18    version   => 0.01,
19    debug     => 0,
20    base      => 'Badger::Prototype Badger::Filesystem::Base',
21    import    => 'class',
22    utils     => 'params is_object random_name',
23    constants => 'HASH ARRAY TRUE REFS PKG',
24    constant  => {
25        virtual     => 0,
26        NO_FILENAME => 1,
27        FILESPEC    => 'File::Spec',
28        FINDBIN     => 'FindBin',
29        ROOTDIR     =>  File::Spec->rootdir,
30        CURDIR      =>  File::Spec->curdir,
31        UPDIR       =>  File::Spec->updir,
32        FS          => 'Badger::Filesystem',
33        VFS         => 'Badger::Filesystem::Virtual',
34        UFS         => 'Badger::Filesystem::Universal',
35        PATH        => 'Badger::Filesystem::Path',
36        FILE        => 'Badger::Filesystem::File',
37        DIRECTORY   => 'Badger::Filesystem::Directory',
38        VISITOR     => 'Badger::Filesystem::Visitor',
39    },
40    exports   => {
41        any         => 'FS PATH FILE DIR DIRECTORY cwd getcwd rel2abs abs2rel',
42        tags        => {
43            types   => 'Path File Dir Directory Cwd Bin',
44            dirs    => 'ROOTDIR UPDIR CURDIR',
45        },
46        hooks       => {
47            VFS     => sub {
48                # load VFS module and call its export() method
49                class(shift->VFS)->load->pkg->export(shift, shift)
50            },
51            UFS     => sub {
52                # load UFS module and call its export() method
53                class(shift->UFS)->load->pkg->export(shift, shift)
54            },
55            '$Bin'  => \&_export_findbin_hook,
56        },
57    },
58    messages  => {
59        open_failed   => 'Failed to open %s %s: %s',
60        delete_failed => 'Failed to delete %s %s: %s',
61        bad_volume    => 'Volume mismatch: %s vs %s',
62        bad_stat      => 'Nothing known about %s',
63        copy_failed   => 'Failed to %s file from %s to %s: %s',
64        no_path       => 'Unable to determine location of %s',
65    };
66
67use Badger::Filesystem::File;
68use Badger::Filesystem::Directory;
69
70#-----------------------------------------------------------------------
71# special export hooks to make $Bin available from FindBin
72#-----------------------------------------------------------------------
73
74sub _export_findbin_hook {
75    my ($class, $target) = @_;
76    class($class->FINDBIN)->load;
77    $class->export_symbol($target, Bin => \$FindBin::Bin);
78};
79
80
81#-----------------------------------------------------------------------
82# aliases
83#-----------------------------------------------------------------------
84
85*DIR          = \&DIRECTORY;              # constant class name
86*Dir          = \&Directory;              # constructor sub
87*dir          = \&directory;              # object method
88*split_dir    = \&split_directory;        # ...because typing 'directory'
89*join_dir     = \&join_directory;         #    gets tedious quickly
90*collapse_dir = \&collapse_directory;
91*dir_exists   = \&directory_exists;
92*create_dir   = \&create_directory;
93*delete_dir   = \&delete_directory;
94*open_dir     = \&open_directory;
95*read_dir     = \&read_directory;
96*temp_dir     = \&temp_directory;
97*dir_child    = \&directory_child;
98*dir_children = \&directory_children;
99*mkdir        = \&create_directory;
100*rmdir        = \&delete_directory;
101*touch        = \&touch_file;
102
103
104#-----------------------------------------------------------------------
105# In this base class definitive paths are the same as absolute paths.
106# However, in subclasses (like Badger::Filesystem::Virtual) we want
107# to differentiate between absolute paths in a virtual filesystem
108# (e.g. /about/badger.html) and the definitive paths that they map to
109# in a real file system (e.g. /home/abw/web/badger/about/badger.html).
110# We make the further distinction between definitive paths used for
111# reading or writing, and call the appropriate method to perform any
112# virtual -> real mapping before operating on any file or directory.
113# But like I said, these are just hooks for subclasses to use if they
114# need them.  In the base class, we patch them straight into the plain
115# old absolute() method.
116#-----------------------------------------------------------------------
117
118*definitive       = \&absolute;
119*definitive_read  = \&absolute;
120*definitive_write = \&absolute;
121
122
123#-----------------------------------------------------------------------
124# factory subroutines
125#-----------------------------------------------------------------------
126
127sub Path {
128    return PATH unless @_;
129    return @_ == 1 && is_object(PATH, $_[0])
130        ? $_[0]                                 # return existing Path object
131        : FS->path(@_);                         # or construct a new one
132}
133
134sub File {
135    return FILE unless @_;
136    return @_ == 1 && is_object(FILE, $_[0])
137        ? $_[0]                                 # ditto for File object
138        : FS->file(@_);
139}
140
141sub Directory {
142    return DIRECTORY unless @_;
143    return @_ == 1 && is_object(DIRECTORY, $_[0])
144        ? $_[0]                                 # ditto for Directory object
145        : FS->directory(@_);
146}
147
148sub Cwd {
149    FS->directory
150}
151
152sub Bin {
153    class(FINDBIN)->load;
154    FS->directory($FindBin::Bin);
155}
156
157
158#-----------------------------------------------------------------------
159# generated methods
160#-----------------------------------------------------------------------
161
162class->methods(
163    # define methods for path/root/updir/curdir that access a prototype
164    # object when called as class methods.
165    map {
166        my $name = $_;      # fresh copy of lexical for binding in closure
167        $name => sub {
168            $_[0]->prototype->{ $name };
169        }
170    }
171    qw( rootdir updir curdir separator )
172);
173
174
175#-----------------------------------------------------------------------
176# constructor methods
177#-----------------------------------------------------------------------
178
179sub init {
180    my ($self, $config) = @_;
181
182    # NEW CODE: trying to abstract out the file specification so that I
183    # can slot in a Universal file spec decoy which always generates URIs
184    my $spec = $self->{ spec }
185             = $config->{ spec }
186            || $config->{ filespec }
187            || $self->FILESPEC;
188
189    $self->debug("spec is $spec") if DEBUG;
190
191    # The tokens used to represent the root directory ('/'), the
192    # parent directory ('..') and current directory ('.') default to
193    # constants grokked from File::Spec.  To determine the path separator
194    # we have to resort to an ugly hack.  The File::Spec module hard-codes
195    # the path separator in the catdir() method so we have to make a round-
196    # trip through catdir() to grok the separator in a cross-platform manner
197    $self->{ rootdir   } = $config->{ rootdir   } || $spec->rootdir;
198    $self->{ updir     } = $config->{ updir     } || $spec->updir;
199    $self->{ curdir    } = $config->{ curdir    } || $spec->curdir;
200    $self->{ separator } = $config->{ separator } || do {
201        my $sep = FILESPEC->catdir(('badger') x 2);
202        $sep =~ s/badger//g;
203        $sep;
204    };
205
206    # flag to indicate if directory scans should return all entries
207    $self->{ all_entries } = $config->{ all_entries } || 0;
208
209    # current working can be specified explicitly, otherwise we leave it
210    # undefined and let cwd() call getcwd() determine it dynamically
211    $self->{ cwd } = $config->{ cwd };
212
213    # additional options, e.g. codec, encoding
214    $self->init_options($config);
215
216    return $self;
217}
218
219sub spec {
220    return ref $_[0] eq HASH
221        ? $_[0]->{ spec }
222        : FILESPEC;
223}
224
225sub path {
226    Path->new( shift->_child_args( path => @_ ) );
227}
228
229sub file {
230    File->new( shift->_child_args( file => @_ ) );
231}
232
233sub directory {
234    my $self = shift;
235    my $args = $self->_child_args( directory => @_ );
236
237    # default directory is the current working directory
238    $args->{ path } = $self->cwd
239        if exists $args->{ path } && ! defined $args->{ path };
240
241    Directory->new($args);
242}
243
244sub root {
245    my $self = shift->prototype;
246    $self->directory($self->{ rootdir });
247}
248
249sub cwd {
250    my $cwd;
251    if (@_) {
252        # called as an object or class method
253        my $self = shift->prototype;
254        # if we have a hard-coded cwd set then return that, otherwise call
255        # getcwd to return the real current working directory.  NOTE: we don't
256        # cache the dynamically resolved cwd as it'll change if chdir() is called
257        $cwd = $self->{ cwd } || getcwd;
258    }
259    else {
260        # called as a subroutine
261        $cwd = getcwd;
262    }
263    # pass through File::Spec to sanitise path to local filesystem
264    # convention - otherwise we get /forward/slashes on Win32
265    FILESPEC->canonpath($cwd);
266}
267
268
269#-----------------------------------------------------------------------
270# path manipulation methods
271#-----------------------------------------------------------------------
272
273sub merge_paths {
274    my ($self, $base, $path) = @_;
275    my $spec = $self->spec;
276    my @p1   = $spec->splitpath($base);
277    my @p2   = $spec->splitpath($path);
278
279    # check volumes match
280    if (defined $p2[0] and length $p2[0]) {
281        $p1[0] ||= $p2[0];
282        return $self->error_msg( bad_volume => $p1[0], $p1[0] )
283            unless $p1[0] eq $p2[0];
284    }
285    shift(@p2);
286    my $vol = shift(@p1) || '';
287    my $file = pop @p2;
288
289    $spec->catpath($vol, $spec->catdir(@p1, @p2), $file);
290}
291
292sub join_path {
293    my $self = shift;
294    my @args = map { defined($_) ? $_ : '' } @_[0..2];
295    my $spec = $self->spec;
296    $spec->canonpath( $spec->catpath(@args) );
297}
298
299sub join_directory {
300    my $self = shift;
301    my $dir  = @_ == 1 ? shift : [ @_ ];
302    my $spec = $self->spec;
303    $self->debug("join_dir(", ref $dir eq ARRAY ? '[' . join(', ', @$dir) . ']' : $dir, ")\n") if $DEBUG;
304    ref $dir eq ARRAY
305        ? $spec->catdir(@$dir)
306        : $spec->canonpath($dir);
307}
308
309sub split_path {
310    my $self  = shift;
311    my $path  = $self->join_directory(@_);
312    my @split = map { defined($_) ? $_ : '' } $self->spec->splitpath($path);
313    $self->debug("split_path($path) => ", join(', ', @split), "\n") if $DEBUG;
314    return wantarray ? @split : \@split;
315}
316
317sub split_directory {
318    my $self  = shift;
319    my $path  = $self->join_directory(@_);
320    my @split = $self->spec->splitdir($path);
321    return wantarray ? @split : \@split;
322}
323
324sub collapse_directory {
325    my $self = shift->prototype;
326    my @dirs = $self->split_directory(shift);
327    my ($up, $cur) = @$self{qw( updir curdir )};
328    my ($node, @path);
329    while (@dirs) {
330        $node = shift @dirs;
331        if ($node eq $cur) {
332            # do nothing
333        }
334        elsif ($node eq $up) {
335            pop @path if @path;
336        }
337        else {
338            push(@path, $node);
339        }
340    }
341    $self->join_directory(@path);
342}
343
344sub slash_directory {
345    my $self  = shift->prototype;
346    my $path  = $self->absolute(shift);
347    my $slash = $self->{ slashed } ||= do {
348        my $sep = quotemeta $self->{ separator };
349        qr/$sep$/;
350    };
351    $path .= $self->{ separator } unless $path =~ $slash;
352    return $path;
353}
354
355
356#-----------------------------------------------------------------------
357# absolute and relative path tests and transmogrifiers
358#-----------------------------------------------------------------------
359
360sub is_absolute {
361    my $self = shift;
362#    $self->debug("args: ", $self->dump_data(\@_));
363    $self->spec->file_name_is_absolute(
364        $self->join_directory(@_)
365    ) ? 1 : 0;
366}
367
368sub is_relative {
369    shift->is_absolute(@_) ? 0 : 1;
370}
371
372sub absolute {
373    my $self = shift;
374    my $path = $self->join_directory(shift);
375    my $spec = $self->spec;
376    return $path if $spec->file_name_is_absolute($path);
377    $spec->catdir(shift || $self->cwd, $path);
378}
379
380sub relative {
381    my $self = shift;
382    $self->spec->abs2rel($self->join_directory(shift), shift || $self->cwd);
383}
384
385
386#-----------------------------------------------------------------------
387# file/directory test methods
388#-----------------------------------------------------------------------
389
390sub path_exists {
391    shift->stat_path(@_);
392}
393
394sub file_exists {
395    my $self  = shift;
396    my $stats = $self->stat_path(shift) || return;
397    return -f _ ? $stats : 0;       # relies on cached stat
398}
399
400sub directory_exists {
401    my $self = shift;
402    my $stats = $self->stat_path(shift) || return;
403    return -d _ ? $stats : 0;       # relies on cached stat
404}
405
406sub stat_path {
407    my $self  = shift;
408    my $path  = $self->definitive_read(shift) || return;
409    my @stats = (stat($path), -r _, -w _, -x _, -o _, $path);
410
411    return $self->error_msg( bad_stat => $self->{ path } )
412        unless @stats;
413
414    return wantarray
415        ?  @stats
416        : \@stats;
417}
418
419sub chmod_path {
420    my $self = shift;
421    my $path = $self->definitive_write(shift);
422    chmod(shift, $path);
423}
424
425
426#-----------------------------------------------------------------------
427# file manipulation methods
428#-----------------------------------------------------------------------
429
430sub create_file {
431    my ($self, $path) = @_;
432    unless (-e $self->definitive_write($path)) {
433        $self->write_file($path); # calls definitive_write again
434    }
435    return 1;
436}
437
438sub touch_file {
439    my ($self, $path) = @_;
440    my $definitive = $self->definitive_write($path);
441    if (-e $definitive) {
442        my $now = time();
443        utime $now, $now, $definitive;
444    }
445    else {
446        $self->write_file($path); # calls definitive_write again
447    }
448}
449
450sub delete_file {
451    my $self = shift;
452    my $path = $self->definitive_write(shift);
453    unlink($path)
454        || return $self->error_msg( delete_failed => file => $path => $! );
455}
456
457sub open_file {
458    my $self = shift;
459    my $name = shift;
460    my $mode = $_[0] || 'r';            # leave it in @_ for IO::File
461    my $opts = @_ && ref $_[-1] eq HASH ? pop(@_) : { };
462    my $path = $mode eq 'r'
463        ? $self->definitive_read($name)
464        : $self->definitive_write($name);
465    return $self->error_msg( no_path => $name )
466        unless defined $path && length $path;
467
468    require IO::File;
469    $self->debug("about to open file $path (", join(', ', @_), ")\n") if $DEBUG;
470
471    my $fh = IO::File->new($path, @_)
472        || $self->error_msg( open_failed => file => $path => $! );
473
474    $fh->binmode( $opts->{ encoding } )
475        if $opts->{ encoding };
476
477    return $fh;
478}
479
480sub read_file {
481    my $self = shift;
482    my $opts = @_ && ref $_[-1] eq HASH ? pop(@_) : { };
483    my $fh   = $self->open_file(shift, 'r', $opts);
484    return wantarray
485        ? <$fh>
486        : do { local $/ = undef; <$fh> };
487}
488
489sub write_file {
490    my $self = shift;
491    my $opts = @_ && ref $_[-1] eq HASH ? pop(@_) : { };
492    my $fh   = $self->open_file(shift, 'w', $opts);
493    return $fh unless @_;           # return handle if no args
494    print $fh @_;                   # or print args and close
495    $fh->close;
496    return 1;
497}
498
499sub append_file {
500    my $self = shift;
501    my $opts = @_ && ref $_[-1] eq HASH ? pop(@_) : { };
502    my $fh   = $self->open_file(shift, 'a', $opts);
503    return $fh unless @_;           # return handle if no args
504    print $fh @_;                   # or print args and close
505    $fh->close;
506    return 1;
507}
508
509
510sub copy_file {
511    shift->_file_copy( copy => @_ );
512}
513
514sub move_file {
515    shift->_file_copy( move => @_ );
516}
517
518sub _file_copy {
519    require File::Copy;
520
521    my ($self, $action, $from, $to, $params)
522     = (shift, shift, shift, shift, params(@_));
523
524    my $src
525        = is_object(PATH, $from)    ? $from->definitive     # path object
526        : ref($from)                ? $from                 # file handle
527        : $self->definitive_read($from);                    # file path
528
529    my $dest
530        = is_object(PATH, $to)      ? $to->definitive       # as above
531        : ref($to)                  ? $to
532        : $self->definitive_write($to);
533
534    my $code
535        = $action eq 'copy' ? \&File::Copy::copy
536        : $action eq 'move' ? \&File::Copy::move
537        : return $self->error( invalid => action => $action );
538
539    my $file;
540
541   unless (ref $dest) {
542        # NOTE: don't use $self->file($dest) because $self could be a
543        # VFS and $dest is already a definitive path
544        $file = File($dest);
545        # capture our current working directory
546        my $cwd = cwd;
547        eval {
548            # Change to the destination volume if one exists.
549            # Should work for any volume except Windows shares
550            # where resulting behavior is version dependent.
551            chdir $file->volume if ($file->volume);
552            # this code strips volume information
553            $file->directory->must_exist(
554                $params->{ mkdir    },
555                $params->{ dir_mode },
556            );
557            # change back to the current working directory
558            chdir $cwd;
559        } or do {
560            # capture any exception from above
561            # change back to the oringial cwd
562            # and rethrow the execption.
563            if ($@) {
564                chdir $cwd;
565                die $@;
566            }
567        }
568    }
569
570    $code->($src, $dest)
571        || return $self->error_msg( copy_failed => $action, $from, $to, $! );
572
573    my $mode = $params->{ file_mode };
574       $mode = $params->{ mode } unless defined $mode;
575
576    $file->chmod($mode)
577        if $file && defined $mode;
578
579    return $file || $dest;
580}
581
582
583#-----------------------------------------------------------------------
584# directory manipulation methods
585#-----------------------------------------------------------------------
586
587sub create_directory {
588    my $self   = shift;
589    my $path   = $self->definitive_write(shift);
590
591    require File::Path;
592
593    eval {
594        local $Carp::CarpLevel = 1;
595        File::Path::mkpath($path, 0, @_)
596    } || return $self->error($@);
597}
598
599sub delete_directory {
600    my $self = shift;
601    my $path = $self->definitive_write(shift);
602
603    require File::Path;
604    File::Path::rmtree($path, @_)
605}
606
607sub open_directory {
608    my $self = shift;
609    my $path = $self->definitive_read(shift);
610
611    require IO::Dir;
612    $self->debug("Opening directory: $path\n") if $DEBUG;
613
614    return IO::Dir->new($path, @_)
615        || $self->error_msg( open_failed => directory => $path => $! );
616}
617
618sub read_directory {
619    my $self = shift;
620    my $dirh = $self->open_directory(shift);
621    my $all  = shift;
622    my ($path, @paths);
623    while (defined ($path = $dirh->read)) {
624        push(@paths, $path);
625    }
626    @paths = $self->spec->no_upwards(@paths)
627        unless $all || ref $self && $self->{ all_entries };
628
629    $dirh->close;
630    return wantarray ? @paths : \@paths;
631}
632
633sub directory_child {
634    my $self = shift;
635    my $path = $self->join_directory(@_);
636    stat $self->definitive_read($path);
637    -d _ ? $self->directory($path) :
638    -f _ ? $self->file($path) :
639           $self->path($path);
640}
641
642sub directory_children {
643    my $self  = shift;
644    my $dir   = shift;
645    my @paths = map {
646        $self->directory_child($dir, $_)
647    }   $self->read_directory($dir, @_);
648    return wantarray ? @paths : \@paths;
649}
650
651
652#-----------------------------------------------------------------------
653# temporary directory/file methods
654#-----------------------------------------------------------------------
655
656sub temp_directory {
657    my $self = shift;
658    return $self->directory( FILESPEC->tmpdir, @_ )->must_exist(1);
659}
660
661sub temp_file {
662    my $self = shift;
663    return $self->temp_directory->file( @_ ? @_ : random_name() )
664}
665
666
667#-----------------------------------------------------------------------
668# visitor methods
669#-----------------------------------------------------------------------
670
671sub visitor {
672    my $self  = shift;
673    my $vtype = $self->VISITOR;
674    class($vtype)->load;
675
676    return @_ && is_object($vtype => $_[0])
677        ? shift
678        : $vtype->new(@_);
679}
680
681sub visit {
682    shift->root->visit(@_);
683}
684
685sub collect {
686    shift->visit(@_)->collect;
687}
688
689sub accept {
690    shift->root->accept(@_);
691}
692
693#-----------------------------------------------------------------------
694# internal methods
695#-----------------------------------------------------------------------
696
697sub _child_args {
698    my $self = shift->prototype;
699    my $type = shift;
700    my $args = { %{ $self->{ options } } };
701
702    if (@_ && ref $_[-1] eq HASH) {
703        my $more = pop @_;
704        @$args{ keys %$more } = values %$more;
705    }
706
707    if (@_ > 1) {
708        $args->{ path } = [@_];
709    }
710    elsif (@_ == 1) {
711        $args->{ path } = shift;
712    }
713    else {
714        $args->{ path } = undef;
715    }
716
717    $args->{ filesystem } = $self;
718    return $args;
719}
720
721
722
7231;
724
725__END__
726
727=head1 NAME
728
729Badger::Filesystem - filesystem functionality
730
731=head1 SYNOPSIS
732
733The C<Badger::Filesystem> module defines a number of importable constructor
734functions for creating objects that represents files, directories and generic
735paths in a filesystem.
736
737    use Badger::Filesystem 'cwd Cwd Path File Dir Directory';
738    use Badger::Filesystem 'cwd :types';        # same thing
739
740    # cwd returns current working directory as text string,
741    # Cwd return it as a Badger::Filesystem::Directory object
742    print cwd;                                  # /path/to/cwd
743    print Cwd->parent;                          # /path/to
744
745    # create Badger::Filesystem::Path/File/Directory objects using
746    # native OS-specific paths:
747    $path = Path('/path/to/file/or/dir');
748    $file = File('/path/to/file');
749    $dir  = Dir('/path/to/directory');           # short name
750    $dir  = Directory('/path/to/directory');     # long name
751
752    # or generic OS-independant paths
753    $path = File('path', 'to', 'file', 'or', 'dir');
754    $file = File('path', 'to', 'file');
755    $dir  = Dir('path', 'to', 'directory');
756    $dir  = Directory('path', 'to', 'directory');
757
758These constructor functions are simply shortcuts to C<Badger::Filesystem>
759class methods.
760
761    use Badger::Filesystem;
762
763    # we'll just show native paths from now on for brevity
764    $path = Badger::Filesystem->path('/path/to/file/or/dir');
765    $file = Badger::Filesystem->file('/path/to/file');
766    $dir  = Badger::Filesystem->dir('/path/to/directory');
767
768    # 'FS' is an alias for 'Badger::Filesystem' 4 lzy ppl lk me
769    use Badger::Filesystem 'FS'
770
771    $path = FS->path('/path/to/file/or/dir');
772    $file = FS->file('/path/to/file');
773    $dir  = FS->dir('/path/to/directory');
774
775You can also create C<Badger::Filesystem> objects.
776
777    my $fs = Badger::Filesystem->new;
778
779    $path = $fs->path('/path/to/file/or/dir');
780    $file = $fs->file('/path/to/file');
781    $dir  = $fs->dir('/path/to/directory');
782
783=head1 INTRODUCTION
784
785This is the documentation for the C<Badger::Filesystem> module. You probably
786don't need to read it.  If you're looking for an easy way to access and
787manipulate files and directories, then all you need to know to get started
788is this:
789
790    use Badger::Filesystem 'File Dir';
791
792    my $file = File('/path/to/file');       # Badger::Filesystem::File
793    my $dir  = Dir('/path/to/directory');   # Badger::Filesystem::Directory
794
795The L<File()> and L<Dir()> subroutines are used to create
796L<Badger::Filesystem::File> and L<Badger::Filesystem::Directory> objects. You
797should read the documentation for those modules first as they cover pretty
798much everything you need to know about working with files and directories for
799simple day-to-day tasks.  In fact, you should start with the documentation
800for L<Badger::Filesystem::Path> because that's the base class for both of
801them.
802
803If you want to do something a little more involved than inspecting, reading
804and writing files, or if you want to find out more about the filesystem
805functionality hidden behind the file and directory objects, then read on!
806
807=head1 DESCRIPTION
808
809The C<Badger::Filesystem> module defines an object class for accessing and
810manipulating files and directories in a file system. It provides a number of
811methods that encapsulate the behaviours of various other filesystem related
812modules, including L<File::Spec>, L<File::Path>, L<IO::File>, L<IO::Dir> and
813L<Cwd>. For example:
814
815    # path manipulation
816    my $dir  = Badger::Filesystem->join_dir('foo', 'bar', 'baz');
817    my @dirs = Badger::Filesystem->split_dir('foo/bar/baz');
818
819    # path inspection
820    Badger::Filesystem->is_relative('foo/bar/baz');     # true
821    Badger::Filesystem->is_absolute('foo/bar/baz');     # false
822
823    # file manipulation
824    Badger::Filesystem->write_file('/path/to/file', 'Hello World');
825    Badger::Filesystem->delete_file('/path/to/file')
826
827    # directory manipulation
828    Badger::Filesystem->cwd;
829    Badger::Filesystem->mkdir('/path/to/dir')
830
831If you get tired of writing C<Badger::Filesystem> over and over again,
832you can import the C<FS> symbol which is an alias to it (or you can define
833your own alias of course).
834
835    use Badger::Filesystem 'FS';
836
837    FS->is_relative('foo/bar/baz');     # true
838    FS->is_absolute('foo/bar/baz');     # false
839
840The C<Badger::Filesystem> module also defines methods that create objects to
841represent files (L<Badger::Filesystem::File>), directories
842(L<Badger::Filesystem::Directory>), and generic paths
843(L<Badger::Filesystem::Path>) that may refer to a file, directory, or a
844resource that doesn't physically exist (e.g. a URI).
845
846These are very similar (although not identical) to the corresponding
847L<Path::Class> modules which you may already be familiar with. The main
848difference between them is that C<Badger> files, directories and paths are
849I<flyweight> objects that call back to the C<Badger::Filesystem> to perform
850any filesystem operations. This gives us a more control over restricting
851certain filesystem operations (e.g. writing files) and more flexibility in
852what we define a filesystem to be (e.g. allowing virtually mounted and/or
853composite file systems - see L<Badger::Filesystem::Virtual> for further
854details).
855
856    use Badger::Filesystem 'FS';
857
858    # file manipulation - via Badger::Filesystem::File object
859    my $file = FS->file('/path/to/file');
860    print $file->size;                  # metadata
861    print $file->modified;              # more metadata
862    my $text = $file->read;             # read file content
863    $file->write("New content");        # write file content
864
865    # directory manipulation - via Badger::Filesystem::Directory object
866    my $dir = FS->directory('/path/to/dir');
867    print $dir->mode;                   # metadata
868    print $dir->modified;               # more metadata
869    my @entries = $dir->read;           # read directory entries
870    my $file = $dir->file('foo');       # fetch a file
871    my $sub  = $dir->dir('bar');        # fetch a sub-directory
872
873The module also defines the L<Path()>, L<File()> and L<Directory()>
874subroutines to easily create L<Badger::Filesystem::Path>,
875L<Badger::Filesystem::File> and L<Badger::Filesystem::Directory> objects,
876respectively. The L<Dir> subroutine is provided as an alias for L<Directory>.
877
878    use Badger::Filesystem 'Path File Dir';
879
880    my $path = Path('/any/generic/path');
881    my $file = File('/path/to/file');
882    my $dir  = Dir('/path/to/dir');
883
884These subroutines are provided as a convenient way to call the L<path()>,
885L<file()> and L<dir()> class methods. The above examples are functionally
886equivalent to those shown below.
887
888    use Badger::Filesystem;
889
890    my $path = Badger::Filesystem->path('/any/generic/path');
891    my $file = Badger::Filesystem->file('/path/to/file');
892    my $dir  = Badger::Filesystem->dir('/path/to/dir');
893
894The constructor subroutines and the corresponding methods behind them accept a
895list (or reference to a list) of path components as well as a single path
896string. This allows you to specify paths in an operating system agnostic
897manner.
898
899    # these all do the same thing (assuming you're on a Unix-like system)
900    File('/path/to/file');
901    File('path', 'to', 'file');
902    File(['path', 'to', 'file']);
903
904    # these too
905    Badger::Filesystem->file('/path/to/file');
906    Badger::Filesystem->file('path', 'to', 'file');
907    Badger::Filesystem->file(['path', 'to', 'file']);
908
909The above examples assume a Unix-like filesystem using C</> as the path
910separator. On a windows machine, for example, you would need to specify paths
911using backslashes to satisfy their brain-dead file system. However, specifying
912a list of separate path components remains portable.
913
914    # if you're stuck on windows :-(
915    File('\path\to\file');                  # OS specific
916    File('path', 'to', 'file');             # OS agnostic
917
918If you're using Perl on a windows machine then you should probably consider
919getting a new machine. Try a nice shiny Mac, or an Ubuntu box. Go on, you know
920you deserve better.
921
922You can also create a C<Badger::Filesystem> object and call object methods
923against it.
924
925    use Badger::Filesystem;
926
927    my $fs   = Badger::Filesystem->new;
928    my $file = $fs->file('/path/to/file');
929    my $dir  = $fs->dir('/path/to/dir');
930
931Creating an object allows you to define additional configuration parameters
932for the filesystem. There aren't any interesting paramters worth mentioning in
933the base class L<Badger::Filesystem> module at the moment, but subclasses
934(like L<Badger::Filesystem::Virtual>) do use them.
935
936=head1 EXPORTABLE SUBROUTINES
937
938The C<Badger::Filesystem> module defines the L<Path>, L<File> and L<Directory>
939subroutines which can be used to create L<Badger::Filesystem::Path>,
940L<Badger::Filesystem::File> and L<Badger::Filesystem::Directory> objects,
941respectively. The L<Dir> subroutine is provided as an alias for L<Directory>.
942
943To use these subroutines you must import them explicitly when you
944C<use Badger::Filesystem>.
945
946    use Badger::Filesystem 'File Dir';
947    my $file = File('/path/to/file');
948    my $dir  = Dir('/path/to/dir');
949
950You can specify multiple items in a single string as shown in the example
951above, or as multiple items in more traditional Perl style, as shown below.
952
953    use Badger::Filesystem qw(File Dir);
954
955You can pass multiple arguments to these subroutines if you want to specify
956your path in a platform-agnostic way.
957
958    my $file = File('path', 'to, 'file');
959    my $dir  = Dir('path', 'to', 'dir');
960
961A reference to a list works equally well.
962
963    my $file = File(['path', 'to, 'file']);
964    my $dir  = Dir(\@paths);
965
966If you don't provide any arguments then the subroutines return the class name
967associated with the object. For example, the L<File()> subroutine returns
968L<Badger::Filesystem::File>. This allows you to use them as virtual classes,
969(i.e. short-cuts) for the longer class names, if doing things the Object
970Oriented way is your thing.
971
972    my $file = File->new('path/to/file');
973    my $dir  = Dir->new('path/to/dir');
974
975The above examples are functionally identical to:
976
977    my $file = Badger::Filesystem::File->new('path/to/file');
978    my $dir  = Badger::Filesystem::Directory->new('path/to/dir');
979
980A summary of the constructor subroutines follows.
981
982=head2 Path(@path)
983
984Creates a new L<Badger::Filesystem::Path> object.  You can specify the
985path as a single string or list of path components.
986
987    $path = Path('/path/to/something');
988    $path = Path('path', 'to', 'something');
989
990=head2 File(@path)
991
992Creates a new L<Badger::Filesystem::File> object.  You can specify the
993path as a single string or list of path components.
994
995    $file = File('/path/to/file');
996    $file = File('path', 'to', 'file');
997
998=head2 Dir(@path) / Directory(@path)
999
1000Creates a new L<Badger::Filesystem::Directory> object.  You can specify the
1001path as a single string or list of path components.
1002
1003    $dir = Dir('/path/to/dir');
1004    $dir = Dir('path', 'to', 'dir');
1005
1006=head2 Cwd()
1007
1008This returns a L<Badger::Filesystem::Directory> object for the current
1009working directory.
1010
1011    use Badger::Filesystem 'Cwd';
1012
1013    print Cwd;              # /foraging/for/nuts/and/berries
1014    print Cwd->parent;      # /foraging/for/nuts/and
1015
1016=head2 Bin()
1017
1018This returns a L<Badger::Filesystem::Directory> object for the directory
1019in which the currently executing script is located.  It is a simple
1020wrapper around the value defined in L<$Bin>.
1021
1022    use Badger::Filesystem 'Bin';
1023
1024    print Bin;              # /path/to/current/script
1025    print Bin->parent;      # /path/to/current
1026
1027=head2 cwd()
1028
1029This returns a simple text string representing the current working directory.
1030It is a a wrapper around the C<getcwd> function in L<Cwd>.  It also
1031sanitises the path (via the L<canonpath()|Path::Spec/canonpath()> function
1032in L<File::Spec>) to ensure that the path is returned in the local
1033filesystem convention (e.g. C</> is converted to C<\> on Win32).
1034
1035=head2 $Bin
1036
1037This load the L<FindBin> module and exports the C<$Bin> variable into
1038the caller's namespace.
1039
1040    use Badger::Filesystem '$Bin';
1041    use lib "$Bin/../lib";
1042
1043This is exactly the same as:
1044
1045    use FindBin '$Bin';
1046    use lib "$Bin/../lib";
1047
1048One benefit is that you can use it in conjunction with other import options
1049to save on a little typing.  For example:
1050
1051    use Badger::Filesystem 'Cwd File $Bin';
1052
1053Compared to something like:
1054
1055    use Cwd;
1056    use Path::Class;
1057    use FindBin '$Bin';
1058    use lib "$Bin/../lib";
1059
1060=head2 getcwd()
1061
1062This is a direct alias to the C<getcwd> function in L<Cwd>.
1063
1064=head2 C<:types> Import Option
1065
1066Specifying this an an import option will export all of the L<Path()>,
1067L<File>, L<Dir>, L<Directory> and L<Cwd> subroutines to the caller.
1068
1069    use Badger::Filesystem ':types';
1070
1071    my $path   = Path('/some/where');
1072    my $dir    = Dir('/over/there');
1073    my $file   = File('example.html');
1074    my $parent = Cwd->parent;
1075
1076=head1 CONSTRUCTOR METHODS
1077
1078=head2 new(%config)
1079
1080This is a constructor method to create a new C<Badger::Filesystem> object.
1081
1082    my $fs = Badger::Filesystem->new;
1083
1084In most cases there's no need to create a C<Badger::Filesystem> object at
1085all.  You can either call class methods, like this:
1086
1087    my $file = Badger::Filesystem->file('/path/to/file');
1088
1089Or use the constructor subroutines like this:
1090
1091    use Badger::Filesystem 'File';
1092    my $file = File('/path/to/file');
1093
1094However, you might want to create a filesystem object to pass to some other
1095method or object to work with.  In that case, the C<Badger::Filesystem>
1096methods work equally well being called as object or class methods.
1097
1098You may also want to use a subclass of C<Badger::Filesystem> such as
1099L<Badger::Filesystem::Virtual> which requires configuration parameters
1100to be properly initialised.
1101
1102=head2 path(@path)
1103
1104Creates a new L<Badger::Filesystem::Path> object. This is typically used for
1105manipulating paths that don't relate to a specific file or directory in a real
1106filesystem.
1107
1108    # single path (platform specific)
1109    my $path = $fs->path('/path/to/something');
1110
1111    # list or list ref of path components (platform agnostic)
1112    my $path = $fs->path('path', 'to', 'something');
1113    my $path = $fs->path(['path', 'to', 'something']);
1114
1115=head2 file(@path)
1116
1117Creates a new L<Badger::Filesystem::File> object to represent a file in a
1118filesystem.
1119
1120    # single file path (platform specific)
1121    my $file = $fs->file('/path/to/file');
1122
1123    # list or list ref of file path components (platform agnostic)
1124    my $file = $fs->file('path', 'to', 'file');
1125    my $file = $fs->file(['path', 'to', 'file']);
1126
1127=head2 dir(@path) / directory(@path)
1128
1129Creates a new L<Badger::Filesystem::Directory> object to represent a file in a
1130filesystem.  L<dir()> is an alias for L<directory()> to save on typing.
1131
1132    # single directory path (platform specific)
1133    my $dir = $fs->dir('/path/to/directory');
1134
1135    # list or list ref of directory path components (platform agnostic)
1136    my $dir = $fs->dir('path', 'to', 'directory');
1137    my $dir = $fs->dir(['path', 'to', 'directory']);
1138
1139If you don't specify a directory path explicitly then it will default to
1140the current working directory, as returned by L<cwd()>.
1141
1142    my $cwd = $fs->dir;
1143
1144=head1 PATH MANIPULATION METHODS
1145
1146=head2 merge_paths($path1,$path2)
1147
1148Joins two paths into one.
1149
1150    $fs->merge_paths('/path/one', 'path/two');      # /path/one/path/two
1151
1152No attempt will be made to verify that the second argument is an absolute
1153path.  In fact, it is considered a feature that this method will do its
1154best to merge two paths even if they look like they shouldn't go together
1155(this is particularly relevant when using virtual filesystems - see
1156L<Badger::Filesystem::Virtual>)
1157
1158    $fs->merge_paths('/path/one', '/path/two');     # /path/one/path/two
1159
1160If either defines a volume then it will be used as the volume for the combined
1161path. If both paths define a volume then it must be the same or an error will
1162be thrown.
1163
1164    $fs->merge_paths('C:\path\one', 'path\two');    # C:\path\one\path\two
1165    $fs->merge_paths('\path\one', 'C:\path\two');   # C:\path\one\path\two
1166    $fs->merge_paths('C:\path\one', 'C:\path\two'); # C:\path\one\path\two
1167
1168=head2 split_path($path)
1169
1170Splits a composite path into volume, directory name and file name components.
1171This is a wrapper around the L<splitpath()|File::Spec/splitpath()> function
1172in L<File::Spec>.
1173
1174    ($vol, $dir, $file) = $fs->split_path($path);
1175
1176=head2 join_path($volume, $dir, $file)
1177
1178Combines a filesystem volume (where applicable), directory name and file
1179name into a single path.  This is a wrapper around the
1180L<catpath()|File::Spec/catpath()> and L<canonpath()|File::Spec/canonpath()>
1181functions in L<File::Spec>.
1182
1183    my $path = $fs->join_path($volume, $directory, $file);
1184
1185=head2 split_dir($dir) / split_directory($dir)
1186
1187Splits a directory path into individual directory names.  This is a wrapper
1188around the L<splitdir()|File::Spec/splitdir()> function in L<File::Spec>.
1189
1190    @dirs = $fs->split_dir($dir);
1191
1192=head2 join_dir(@dirs) / join_directory(@dirs)
1193
1194Combines multiple directory names into a single path.  This is a wrapper
1195around the L<catdir()|File::Spec/catdir()> function in L<File::Spec>.
1196
1197    my $dir = $fs->join_dir('path', 'to', 'my', 'dir');
1198
1199The final element can also be a file name.   TODO: is that portable?
1200
1201    my $dir = $fs->join_dir('path', 'to', 'my', 'file');
1202
1203=head2 collapse_dir($dir) / collapse_directory($dir)
1204
1205Reduces a directory to its simplest form by resolving and removing any C<.>
1206(current directory) and C<..> (parent directory) components (or whatever the
1207corresponding tokens are for the current and parent directories of your
1208filesystem).
1209
1210    print $fs->collapse_dir('/foo/bar/../baz');   # /foo/baz
1211
1212The reduction is purely syntactic. No attempt is made to verify that the
1213directories exist, or to intelligently resolve parent directory where symbolic
1214links are involved.
1215
1216Note that this may not work portably across all operating systems.  If you're
1217using a Unix-based filesystem (including Mac OSX) or MS Windows then you
1218should be OK.  If you're using an old MacOS machine (pre-OSX), VMS, or
1219something made out of clockwork, then be warned that this method is untested
1220on those platforms.
1221
1222C<collapse_dir()> is a direct alias of C<collapse_directory()> to save on
1223typing.
1224
1225=head2 slash_directory($path)
1226
1227Returns the directory L<$path> with a trailing C</> appended (or whatever
1228the directory separator is for your filesystem) if it doesn't already
1229have one.
1230
1231    print $fs->slash_directory('foo');      # foo/
1232
1233=head1 PATH INSPECTION METHODS
1234
1235=head2 is_absolute($path)
1236
1237Returns true if the path specified is absolute.  That is, if it starts
1238with a C</>, or whatever the corresponding token for the root directory is
1239for your file system.
1240
1241    $fs->is_absolute('/foo');               # true
1242    $fs->is_absolute('foo');                # false
1243
1244=head2 is_relative($path)
1245
1246Returns true if the path specified is relative. That is, if it does not start
1247with a C</>, or whatever the corresponding token for the root directory is for
1248your file system.
1249
1250    $fs->is_relative('/foo');               # false
1251    $fs->is_relative('foo');                # true
1252
1253=head1 PATH CONVERSION METHODS
1254
1255=head2 absolute($path, $base)
1256
1257Converts a relative path to an absolute one.  The path passed as an argument
1258is assumed to be relative to the current working directory unless you
1259explicitly provide a C<$base> parameter.
1260
1261    $fs->cwd;                               # /foo/bar  (for example)
1262    $fs->absolute('baz');                   # /foo/bar/baz
1263    $fs->absolute('baz', '/wam/bam');       # /wam/bam/baz
1264
1265Note how potentially confusing that last example is. The base path is the
1266I<second> argument which ends up in front of the I<first> argument.  It's
1267an unfortunately consequence of the way the parameters are ordered (the
1268optional parameter must come after the mandatory one) and can't be avoided.
1269
1270=head2 relative($path, $base)
1271
1272Converts an absolute path to a relative one.  It is assumed to be relative
1273to the current working direct unless you explicitly provide a C<$base>
1274parameter.
1275
1276    $fs->cwd;                               # /foo/bar  (for example)
1277    $fs->relative('/foo/bar/wam/bam');      # wam/bam
1278    $fs->relative('/baz/wam/bam', '/baz');  # wam/bam
1279
1280Again note that last example where
1281
1282=head2 definitive($path)
1283
1284Converts an absolute or relative path to a definitive one.  In most cases,
1285a definitive path is identical to an absolute one.
1286
1287    $fs->definitive('/foo/bar');            # /foo/bar
1288
1289However, if you're using a L<virtual filesystem|Badger::Filesystem::Virtual>
1290with a virtual root directory, then a I<definitive> path I<will> include the
1291virtual root directory, whereas a an I<absolute> path will I<not>.
1292
1293    my $vfs = Badger::Filesystem::Virtual->new( root => '/my/vfs' );
1294    $vfs->absolute('/foo/bar');              # /foo/bar
1295    $vfs->definitive('/foo/bar');            # /my/vfs/foo/bar
1296
1297The C<Badger::Filesystem> module uses definitive paths when performing any
1298operations on the file system (e.g. opening and reading files and
1299directories). You can think of absolute paths as being like conceptual URIs
1300(identifiers) and definitive paths as being like concrete URLs (locators). In
1301practice, they'll both have the same value unless unless you're using a
1302virtual file system.
1303
1304In the C<Badger::Filesystem> base class, the C<definitive()> method is
1305mapped directly to the L<definitive_write()> method.  This has no real
1306effect in this module, but provides the relevant hooks that allow the
1307L<Badger::Filesystem::Virtual> subclass to work properly.
1308
1309=head2 definitive_read($path)
1310
1311Converts an absolute or relative path to a definitive one for a read
1312operation.  See L<definitive()>.
1313
1314=head2 definitive_write($path)
1315
1316Converts an absolute or relative path to a definitive one for a write
1317operation.  See L<definitive()>.
1318
1319=head1 PATH TEST METHODS
1320
1321=head2 path_exists($path)
1322
1323Returns true if the path exists, false if not.
1324
1325=head2 file_exists($path)
1326
1327Returns true if the path exists and is a file, false if not.
1328
1329=head2 dir_exists($path) / directory_exists($path)
1330
1331Returns true if the path exists and is a directory, false if not.
1332
1333=head2 stat_path($path)
1334
1335Performs a C<stat()> on the filesystem path.  It returns a list (in list
1336context) or a reference to a list (in scalar context) containing 17 items.
1337The first 13 are those returned by Perl's inbuilt C<stat()> function.  The
1338next 3 items are flags indicating if the file is readable, writeable and/or
1339executable.  The final item is a flag indicating if the file is owned by the
1340current user (i.e. owner of the current process.
1341
1342A summary of the fields is shown below. See C<perldoc -f stat> and the
1343L<stat()|Badger::Filesystem::Path/stat()> method in
1344L<Badger::Filesystem::Path> for further details.
1345
1346    Field   Description
1347    --------------------------------------------------------
1348      0     device number of filesystem
1349      1     inode number
1350      2     file mode  (type and permissions)
1351      3     number of (hard) links to the file
1352      4     numeric user ID of file’s owner
1353      5     numeric group ID of file’s owner
1354      6     the device identifier (special files only)
1355      7     total size of file, in bytes
1356      8     last access time in seconds since the epoch
1357      9     last modify time in seconds since the epoch
1358     10     inode change time in seconds since the epoch (*)
1359     11     preferred block size for file system I/O
1360     12     actual number of blocks allocated
1361     13     file is readable by current process
1362     14     file is writeable by current process
1363     15     file is executable by current process
1364     16     file is owned by current process
1365
1366=head2 chmod_path($path)
1367
1368Changes the file permissions on a path.
1369
1370    $fs->chmod_path('/path/to/file', 0755);
1371
1372=head1 FILE MANIPULATION METHODS
1373
1374=head2 create_file($path)
1375
1376Creates an empty file if it doesn't already exist.  Returns a true value
1377if the file is created and a false value if it already exists.  Errors are
1378thrown as exceptions.
1379
1380    $fs->create_file('/path/to/file');
1381
1382=head2 touch_file($path) / touch($path)
1383
1384Creates a file if it doesn't exists, or updates the timestamp if it does.
1385
1386    $fs->touch_file('/path/to/file');
1387
1388=head2 delete_file($path)
1389
1390Deletes a file.
1391
1392    $fs->delete_file('/path/to/file');      # Careful with that axe, Eugene!
1393
1394=head2 open_file($path, $mode, $perms)
1395
1396Opens a file for reading (by default) or writing/appending (by passing
1397C<$mode> and optionally C<$perms>). Accepts the same parameters as for the
1398L<IO::File::open()|IO::File> method and returns an L<IO::File> object.
1399
1400    my $fh = $fs->open_file('/path/to/file');
1401    my $fh = $fs->open_file('/path/to/file', 'w');
1402    my $fh = $fs->open_file('/path/to/file', 'w', 0644);
1403
1404=head2 read_file($path)
1405
1406Reads the content of a file, returning it as a list of lines (in list context)
1407or a single text string (in scalar context).
1408
1409    my $text  = $fs->read_file('/path/to/file');
1410    my @lines = $fs->read_file('/path/to/file');
1411
1412=head2 write_file($path, @content)
1413
1414When called with a single C<$path> argument, this method opens the specified
1415file for writing and returns an L<IO::File> object.
1416
1417    my $fh = $fs->write_file('/path/to/file');
1418    $fh->print("Hello World!\n");
1419    $fh->close;
1420
1421If any additional C<@content> argument(s) are passed then they will be
1422written to the file.  The file is then closed and a true value returned
1423to indicate success.  Errors are thrown as exceptions.
1424
1425    $fs->write_file('/path/to/file', "Hello World\n", "Regards, Badger\n");
1426
1427=head2 append_file($path, @content)
1428
1429This method is similar to L<write_file()>, but opens the file for appending
1430instead of overwriting.  When called with a single C<$path> argument, it opens
1431the file for appending and returns an L<IO::File> object.
1432
1433    my $fh = $fs->append_file('/path/to/file');
1434    $fh->print("Hello World!\n");
1435    $fh->close;
1436
1437If any additional C<@content> argument(s) are passed then they will be
1438appended to the file.  The file is then closed and a true value returned
1439to indicate success.  Errors are thrown as exceptions.
1440
1441    $fs->append_file('/path/to/file', "Hello World\n", "Regards, Badger\n");
1442
1443=head2 copy_file($from, $to, %params)
1444
1445Copies a file from the C<$from> path to the C<$to> path, using L<File::Copy>
1446
1447    $fs->copy_file($from, $to);
1448
1449The C<$from> and C<$to> arguments can be file names, file objects, or file
1450handles.
1451
1452An optional list or reference to a hash array of named parameters can follow
1453the file names.  The C<mkdir> option can be set to indicate that
1454the destination direction should be created if it doesn't already exist,
1455along with any intermediate directories.
1456
1457    $fs->copy_file($from, $to, mkdir => 1);
1458
1459The C<dir_mode> parameter can be used to specify the octal file
1460permissions for any directories created.
1461
1462    $fs->copy_file($from, $to, 1, mkdir => 1, dir_mode => 0770);
1463
1464The C<file_mode> parameter (or C<mode> for short) can be used to specify the
1465octal file permissions for the created file.
1466
1467    $fs->copy_file($from, $to, file_mode => 0644);
1468
1469=head2 move_file($from, $to, %params)
1470
1471Moves a file from the C<$from> path to the C<$to> path, using L<File::Copy>.
1472The arguments are as per L<copy_file()>.
1473
1474=head1 DIRECTORY MANIPULATION METHODS
1475
1476=head2 create_dir($path) / create_directory($path) / mkdir($path)
1477
1478Creates the directory specified by C<$path>. Errors are thrown as exceptions.
1479
1480    $fs->create_dir('/path/to/directory');
1481
1482Additional arguments can be specified as per the L<File::Path> C<mkpath()>
1483method. NOTE: this is subject to change. Better to use C<File::Path> directly
1484for now if you're relying on this.
1485
1486=head2 delete_dir($path) / delete_directory($path) / rmdir($path)
1487
1488Deletes the directory specified by C<$path>. Errors are thrown as exceptions.
1489
1490    $fs->delete_dir('/path/to/directory');
1491
1492=head2 open_dir($path) / open_directory($path)
1493
1494Returns an L<IO::Dir> handle opened for reading a directory or throws
1495an error if the open failed.
1496
1497    my $dh = $fs->open_dir('/path/to/directory');
1498    while (defined ($path = $dh->read)) {
1499        print " - $path\n";
1500    }
1501
1502=head2 read_dir($dir, $all) / read_directory($dir, $all)
1503
1504Returns a list (in list context) or a reference to a list (in scalar context)
1505containing the entries in the directory. These are simple text strings
1506containing the names of the files and/or sub-directories in the directory.
1507
1508    my @paths = $fs->read_dir('/path/to/directory');
1509
1510By default, this excludes the current and parent entries (C<.> and C<..> or
1511whatever the equivalents are for your filesystem. Pass a true value for the
1512optional second argument to include these items.
1513
1514    my @paths = $fs->read_dir('/path/to/directory', 1);
1515
1516=head2 dir_children($dir, $all) / directory_children($dir, $all)
1517
1518Returns a list (in list context) or a reference to a list (in scalar
1519context) of objects to represent the contents of a directory.  As per
1520L<read_dir()>, the current (C<.>) and parent (C<..>) directories
1521are excluded unless you set the C<$all> flag to a true value.  Files are
1522returned as L<Badger::Filesystem::File> objects, directories as
1523L<Badger::Filesystem::File> objects.  Anything else is returned as a
1524generic L<Badger::Filesystem::Path> object.
1525
1526=head2 dir_child($path) / directory_child($path)
1527
1528Returns an object to represent a single item in a directory. Files are
1529returned as L<Badger::Filesystem::File> objects, directories as
1530L<Badger::Filesystem::File> objects. Anything else is returned as a generic
1531L<Badger::Filesystem::Path> object.
1532
1533=head1 TEMPORARY DIRECTORY AND FILE METHODS
1534
1535=head2 temp_dir($dir) / temp_directory($dir)
1536
1537This returns a reference to a L<Badger::Filesystem::Directory> object for the
1538temporary directory on your system (as reported by C<tmpdir> in L<File::Spec>).
1539
1540    my $tmp = $fs->temp_dir;
1541
1542If any arguments are specified then they are appended as sub-directories to
1543the temporary directory path.
1544
1545    my $tmp = $fs->temp_dir('foo', 'bar');  # e.g. /tmp/foo/bar
1546
1547=head2 temp_file($name)
1548
1549This returns a reference to a L<Badger::Filesystem::File> object for a named
1550file created in the temporary directory returned by the L<temp_directory()>
1551method.
1552
1553    my $file = $fs->temp_file('foo.tmp');   # e.g. /tmp/foo.tmp
1554
1555=head1 VISITOR METHODS
1556
1557=head2 visitor(\%params)
1558
1559This method creates a L<Badger::Filesystem::Visitor> object from the arguments
1560passed as a list or reference to a hash array of named parameters.
1561
1562    # list of named parameters.
1563    $fs->visitor( files => 1, dirs => 0 );
1564
1565    # reference to hash array of named parameters
1566    $fs->visitor( files => 1, dirs => 0 );
1567
1568If the first argument is already a reference to a
1569L<Badger::Filesystem::Visitor> object or subclass then it will be returned
1570unmodified.
1571
1572=head2 visit(\%params)
1573
1574This methods forwards all arguments onto the
1575L<visit()|Badger::Filesystem::Directory/visit()> method of the
1576L<root()> directory.
1577
1578=head2 accept($visitor)
1579
1580This lower-level method is called to dispatch a visitor to the correct method
1581for a filesystem object. It forward the visitor onto the
1582L<accept()|Badger::Filesystem::Directory/accept()> method for the L<root()>
1583directory.
1584
1585=head2 collect(\%params)
1586
1587This is a short-cut to call the L<visit()> method and then the
1588L<collect()|Badger::Filesystem::Visitor/collect()> method on the
1589L<Badger::Filesystem::Visitor> object returned.
1590
1591    # short form
1592    my @items = $fs->collect( files => 1, dirs => 0 );
1593
1594    # long form
1595    my @items = $fs->visit( files => 1, dirs => 0 )->collect;
1596
1597=head1 MISCELLANEOUS METHODS
1598
1599=head2 cwd()
1600
1601Returns the current working directory. This is a text string rather than a
1602L<Badger::Filesystem::Directory> object. Call the L<Cwd()> method
1603if you want a L<Badger::Filesystem::Directory> object instead.
1604
1605    my $cwd = $fs->cwd;
1606
1607=head2 root()
1608
1609Returns a L<Badger::Filesystem::Directory> object representing the root
1610directory for the filesystem.
1611
1612=head2 rootdir
1613
1614Returns a text string containing the representation of the root directory
1615for your filesystem.
1616
1617    print $fs->rootdir;             # e.g. '/' on Unix-based file systems
1618
1619=head2 updir
1620
1621Returns a text string containing the representation of the parent directory
1622for your filesystem.
1623
1624    print $fs->updir;               # e.g. '..' on Unix-based file systems
1625
1626=head2 curdir
1627
1628Returns a text string containing the representation of the current directory
1629for your filesystem.
1630
1631    print $fs->curdir;              # e.g. '.' on Unix-based file systems
1632
1633=head2 separator
1634
1635Returns a text string containing the representation of the path separator
1636for your filesystem.
1637
1638    print $fs->separator;           # e.g. '/' on Unix-based file systems
1639
1640=head2 spec
1641
1642Returns a text string containing the class name of C<File::Spec> or some
1643other user-definable module that implements the same functionality.  This
1644is used internally for splitting and joining file paths.
1645
1646=head1 EXPORTABLE CONSTANTS
1647
1648=head2 FS
1649
1650An alias for C<Badger::Filesystem>
1651
1652=head2 VFS
1653
1654An alias for L<Badger::Filesystem::Virtual>.  This also ensures that the
1655L<Badger::Filesystem::Virtual> module is loaded.
1656
1657=head2 PATH
1658
1659An alias for C<Badger::Filesystem::Path>
1660
1661=head2 FILE
1662
1663An alias for C<Badger::Filesystem::File>
1664
1665=head2 DIR / DIRECTORY
1666
1667An alias for C<Badger::Filesystem::Directory>
1668
1669=head1 AUTHOR
1670
1671Andy Wardley L<http://wardley.org/>
1672
1673=head1 COPYRIGHT
1674
1675Copyright (C) 2005-2009 Andy Wardley. All rights reserved.
1676
1677This module is free software; you can redistribute it and/or
1678modify it under the same terms as Perl itself.
1679
1680=head1 ACKNOWLEDGEMENTS
1681
1682The C<Badger::Filesystem> modules are built around a number of Perl modules
1683written by some most excellent people. May the collective gratitude of the
1684Perl community shine forth upon them.
1685
1686L<File::Spec> by Ken Williams, Kenneth Albanowski, Andy Dougherty, Andreas
1687Koenig, Tim Bunce, Charles Bailey, Ilya Zakharevich, Paul Schinder, Thomas
1688Wegner, Shigio Yamaguchi, Barrie Slaymaker.
1689
1690L<File::Path> by Tim Bunce and Charles Bailey.
1691
1692L<Cwd> by Ken Williams and the Perl 5 Porters.
1693
1694L<IO::File> and L<IO::Dir> by Graham Barr.
1695
1696It was also inspired by, and draws heavily on the ideas and code in
1697L<Path::Class> by Ken Williams. There's also more than a passing influence
1698from the C<Template::Plugin::File> and C<Template::Plugin::Directory>
1699modules which were based on code originally by Michael Stevens.
1700
1701=head1 SEE ALSO
1702
1703L<Badger::Filesystem::Path>,
1704L<Badger::Filesystem::Directory>,
1705L<Badger::Filesystem::File>,
1706L<Badger::Filesystem::Visitor>
1707L<Badger::Filesystem::Virtual>.
1708
1709=cut
1710
1711# Local Variables:
1712# mode: Perl
1713# perl-indent-level: 4
1714# indent-tabs-mode: nil
1715# End:
1716#
1717# vim: expandtab shiftwidth=4:
1718# TextMate: rocks my world
1719