#======================================================================== # # Badger::Filesystem::Directory # # DESCRIPTION # OO representation of a file in a filesystem. # # AUTHOR # Andy Wardley # #======================================================================== package Badger::Filesystem::Directory; use Badger::Class version => 0.01, debug => 0, base => 'Badger::Filesystem::Path', dumps => 'path volume directory name stats', constants => 'ARRAY HASH', constant => { is_directory => 1, type => 'Directory', }; *dir = \&directory; *dirs = \&directories; *is_dir = \&is_directory; sub init { my ($self, $config) = @_; $self->init_path($config); $self->init_options($config); return $self; } sub base { $_[0]; } sub directory { my $self = shift; my $opts = (@_ > 1 && ref $_[-1] eq HASH) ? pop(@_) : $self->{ options }; # needs work return (@_ || %$opts) ? $self->filesystem->directory( @_ ? $self->relative(@_) : (), $opts ) : $self->{ directory }; } sub file { my $self = shift; my $opts = @_ && ref $_[-1] eq HASH ? pop(@_) : $self->{ options }; return (@_ || %$opts) ? $self->filesystem->file( @_ ? $self->relative(@_) : (), $opts ) : $self->error( missing => 'file name' ); } sub canonical { my $self = shift; $self->filesystem->slash_directory( $self->{ path } ); } sub exists { my $self = shift; $self->filesystem->directory_exists($self->{ path }); } sub create { my $self = shift; $self->filesystem->create_directory($self->{ path }, @_); } sub delete { my $self = shift; $self->filesystem->delete_directory($self->{ path }, @_); } sub mkdir { my $self = shift; return @_ ? $self->directory(@_)->create : $self->create; } sub rmdir { my $self = shift; return @_ ? $self->directory(@_)->delete : $self->delete; } sub open { my $self = shift; $self->filesystem->open_directory($self->{ path }, @_); } sub read { my $self = shift->must_exist; $self->filesystem->read_directory($self->{ path }, @_); } sub children { my $self = shift; $self->debug("asking for $self->{ path } children\n") if $DEBUG; return $self->filesystem->directory_children($self->{ path }, @_); } sub files { my $self = shift; my @files = grep { $_->is_file } $self->children; return wantarray ? @files : \@files; } sub directories { my $self = shift; my @dirs = grep { $_->is_dir } $self->children; return wantarray ? @dirs : \@dirs; } sub accept { $_[1]->visit_directory($_[0]); } # Custom entry handler for the special case when a visitor starts visiting # at a directory - in this case we move straight onto visiting the children # of the directory rather than making a callback for the root directory. sub enter { $_[1]->enter_directory($_[0]); } 1; =head1 NAME Badger::Filesystem::Directory - directory object =head1 SYNOPSIS # using either of Badger::Filesytem constructor subroutines use Badger::Filesystem 'Dir Directory'; # use native OS-specific paths: $dir = Dir('/path/to/dir'); # or generic OS-independent paths $dir = Dir('path', 'to', 'dir'); # Dir is short for Directory if you prefer longness $dir = Directory('/path/to/dir'); $dir = Directory('path', 'to', 'dir'); # manual object construction use Badger::Filesystem::Directory; # positional arguments $dir = Badger::Filesystem::Directory->new('/path/to/file'); $dir = Badger::Filesystem::Directory->new(['path', 'to', 'file']); # named parameters $dir = Badger::Filesystem::Directory->new( path => '/path/to/dir' # native ); $dir = Badger::Filesystem::Directory->new( path => ['path', 'to', 'dir'] # portable ); # path inspection methods $dir->path; # full path $dir->directory; # same as path() $dir->dir; # alias to directory() $dir->base; # same as path() $dir->volume; # path volume (e.g. C:) $dir->is_absolute; # path is absolute $dir->is_relative; # path is relative $dir->exists; # returns true/false $dir->must_exist; # throws error if not @stats = $dir->stat; # returns list $stats = $dir->stat; # returns list ref # path translation methods $dir->relative; # relative to cwd $dir->relative($base); # relative to $base $dir->absolute; # relative to filesystem root $dir->definitive; # physical file location $dir->collapse; # resolve '.' and '..' in $file path # path comparison methods $dir->above($another_path); # $dir is ancestor of $another_path $dir->below($another_path); # $dir is descendant of $another_path # directory manipulation methods $dir->create; # create directory $dir->delete; # delete directory $fh = $dir->open; # open directory to read # all-in-one read/write methods @data = $dir->read; # return directory index @kids = $dir->children; # objects for each file/subdir @files = $dir->files; # objects for each file in dir @dirs = $dir->dirs; # objects for each sub-dir in dir @dirs = $dir->directories; # same as dirs() =head1 DESCRIPTION The C module is a subclass of L for representing directories in a file system. You can create a file object using the C constructor function in L. This is also available as C if you prefer longer names. use Badger::Filesystem 'Dir'; Directory paths can be specified as a single string using your native filesystem format or as a list or reference to a list of items in the path for platform-independent paths. my $dir = Dir('/path/to/dir'); If you're concerned about portability to other operating systems and/or file systems, then you can specify the directory path as a list or reference to a list of component names. my $dir = Dir('path', 'to', 'dir'); my $dir = Dir(['path', 'to', 'dir']); =head1 METHODS In addition to the methods inherited from L, the following methods are defined or re-defined. =head2 init(\%config) Customised initialisation method specific to directories. =head2 exists Returns true if the directory exists in the filesystem. Returns false if the directory does not exists or if it is not a directory (e.g. a file). =head2 is_directory() / is_dir() This method returns true for all C instances. =head2 volume() / vol() Returns any volume defined as part of the path. This is most commonly used on Win32 platforms to indicate drive letters, e.g. C. # on MS Windows print Dir('C:\\foo\\bar')->volume; # C =head2 base() This always returns C<$self> for directories. =head2 canonical() This returns the canonoical representation of the directory path. This is the absolute path with a trailing slash added (or whatever the relevant directory separator is for your filesystem). print Dir('/foo/bar')->canonical; # /foo/bar/ =head2 directory() / dir() Returns the complete directory path when called without arguments. This is effectively the same thing as C or C returns, given that this object I a directory. This can also be used with an argument to locate another directory relative to this one. my $dir = Dir('/path/to/dir'); print $dir->dir; # /path/to/dir (auto-stringified) print $dir->dir('subdir'); # /path/to/dir/subdir (ditto) Directories are returned as new C objects. The above examples are relying on the auto-stringification to display the path when printed. =head2 file($name) This method can be used to locate a file relative to the directory. The file is returned as a L object. my $dir = Dir('/path/to/dir'); my $file = $dir->file('example.txt'); print $file->path; # /path/to/dir/example.txt print $file; # same (auto-stringified) =head2 create() This method can be used to create the directory if it doesn't already exist. Dir('/path/to/dir')->create; =head2 delete() This method deletes the directory permanently. Use it wisely. Dir('/tmp/junk')->delete; =head2 mkdir($subdir) This method can be used to create a sub-directory. my $dir = Dir('/tmp'); $dir->mkdir('junk'); # /tmp/junk When called without an argument it has the same effect as L in creating itself. my $dir = Dir('/tmp/junk'); $dir->mkdir; # same as $dir->create =head2 rmdir($subdir); This does the opposite of L but works in the same way. It can be used to delete a sub-directory: my $dir = Dir('/tmp'); $dir->rmdir('junk'); # /tmp/junk Or the directory itself when called without an argument: my $dir = Dir('/tmp/junk'); $dir->rmdir; # same as $dir->delete =head2 open() This method opens the directory and returns an L handle to it. $fh = $dir->open; while (defined($item = $fh->read)) { print $item, "\n"; } =head2 read($all) This method read the contents of the directory. It returns a list (in list context) or a reference to a list (in scalar context) containing the names of the entries in the directory. my @entries = $dir->read; # list in list context my $entries = $dir->read; # list ref in scalar context By default, the C<.> and C<..> directories (or the equivalents for your file system) are ignored. Pass a true value for the C<$all> flag if you want them included. =head2 children($all) Returns the entries of a directory as L or L objects. Returns a list (in list context) or a reference to a list (in scalar context). my @kids = $dir->children; # list in list context my $kids = $dir->children; # list ref in scalar context =head2 files() Returns a list (in list context) or a reference to a list (in scalar context) of all the files in a directory as L objects. my @files = $dir->files; # list in list context my $files = $dir->files; # list ref in scalar context =head2 directories() / dirs() Returns a list (in list context) or a reference to a list (in scalar context) of all the sub-directories in a directory as L objects. my @dirs = $dir->dirs; # list in list context my $dirs = $dir->dirs; # list ref in scalar context =head2 visit($visitor) Entry point for a filesystem visitor for visit a directory. A reference to a L object (or subclass) should be passed as the first argument. use Badger::Filesystem::Visitor; my $visitor = Badger::Filesystem::Visitor->new( in_dirs => 1 ); $dir->visit($visitor); Alternately, a list or reference to a hash array of named parameters may be provided. These will be used to instantiate a new L object (via the L L method) which will then be applied to the directory. If no arguments are passed then a visitor is created with a default configuration. # either list of named params $dir->visit( in_dirs => 1 ); # or reference to hash array $dir->visit({ in_dirs => 1}); The method then calls the visitor L passing C<$self> as an argument to begin visiting the directory. =head2 accept($visitor) This method is called to dispatch a visitor to the correct method for a filesystem object. In the L class, it calls the visitor L method, passing the C<$self> object reference as an argument. =head2 enter($visitor) This is a custom variant of the L method which is called by a visitor when it first enters a filesystem. Instead of calling the visitor L method, it calls L passing C<$self> as an argument to begin visiting the files and sub-directories contained in this directory. =head1 AUTHOR Andy Wardley L =head1 COPYRIGHT Copyright (C) 2005-2009 Andy Wardley. All rights reserved. =head1 ACKNOWLEDGEMENTS The C modules are built around a number of existing Perl modules, including L, L, L, L, L and draw heavily on ideas in L. Please see the L in L for further information. =head1 SEE ALSO L, L, L, L. =cut # Local Variables: # mode: Perl # perl-indent-level: 4 # indent-tabs-mode: nil # End: # # vim: expandtab shiftwidth=4: # TextMate: doesn't need this cruft