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