package Path::Abstract; use warnings; use strict; =head1 NAME Path::Abstract - Fast and featureful UNIX-style path parsing and manipulation =cut use vars qw/$VERSION $_0_093_warn %_0_093_warning/; $VERSION = '0.095'; =head1 VERSION Version 0.095 =head1 SYNOPSIS use Path::Abstract; my $path = Path::Abstract->new( '/apple/banana' ) # $parent is '/apple' my $parent = $path->parent # $cherry is '/apple/banana/cherry.txt' my $cherry = $path->child( "cherry.txt" ) path( '/a/b/c/' )->list # ( 'a', 'b', 'c' ) path( '/a/b/c/' )->split # ( '/a', 'b', 'c/' ) path( '/a/b/c/' )->first # a path( '/a/b/c/' )->beginning # /a path( '/a/b/c/' )->last # c path( '/a/b/c/' )->ending # c/ path( '/a/b/c/' ).at(0) # a (equivalent to ->first) path( '/a/b/c/' ).at(-1) # c (equivalent to ->last) path( '/a/b/c/' ).at(1) # b $path = path( 'a/b/c' ) $path->append( 'd', 'ef/g', 'h' ) # a/b/cd/ef/g/h path( 'a/b/c.html' )->extension # .html path( 'a/b/c' )->extension # '' path( 'a/b/c.tar.gz' )->extension # .gz path( 'a/b/c.tar.gz' )-> extension({ match: '*' }) # .tar.gz path( 'a/b/c.html' )->extension( '.txt' ) # a/b/c.txt path( 'a/b/c.html' )->extension( 'zip' ) # a/b/c.zip path( 'a/b/c.html' )->extension( '' ) # a/b/c path( 'a/b/c' )->down( 'd/e' ) # a/b/c/d/e path( 'a/b/c' )->child( 'd/e' ) # a/b/c/d/e (Same as ->down except # returning a new path instead of # modifying the original) path( 'a/b/c' )->up # a/b path( 'a/b/c' )->parent # a/b (Same as ->up except # returning a new path instead of # modifying the original) =head1 DESCRIPTION Path::Abstract is a tool for parsing, interrogating, and modifying a UNIX-style path. The parsing behavior is similar to L, except that trailing slashes are preserved (converted into a single slash). =cut $_0_093_warn = 1; use Sub::Exporter; { my $exporter = Sub::Exporter::build_exporter({ exports => [ path => sub { sub { return __PACKAGE__->new(@_) } } ], }); sub import { if (@_ && grep { defined && $_ eq '--no_0_093_warning' } @_) { $_0_093_warn = 0; } @_ = grep { ! defined || $_ !~ m/^--/ } @_; goto $exporter; }; } use overload '""' => 'get', fallback => 1, ; use base qw/Path::Abstract::Underload/; use Carp; sub _0_093_warn { if ($_0_093_warn) { my ($package, $filename, $line, $subroutine) = caller(1); if (! $_0_093_warning{$subroutine}) { $_0_093_warning{$subroutine} = 1; $subroutine =~ s///g; carp "** $subroutine behavior has changed since 0.093\n" . "** To disable this warning: use Path::Abstract qw/--no_0_093_warning/" } } } =head1 Different behavior since 0.093 Some methods of Path::Abstract have changed since 0.093 with the goal of having better/more consistent behavior Unfortunately, this MAY result in code that worked with 0.093 and earlier be updated to reflect the new behavior The following has changed: =head2 $path->list The old behavior (kept the leading slash but dropped trailing slash): path('/a/b/c/')->list # ( '/a', 'b', 'c' ) path('a/b/c/')->list # ( 'a', 'b', 'c' ) The new behavior (neither slash is kept): path('/a/b/c/')->list # ( 'a', 'b', 'c' ) path('a/b/c/')->list # ( 'a', 'b', 'c' ) In addition, $path->split was an alias for $path->list, but this has changed. Now split WILL keep BOTH leading and trailing slashes (if any): path('/a/b/c/')->split # ( '/a', 'b', 'c/' ) path('a/b/c/')->split # ( 'a', 'b', 'c/' ) path('a/b/c')->split # ( 'a', 'b', 'c' ) Effectively equivalent to ->list =head2 $path->split See the above note on $path->list =head2 $path->first The old behavior: 1. Would return undef for the empty path 2. Would include the leading slash (if present) 3. Would NOT include the trailing slash (if present) path(undef)->first # undef path('')->first # undef path('/a')->first # /a path('/a/')->first # /a path('a')->first # a The new behavior: 1. Always returns at least the empty string 2. Never includes any slashes path(undef)->first # '' path('')->first # '' path('/a')->first # a path('/a/')->first # a path('a')->first # a For an alternative to ->first, try ->beginning =head2 $path->last Simlar to ->first The old behavior: 1. Would return undef for the empty path 2. Would include the leading slash (if present) 3. Would NOT include the trailing slash (if present) path(undef)->last # undef path('')->last # undef path('/a')->last # /a path('/a/')->last # /a path('a')->last # a path('a/b')->last # b path('a/b/')->last # b The new behavior: 1. Always returns at least the empty string 2. Never includes any slashes path(undef)->last # '' path('')->last # '' path('/a')->last # a path('/a/')->last # a path('a')->last # a path('a/b')->last # b path('a/b/')->last # b For an alternative to ->last, try ->ending =head2 $path->is_branch The old behavior: 1. The empty patch ('') would not be considered a branch The new behavior: 1. The empty patch ('') IS considered a branch =back =head1 METHODS =head2 Path::Abstract->new( ) =head2 Path::Abstract->new( , [ , ..., ] ) Create a new C object using or by joining each with "/" Returns the new C object =cut =head2 Path::Abstract::path( ) =head2 Path::Abstract::path( , [ , ..., ] ) Create a new C object using or by joining each with "/" Returns the new C object =cut =head2 $path->clone Returns an exact copy of $path =cut =head2 $path->set( ) =head2 $path->set( , [ , ..., ] ) Set the path of $path to or the concatenation of each (separated by "/") Returns $path =cut =head2 $path->is_nil =head2 $path->is_empty Returns true if $path is equal to "" =cut =head2 $path->is_root Returns true if $path is equal to "/" =cut =head2 $path->is_tree Returns true if $path begins with "/" path("/a/b")->is_tree # Returns true path("c/d")->is_tree # Returns false =cut =head2 $path->is_branch Returns true if $path does NOT begin with a "/" path("")->is_branch # Returns true path("/")->is_branch # Returns false path("c/d")->is_branch # Returns true path("/a/b")->is_branch # Returns false =cut =head2 $path->to_tree Change $path by prefixing a "/" if it doesn't have one already Returns $path =cut =head2 $path->to_branch Change $path by removing a leading "/" if it has one Returns $path =cut =head2 $path->list Returns the path in list form by splitting at each "/" path("c/d")->list # Returns ("c", "d") path("/a/b/")->last # Returns ("a", "b") NOTE: This behavior is different since 0.093 (see above) =cut =head2 $path->split =cut =head2 $path->first Returns the first part of $path up to the first "/" (but not including the leading slash, if any) path("c/d")->first # Returns "c" path("/a/b")->first # Returns "a" This is equivalent to $path->at(0) =cut =head2 $path->last Returns the last part of $path up to the last "/" path("c/d")->last # Returns "d" path("/a/b/")->last # Returns "b" This is equivalent to $path->at(-1) =cut =head2 $path->at( $index ) Returns the part of path at $index, not including any slashes You can use a negative $index to start from the end of path path("/a/b/c/").at(0) # a (equivalent to $path->first) path("/a/b/c/").at(-1) # c (equivalent to $path->last) path("/a/b/c/").at(1) # b =cut =head2 $path->beginning Returns the first part of path, including the leading slash, if any path("/a/b/c/")->beginning # /a path("a/b/c/")->beginning # a =cut =head2 $path->ending Returns the first part of path, including the leading slash, if any path("/a/b/c/")->ending # c/ path("/a/b/c")->ending # c =cut =head2 $path->get =head2 $path->stringify Returns the path in string or scalar form path("c/d")->list # Returns "c/d" path("/a/b/")->last # Returns "/a/b" =cut =head2 $path->push( , [ , ..., ] ) =head2 $path->down( , [ , ..., ] ) Modify $path by appending each to the end of \$path, separated by "/" Returns $path path( "a/b/c" )->down( "d/e" ) # a/b/c/d/e =cut =head2 $path->child( , [ , ..., ] ) Make a copy of $path and push each to the end of the new path. Returns the new child path path( "a/b/c" )->child( "d/e" ) # a/b/c/d/e =cut =head2 $path->append( $part1, [ $part2 ], ... ) Modify path by appending $part1 WITHOUT separating it by a slash. Any, optional, following $part2, ..., will be separated by slashes as normal $path = path( "a/b/c" ) $path->append( "d", "ef/g", "h" ) # "a/b/cd/ef/g/h" =cut =head2 $path->extension Returns the extension of path, including the leading the dot Returns "" if path does not have an extension path( "a/b/c.html" )->extension # .html path( "a/b/c" )->extension # "" path( "a/b/c.tar.gz" )->extension # .gz path( "a/b/c.tar.gz" )->extension({ match: "*" }) # .tar.gz =head2 $path->extension( $extension ) Modify path by changing the existing extension of path, if any, to $extension path( "a/b/c.html" )->extension( ".txt" ) # a/b/c.txt path( "a/b/c.html" )->extension( "zip" ) # a/b/c.zip path( "a/b/c.html" )->extension( "" ) # a/b/c Returns path =cut =head2 $path->pop( ) Modify $path by removing parts from the end of $path Returns the removed path as a C object =cut =head2 $path->up( ) Modify $path by removing parts from the end of $path Returns $path =cut =head2 $path->parent( ) Make a copy of $path and pop parts from the end of the new path Returns the new parent path =cut =head2 $path->file =head2 $path->file( , [ , ..., ] ) Create a new C object using $path as a base, and optionally extending it by each Returns the new file object =cut =head2 $path->dir =head2 $path->dir( , [ , ..., ] ) Create a new C object using $path as a base, and optionally extending it by each Returns the new dir object =cut =head1 SEE ALSO L L L L L L =head1 AUTHOR Robert Krimen, C<< >> =head1 SOURCE You can contribute or fork this project via GitHub: L git clone git://github.com/robertkrimen/path-abstract.git Path-Abstract =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc Path::Abstract You can also look for information at: =over 4 =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * RT: CPAN's request tracker L =item * Search CPAN L =back =head1 ACKNOWLEDGEMENTS Thanks to Joshua ben Jore, Max Kanat-Alexander, and Scott McWhirter for discovering the "use overload ..." slowdown issue. =head1 COPYRIGHT & LICENSE Copyright 2007 Robert Krimen, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; # End of Path::Abstract