#======================================================================== # # Badger::Filesystem::File # # DESCRIPTION # OO representation of a file in a filesystem. # # AUTHOR # Andy Wardley # #======================================================================== package Badger::Filesystem::File; use Badger::Class version => 0.01, base => 'Badger::Filesystem::Path', debug => 0, dumps => 'path volume directory name stats', import => 'class', constants => 'ARRAY BLANK', constant => { type => 'File', is_file => 1, }; # aliases *base = \&directory; *copy = \©_to; *move = \&move_to; sub init { my ($self, $config) = @_; $self->init_path($config); $self->init_options($config); return $self; } sub directory { my $self = shift; return @_ ? $self->filesystem->directory( $self->relative(@_) ) : $self->parent; } sub file { my $self = shift; return @_ ? $self->filesystem->file( $self->relative(@_) ) : $self; } sub exists { my $self = shift; # cache the stats returned in case we want them later return ($self->{ stats } = $self->filesystem->file_exists($self->{ path })); } sub create { my $self = shift; $self->filesystem->create_file($self->{ path }, @_); } sub touch { my $self = shift; $self->filesystem->touch_file($self->{ path }); } sub open { my $self = shift; $self->filesystem->open_file($self->{ path }, @_, $self->{ options }); } sub read { my $self = shift; $self->filesystem->read_file($self->{ path }, @_, $self->{ options }); } sub write { my $self = shift; $self->filesystem->write_file($self->{ path }, @_, $self->{ options }); } sub append { my $self = shift; $self->filesystem->append_file($self->{ path }, @_, $self->{ options }); } sub copy_to { my $self = shift; $self->filesystem->copy_file($self->{ path }, @_); } sub copy_from { my $self = shift; $self->filesystem->copy_file(shift, $self->{ path }, @_); } sub move_to { my $self = shift; $self->filesystem->move_file($self->{ path }, @_); } sub move_from { my $self = shift; $self->filesystem->move_file(shift, $self->{ path }, @_); } sub print { my $self = shift; $self->write( join(BLANK, @_) ); } sub delete { my $self = shift; $self->filesystem->delete_file($self->{ path }, @_); } sub text { my $self = shift; my $text = $self->read(@_, $self->{ options }); return $text; } sub data { my $self = shift; my $codec = $self->{ options }->{ codec }; my $data; $self->debug("filesystem codec: $codec") if DEBUG; if (@_) { $data = @_ == 1 ? shift : [@_]; $data = $codec->encode($data) if $codec; return $self->write($data); } else { $data = $self->read; $data = $codec->decode($data) if $codec; return $data; } } sub accept { $_[1]->visit_file($_[0]); } class->methods( map { my $item = $_; # lexical copy for closure $item => sub { my $self = shift; # ...and strict in what you provide $self->encoding(':' . $item); return $self; } } qw( raw utf8 crlf bytes ) ); 1; __END__ =head1 NAME Badger::Filesystem::File - file object =head1 SYNOPSIS # using Badger::Filesytem constructor subroutine use Badger::Filesystem 'File'; # use native OS-specific paths: $file = File('/path/to/file'); # or generic OS-independent paths $file = File('path', 'to', 'file'); # manual object construction use Badger::Filesystem::File; # positional arguments $file = Badger::Filesystem::File->new('/path/to/file'); $file = Badger::Filesystem::File->new(['path', 'to', 'file']); # named parameters $file = Badger::Filesystem::File->new( path => '/path/to/file' # native ); $file = Badger::Filesystem::File->new( path => ['path', 'to', 'file'] # portable ); # path inspection methods $file->path; # full path $file->name; # file name $file->directory; # parent directory $file->dir; # alias to directory() $file->base; # same thing as directory() $file->volume; # path volume (e.g. C:) $file->is_absolute; # path is absolute $file->is_relative; # path is relative $file->exists; # returns true/false $file->must_exist; # throws error if not @stats = $file->stat; # returns list $stats = $file->stat; # returns list ref # path translation methods $file->relative; # relative to cwd $file->relative($base); # relative to $base $file->absolute; # relative to filesystem root $file->definitive; # physical file location $file->collapse; # resolve '.' and '..' in $file path # path comparison methods $file->above($another_path); # $file is ancestor of $another_path $file->below($another_path); # $file is descendant of $another_path # file manipulation methods $file->create; # create file $file->touch; # create file or update timestamp $file->delete; # delete file $fh = $file->open($mode); # open file (for read by default) $fh = $file->write; # open for write $fh = $file->append; # open for append; # all-in-one read/write methods @data = $file->read; # return list of lines $data = $file->read; # slurp whole content $text = $file->text; # same as read(); $file->write(@content); # write @content to file $file->append(@content); # append @content to file =head1 DESCRIPTION The C module is a subclass of L for representing files in a file system. You can create a file object C constructor function in L. use Badger::Filesystem 'File'; File 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 $file = File('/path/to/file'); If you're concerned about portability to other operating systems and/or file systems, then you can specify the file path as a list or reference to a list of component names. my $file = File('path', 'to', 'file'); my $file = File(['path', 'to', 'file']); You can specify a reference to a hash array of additional configuration items as the final argument. At present, there is are two configuration options. The first, C, can be use to specify the encoding of the file. This is applied through Perl's IO layer encodings. See C for further information. my $file = File('path' , 'to', 'file', { encoding => 'utf8' }); The other option is C. This allows you to define the name of a L which will be used to serialise data to and from the file via the L method. my $file = File('path' , 'to', 'file', { codec => 'storable' }); # save some data, automatically serialised via Storable (freeze) $file->data({ message => 'Hello World', numbers => [1.618, 2.718, 3.142], }); # later... load data again and automatically de-serialise (thaw) $data = $file->data; print $data->{ message }, "\n"; # Hello World print join(', ', @{ $data->{ numbers } }); # 1.618, 2.718, 3.142 =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 files. =head2 volume() / vol() Returns any volume defined as part of the path. This is most commonly used on MS Windows platforms to indicate drive letters, e.g. C. =head2 directory() / dir() / base() Returns the directory portion of the file path. This can also be used with an argument to locate another directory relative to this file. my $file = File('/path/to/file'); print $file->dir; # /path/to print $file->dir('subdir'); # /path/to/subdir =head2 name() Returns the file name portion of the path. =head2 file() When called without arguments, this method simply returns the file object itself. It can also be called with an argument to locate another file relative to the directory in which the current file is located. my $file = File('/path/to/file1'); print $file->file; # /path/to/file1 print $file->file('file2'); # /path/to/file2 =head2 exists Returns true if the file exists in the filesystem. Returns false if the file does not exists or if it is not a file (e.g. a directory). =head2 is_file() This method returns true for all C instances. =head2 create() This method can be used to create the file if it doesn't already exist. =head2 touch() This method can be used to create the file if it doesn't already exist or update the timestamp if it does. =head2 open($mode,$perms) This method opens the file and returns an L handle to it. The default is to open the file for read-only access. The optional arguments can be used to specify a different mode and default permissions for the file (these arguments are forwarded to L). my $fh = $file->open; # read my $fh = $file->open('w'); # write =head2 read() This method read the content of the file. It is returned as a single string in scalar context and a list of each line in list context. my $text = $file->read; my @lines = $file->read; =head2 write(@content) When called without arguments this method opens the file for writing and returns an L handle. my $fh = $file->write; $fh->print("Hello World!\n"); $fh->close; When called with arguments, the method opens the file, writes the argument to it, and then closes the file again. $file->write("Hello World!\n"); =head2 copy($to, %params) / copy_to($to, %params) This method copies the file to the new location specified by the first argument. It delegates to the L method in L. $file->copy('/some/where/else'); The destination can be specified as a file name, file object or file handle. An optional list of reference to a hash array of named parameters can follow. $file->copy( '/some/where/else' => { mkdir => 1, # create intermediate directories dir_mode => 0775, # permissions for created directories file_mode => 0664, # permissions for created file } ); =head2 copy_from($from, %params) Like L but working in reverse. $target_file->copy_from( $source_file ); =head2 move($to, %params) / move_to($to, %params) This method moves the file to the new location specified by the first argument. It delegates to the L method in L. $file->move('/some/where/else'); Arguments are as per L. =head2 move_from($from, %params) Like L but working in reverse. $target_file->move_from( $source_file ); =head2 print(@content) This method concatentates all arguments into a single string which it then forwards to the L method. This effectively forces the L method to always write something to the file, even if it's an empty string. $file->print("hello"); $file->print(@stuff); # works OK if @stuff is empty =head2 append(@content) This method is similar to L, but opens the file for appending (when called with no arguments), or appends any arguments to the end of the file. # manual open, append, close my $fh = $file->append; $fh->print("Hello World!\n"); $fh->close; # all-in-one $file->append("Hello World\n"); =head2 delete() This method deletes the file permanently. Use it wisely. =head2 text() This method is a wrapper around the L method which forces scalar context. The content of the file is always returned as a single string. NOTE: future versions will probably return this as a text object. =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 encoding($enc) This method can be used to get or set the encoding for the file. $file->encoding(':utf8'); The encoding will affect all operations that read data from, or write data to the file. TODO: note that you can call encoding() on a directory or filesystem to make that the default for all files contained therein =head2 utf() A method of convenience to set the file's encoding to UTF-8. $file->utf8; It has the same affect as calling the L method with an argument of C<:utf8>. See C for further information. # same $file->encoding(':utf8'); The method returns the file object itself, so can be used in a chain. $file->utf8->must_exist. =head2 bytes() Like C, this is a method of convenience to set the file encoding to C<:bytes>. =head2 crlf() Like C, this is a method of convenience to set the file encoding to C<:crlf>. =head2 raw() Like C, this is a method of convenience to set the file encoding to C<:raw>. =head2 codec() This method can be used to get or set the codec used to serialise data to and from the file via the L method. The codec should be specified by name, using any of the names that L recognises or can load. $file->codec('storable'); # first save the data to file $file->data($some_data_to_save); # later... load the data back out my $data = $file->data; You can use chained codec specifications if you want to pass the data through more than one codec. $file->code('storable+base64'); See L for further information on codecs. TODO: note that you can call codec() on a directory or filesystem to make that the default for all files contained therein =head2 data(@data) This method is used to read or write serialised data to and from the file. When called with arguments, the method serialises the data through any L defined and writes it to the file. A single argument should be a reference to an array or hash array. $file->data({ # either: HASH reference e => 2.718, pi => 3.142, phi => 1.618, }); $file->data([ # or: ARRAY reference 2.718, 3.142, 1.618 ]); If multiple arguments are specified then they are implicitly converted to a list reference. $file->data( # same as ARRAY reference above 2.718, 3.142, 1.618 ); When called without any arguments the method will read the data from the file and de-serialise it using any L that is defined. my $data = $file->data; # HASH or ARRAY reference If no L is defined then the method behaves the same as a direct call to either L (called without arguments) or L (called with arguments). =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