File::Fu::File - a filename object


File-Fu documentation Contained in the File-Fu distribution.

Index


Code Index:

NAME

Top

File::Fu::File - a filename object

SYNOPSIS

Top

  use File::Fu;

  my $file = File::Fu->file("path/to/file");
  $file %= '.extension';
  $file->e and warn "$file exists";

  $file->l and warn "$file is a link to ", $file->readlink;

Constructor

Top

new

  my $file = File::Fu::File->new($path);

  my $file = File::Fu::File->new(@path);

new_direct

  my $file = File::Fu::File->new_direct(
    dir => $dir_obj,
    file => $name
  );

Class Constants

Top

dir_class

Return the corresponding dir class for this file object.

  my $dc = $class->dir_class;

is_dir

Always false for a file.

is_file

Always true for a file.

Parts

Top

basename

Returns a new object representing only the file part of the name.

  my $obj = $file->basename;

Methods

Top

stringify

  my $string = $file->stringify;

append

Append a string only to the filename part.

  $file->append('.gz');

  $file %= '.gz';

(Yeah... I tried to use .=, but overloading hates me.)

map

  $file->map(sub {...});

  $file &= sub {...};

absolute

Get an absolute name (without checking the filesystem.)

  my $abs = $file->absolute;

absolutely

Get an absolute name (resolved on the filesytem.)

  my $abs = $file->absolutely;

Doing stuff

Top

open

Open the file with $mode ('<', 'r', '>', 'w', etc) -- see IO::File.

  my $fh = $file->open($mode, $permissions);

Throws an error if anything goes wrong or if the resulting filehandle happens to be a directory.

piped_open

Opens a read pipe. The file is appended to @command.

  my $fh = $file->piped_open(@command);

touch

Update the timestamp of a file (or create it.)

  $file->touch;

read

Read the entire file into memory (or swap!)

  my @lines = $file->read;

  my $file = $file->read;

If File::Slurp is available, options to read_file will be passed along. See read_file in File::Slurp.

write

Write the file's contents.

  $file->write($content);

If File::Slurp is available, $content may be either a scalar, scalar ref, or array ref.

  $file->write($content, %args);

copy

  $file->copy($dest);

AUTHOR

Top

Eric Wilhelm @ <ewilhelm at cpan dot org>

http://scratchcomputing.com/

BUGS

Top

If you found this module on CPAN, please report any bugs or feature requests through the web interface at http://rt.cpan.org. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

If you pulled this development version from my /svn/, please contact me directly.

COPYRIGHT

Top

NO WARRANTY

Top

Absolutely, positively NO WARRANTY, neither express or implied, is offered with this software. You use this software at your own risk. In case of loss, no person or entity owes you anything whatsoever. You have been warned.

LICENSE

Top

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


File-Fu documentation Contained in the File-Fu distribution.
package File::Fu::File;
$VERSION = v0.0.7;

use warnings;
use strict;
use Carp;

use IO::File ();

use base 'File::Fu::Base';

use Class::Accessor::Classy;
lv 'file';
ro 'dir';  aka dir  => 'dirname', 'parent';
no  Class::Accessor::Classy;

#use overload ();

sub new {
  my $package = shift;
  my $class = ref($package) || $package;
  my $self = {$class->_init(@_)};
  bless($self, $class);
  return($self);
} # end subroutine new definition
########################################################################

sub new_direct {
  my $package = shift;
  my $class = ref($package) || $package;
  my $self = {@_};
  bless($self, $class);
  return($self);
} # end subroutine new_direct definition
########################################################################

use constant dir_class => 'File::Fu::Dir';
use constant is_dir => 0;
use constant is_file => 1;

########################################################################

sub _init {
  my $class = shift;
  my @dirs = @_ or croak("file must have a name");
  my $file = pop(@dirs);
  if($file =~ m#/#) {
    croak("strange mix: ", join(',', @_, $file)) if(@dirs);
    my %p = $class->dir_class->_init($file);
    @dirs = @{$p{dirs}};
    $file = pop(@dirs);
  }

  return(dir => $class->dir_class->new(@dirs), file => $file);
} # end subroutine _init definition
########################################################################

sub basename {
  my $self = shift;
  $self->new($self->file);
} # end subroutine basename definition
########################################################################

sub stringify {
  my $self = shift;
  my $dir = $self->dir;
  #warn "stringify(..., $_[1], $_[2])";
  #Carp::carp("stringify ", overload::StrVal($self), " ($self->{file})");
  $dir = $dir->is_cwd ? '' : $dir->stringify;
  return($dir . $self->file);
} # end subroutine stringify definition
########################################################################

sub append {
  my $self = shift;
  my ($tail) = @_;
  $self->file .= $tail;
  $self;
} # end subroutine append definition
########################################################################

sub map :method {
  my $self = shift;
  my ($sub) = shift;
  local $_ = $self->file;
  $sub->();
  $self->file = $_;
  $self;
} # end subroutine map definition
########################################################################

sub absolute {
  my ($self) = shift;
  return($self->dir->absolute->file($self->file));
} # end subroutine absolutely definition
########################################################################

sub absolutely {
  my $self = shift;
  return($self->dir->absolutely->file($self->file));
} # end subroutine absolutely definition
########################################################################

# TODO should probably have our own filehandle so we can close in the
# destructor and croak there too?

sub open :method {
  my $self = shift;
  my $fh = IO::File->new($self, @_) or croak("cannot open '$self' $!");
  -d $fh and croak("$self is a directory");
  return($fh);
} # end subroutine open definition
########################################################################

sub piped_open {
  my $self = shift;
  my (@command) = @_;

  # TODO some way to decide where self goes in @command
  push(@command, $self);

  # TODO closing STDIN and such before the fork?

  # TODO here is where we need our own filehandle object again
  my $pid = open(my $fh, '-|', @command) or
    croak("cannot exec '@command' $!");
  return($fh);
} # end subroutine piped_open definition
########################################################################

sub touch {
  my $self = shift;
  if(-e $self) {
    $self->utime(time);
  }
  else {
    $self->open('>');
  }
  return($self);
} # end subroutine touch definition
########################################################################

sub link :method {
  my $self = shift;
  my ($name) = @_;
  link($self, $name) or croak("link '$self' to '$name' failed $!");
  return($self->new($name));
} # end subroutine link definition
########################################################################

sub symlink :method {
  my $self = shift;
  my ($name) = @_;
  symlink($self, $name) or
    croak("symlink '$self' to '$name' failed $!");
  return($self->new($name));
} # end subroutine symlink definition
########################################################################

# TODO
# my $link = $file->dwimlink(absolute|relative|samedir => $linkname);

sub unlink :method {
  my $self = shift;
  unlink("$self") or croak("unlink '$self' failed $!");
} # end subroutine unlink definition
########################################################################

sub readlink :method {
  my $self = shift;
  my $name = readlink($self);
  defined($name) or croak("cannot readlink '$self' $!");
  return($self->new($name));
} # end subroutine readlink definition
########################################################################

########################################################################
{ # a closure for this variable
my $has_slurp;

sub read :method {
  my $self = shift;
  my @args = @_;

  $has_slurp ||= eval {require File::Slurp; 1} || -1;

  if($has_slurp > 0) {
    local $Carp::CarpLevel = 1;
    return(File::Slurp::read_file("$self", @args, err_mode => 'croak'));
  }
  else {
    croak("must have File::Slurp for fancy reads") if(@args);

    my $fh = $self->open;
    local $/ = wantarray ? $/ : undef;
    return(<$fh>);
  }
} # end subroutine read definition
########################################################################

sub write {
  my $self = shift;
  my ($content, @args) = @_;

  $has_slurp ||= eval {require File::Slurp; 1} || -1;

  if($has_slurp > 0) {
    local $Carp::CarpLevel = 1;
    return(File::Slurp::write_file("$self",
      {@args, err_mode => 'croak'},
      $content
    ));
  }
  else {
    croak("must have File::Slurp for fancy writes")
      if(@args or ref($content));
    my $fh = $self->open('>');
    print $fh $content;
    close($fh) or croak("write '$self' failed: $!");
  }
} # end subroutine write definition
########################################################################
} # File::Slurp closure
########################################################################

sub copy {
  my $self = shift;
  my ($dest) = shift;
  my (%opts) = @_;

  # decide if this is file-to-dir or file-to-file
  if(-d $dest) {
    $dest = $self->dir_class->new($dest)->file($self->basename);
  }
  else {
    $dest = $self->new($dest) unless(ref($dest));
  }
  if($dest->e) {
    croak("'$dest' and '$self' are the same file")
      if($self->is_same($dest));
  }

  # TODO here's another good reason to have our own filehandle object:
  # This fh-copy should be in there.
  my $ifh = $self->open;
  my $ofh = $dest->open('>');
  binmode($_) for($ifh, $ofh);
  while(1) {
    my $buf;
    defined(my $r = sysread($ifh, $buf, 1024)) or
      croak("sysread failed $!");
    $r or last;
    # why did File::Copy::copy do it like this?
    for(my $t = my $w = 0; $w < $r; $w += $t) {
      $t = syswrite($ofh, $buf, $r - $w, $w) or
        croak("syswrite failed $!");
    }
  }
  close($ofh) or croak("write '$dest' failed: $!");
  # TODO some form of rollback?

  # TODO handle opts
  #if($opts{preserve}) {
  #  # TODO chmod/chown and such
  #  $dest->utime($self->stat->mtime);
  #}

} # end subroutine copy definition
########################################################################

require File::Fu;
# vi:ts=2:sw=2:et:sta
1;