Path-Iter-0.2/0000755000076500007650000000000011112544570012212 5ustar dmueydmueyPath-Iter-0.2/Changes0000644000076500007650000000026711112544275013514 0ustar dmueydmueyRevision history for Path-Iter 0.2 Sun Nov 23 08:30:20 2008 Do symlink in test script so it will be a symlink 0.1 Wed Nov 19 21:17:05 2008 Initial release. Path-Iter-0.2/lib/0000755000076500007650000000000011112544570012760 5ustar dmueydmueyPath-Iter-0.2/lib/Path/0000755000076500007650000000000011112544570013654 5ustar dmueydmueyPath-Iter-0.2/lib/Path/Iter.pm0000644000076500007650000000525711112544440015122 0ustar dmueydmueypackage Path::Iter; use strict; use warnings; use File::Spec (); $Path::Iter::VERSION = 0.2; sub get_iterator { my @queue = @_; my %args = (); if (ref $queue[-1] eq 'HASH') { %args = (%args, %{ pop @queue }); } @{ $args{'errors'} } = (); # empty an existing one %{ $args{'initial'} } = (); # empty an existing one for my $queue_item (@queue) { $queue_item = File::Spec->catdir($queue_item); # File::Spec version of =~ s{/+$}{}; $args{'initial'}{$queue_item} = 1; } return sub { return if !@queue; my $path = shift @queue; $path = File::Spec->catdir($path); # File::Spec version of =~ s{/+$}{}; # make it possible to handle symlinks how they want/need # return $path if -l $path; if (-l $path) { if (ref $args{'symlink_handler'} eq 'CODE') { my $is_initial = exists $args{'initial'}->{$path} ? 1 : 0; my ($symlink_traverse) = $args{'symlink_handler'}->($path, $is_initial); if (defined $symlink_traverse && $symlink_traverse) { if ($symlink_traverse == 2) { delete $args{'initial'}->{$path} if $is_initial; $path = readlink($path); $path = File::Spec->catdir($path); # File::Spec version of =~ s{/+$}{}; $args{'initial'}->{$path}++ if $is_initial; } } else { return $path; } } else { return $path; } } if (-d $path) { if (opendir DIR, $path) { my @dir_contents = grep { $_ ne '.' && $_ ne '..' } readdir DIR; closedir DIR; if (@dir_contents) { if (ref $args{'readdir_handler'} eq 'CODE') { push @queue, $args{'readdir_handler'}->( $path, map { File::Spec->catdir($path,$_) } @dir_contents); } else { push @queue, map { File::Spec->catdir($path,$_) } @dir_contents; } } } else { push @{$args{'errors'}}, { 'path' => $path, 'function' => 'opendir', 'args' => [\*DIR, $path], 'errno' => int($!), 'error' => int($!) . ": $!", }; return if $args{'stop_when_opendir_fails'}; } } return $path; } } 1;Path-Iter-0.2/lib/Path/Iter.pod0000644000076500007650000001300211112544460015255 0ustar dmueydmuey=head1 NAME Path::Iter - Simple Efficient Path Iteration =head1 VERSION This document describes Path::Iter version 0.2 =head1 SYNOPSIS use Path::Iter; my $fetch = Path::Iter::get_iterator('foo'); while ( my $next_path = $fetch->() ) { # do something with $next_path } =head1 DESCRIPTION Iterate through the contents of a given path without having to build the entire list first. =head1 INTERFACE =head2 Path::Iter::get_iterator() This returns an code reference that when called returns the next path. When there are no more (or a directory can't be opened and you have told it to stop as soon as that happens) it returns false. It takes one or more paths to process and an optional argument hashref as the last argument (See L) The paths returned contain the initial argument and its contents (if any). Assuming 'foo' in the SYNOPSIS: is a symlink: you'd iterate through foo is a file: you'd iterate through foo is a directory: you'd iterate through foo, foo/bar, foo/wop, foo/bar/baz, =head2 %ARGS These are all optional. =head3 errors This is an array of hashref's where any errors get put. my @err; my $iter = Path::Iter::get_iterator($path, {errors => \@err}); while(my $next = $iter->()) { ... } if (@err) { ... handle error ... } The keys for each error in the array are as follows: 'path' => $path, 'function' => 'opendir', 'args' => [\*DIR, $path], 'errno' => int($!), 'error' => int($!) . ": $!", =head3 stop_when_opendir_fails Boolean, when, if true will short circuit the iteration if an opendir() call fails. =head3 readdir_handler A coderef to be used to process and return directory contents (IE readdir() results) sub { my($working_path, @contents) = @_; if ($working_path eq '.data') { @contents = grep !/^\.private\-.*/, @contents; # ignore .private-... files in .data/ dir } return sort { $a cmp $b } @contents; # return what is left in sorted order } The first argument is the directory and the rest is its contents. The @contents have already had the $working_path prepended. It should return the contents how you wish them to appear in the iteration. =head3 symlink_handler By default syminks are not followed. This is a coderef that can control how symlinks are handled. B It receives two arguments: the path and a boolean of if it was an initial argument or not. If it returns true the path will be followed. If the true value is '2' it will be tranformed into its target. B Assume we have a link named 'link' whose target is 'path': sub { my ($path, $is_initial_arg) = @_; ... return; # default behavior = link } sub { my ($path, $is_initial_arg) = @_; ... return 1; # link link/dir link/file link/dir/etc } sub { my ($path, $is_initial_arg) = @_; ... return 2; # path path/dir path/file path/dir/etc } B =head3 initial This is a hashref used internally as a lookup of initial args as they are after any initialization. =head1 DIAGNOSTICS Throws no warnings or errors of its own. You can catch internal opendir failures. See L and L =head1 CONFIGURATION AND ENVIRONMENT Path::Iter requires no configuration files or environment variables. =head1 DEPENDENCIES L =head1 INCOMPATIBILITIES None reported. =head1 BUGS AND LIMITATIONS No bugs have been reported. Please report any bugs or feature requests to C, or through the web interface at L. =head1 AUTHOR Daniel Muey C<< >> =head1 LICENCE AND COPYRIGHT Copyright (c) 2008, Daniel Muey C<< >>. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.Path-Iter-0.2/Makefile.PL0000644000076500007650000000075711111154765014177 0ustar dmueydmueyuse strict; use warnings; use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Path::Iter', AUTHOR => 'Daniel Muey ', VERSION_FROM => 'lib/Path/Iter.pm', ABSTRACT_FROM => 'lib/Path/Iter.pod', PL_FILES => {}, PREREQ_PM => { 'Test::More' => 0, }, dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, clean => { FILES => 'Path-Iter-*' }, ); Path-Iter-0.2/MANIFEST0000644000076500007650000000051211112544570013341 0ustar dmueydmueyChanges MANIFEST Makefile.PL README lib/Path/Iter.pm lib/Path/Iter.pod t/00.load.t t/perlcritic.t t/pod-coverage.t t/pod.t t/path/ t/path/dir.a/ t/path/dir.a/dir.a t/path/dir.a/dir.b t/path/dir.a/file.x t/path/dir.a/file.y t/path/file.b t/path/file.z META.yml Module meta-data (added by MakeMaker) Path-Iter-0.2/META.yml0000644000076500007650000000052211112544570013462 0ustar dmueydmuey# http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: Path-Iter version: 0.2 version_from: lib/Path/Iter.pm installdirs: site requires: Test::More: 0 distribution_type: module generated_by: ExtUtils::MakeMaker version 6.30 Path-Iter-0.2/README0000644000076500007650000000071311112544430013066 0ustar dmueydmueyPath-Iter version 0.2 DOCUMENTATION See POD for documentation. INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install DEPENDENCIES See DEPENDENCIES section in POD, 'requires' key in Build.PL, or 'PREREQ_PM' key in Makefile.PL COPYRIGHT AND LICENCE Copyright (C) 2008, Daniel Muey This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Path-Iter-0.2/t/0000755000076500007650000000000011112544570012455 5ustar dmueydmueyPath-Iter-0.2/t/00.load.t0000644000076500007650000000531311112544533014000 0ustar dmueydmueyuse Test::More tests => 14; use File::Spec; BEGIN { use_ok( 'Path::Iter' ); } diag( "Testing Path::Iter $Path::Iter::VERSION" ); chdir 't'; # just in case we're not already there BAIL_OUT('I am not in the right place, sorry') if !-d 'path'; symlink('path','link'); BAIL_OUT('Could not make the symlink') if !-l 'link'; my @lookup_build = ( [qw(dir.a)], [qw(file.b)], [qw(file.z)], [qw(dir.a dir.a)], [qw(dir.a dir.b)], [qw(dir.a file.x)], [qw(dir.a file.y)], ); my %path_lookup = ( 'path' => 1, map { File::Spec->catdir('path',@{$_}) => 1 } @lookup_build ); my %link_lookup = ( 'link' => 1, map { File::Spec->catdir('link',@{$_}) => 1 } @lookup_build ); my @path_readdir_handler = ( 'path', map { File::Spec->catdir('path',@{$_}) } @lookup_build ); my %results; my $iter = Path::Iter::get_iterator('path/'); ok(ref $iter eq 'CODE', 'iterator is a code ref'); while(my $key = $iter->()) { # diag($key); $results{$key}++; } is_deeply(\%results, \%path_lookup, 'path traversal'); %results = (); $iter = Path::Iter::get_iterator('link/'); while(my $key = $iter->()) { $results{$key}++; } is_deeply(\%results, { 'link' => 1 }, 'link - slash'); %results = (); $iter = Path::Iter::get_iterator('link'); while(my $key = $iter->()) { $results{$key}++; } is_deeply(\%results, { 'link' => 1 }, 'link - no slash'); my %initial; %results = (); $iter = Path::Iter::get_iterator('link/', {'symlink_handler' => sub { return }, 'initial' => \%initial, }); while(my $key = $iter->()) { $results{$key}++; } ok(keys %initial == 1, 'initial arg populated'); ok(exists $initial{'link'}, 'arg cleaned'); is_deeply(\%results, { 'link' => 1 }, 'symlink not traversed w/ symlink_handler return false'); %initial = (); %results = (); $iter = Path::Iter::get_iterator('link', {'symlink_handler' => sub { return 1 }, 'initial' => \%initial, }); while(my $key = $iter->()) { $results{$key}++; } is_deeply(\%results, \%link_lookup, 'link traversal w/ symlink_handler return 1'); %initial = (); %results = (); $iter = Path::Iter::get_iterator('link', {'symlink_handler' => sub { return 2 }, 'initial' => \%initial, }); while(my $key = $iter->()) { $results{$key}++; } is_deeply(\%results, \%path_lookup, 'link traversal w/ symlink_handler return 2'); ok(!exists $initial{'link'}, 'arg change ! included'); ok(exists $initial{'path'}, 'arg change included'); ok(keys %initial == 1, 'arg change changed'); my @results; $iter = Path::Iter::get_iterator('path/', { 'readdir_handler' => sub { my ($dir, @contents) = @_; sort { $a cmp $b } @contents } }); while(my $key = $iter->()) { push @results, $key; } is_deeply(\@results, \@path_readdir_handler, 'readdir_handler'); # TODO: errors and stop_when_opendir_fails keysPath-Iter-0.2/t/path/0000755000076500007650000000000011112544570013411 5ustar dmueydmueyPath-Iter-0.2/t/path/dir.a/0000755000076500007650000000000011112544570014406 5ustar dmueydmueyPath-Iter-0.2/t/path/dir.a/dir.a/0000755000076500007650000000000011111405534015376 5ustar dmueydmueyPath-Iter-0.2/t/path/dir.a/dir.b/0000755000076500007650000000000011111405536015401 5ustar dmueydmueyPath-Iter-0.2/t/path/dir.a/file.x0000644000076500007650000000000011111405550015475 0ustar dmueydmueyPath-Iter-0.2/t/path/dir.a/file.y0000644000076500007650000000000011111405552015500 0ustar dmueydmueyPath-Iter-0.2/t/path/file.b0000644000076500007650000000000011111405522014451 0ustar dmueydmueyPath-Iter-0.2/t/path/file.z0000644000076500007650000000000011111405524014503 0ustar dmueydmueyPath-Iter-0.2/t/perlcritic.t0000644000076500007650000000046611111101121014764 0ustar dmueydmuey#!perl -T use Test::More; eval 'use Test::Perl::Critic'; plan skip_all => 'Test::Perl::Critic required for testing PBP compliance' if $@; plan skip_all => q($ENV{'do_perl_critic_tests'} must be true to run these 'development only' tests) if !$ENV{'do_perl_critic_tests'}; Test::Perl::Critic::all_critic_ok(); Path-Iter-0.2/t/pod-coverage.t0000644000076500007650000000025411111101121015172 0ustar dmueydmuey#!perl -T use Test::More; eval 'use Test::Pod::Coverage 1.04'; plan skip_all => 'Test::Pod::Coverage 1.04 required for testing POD coverage' if $@; all_pod_coverage_ok(); Path-Iter-0.2/t/pod.t0000644000076500007650000000021411111101121013375 0ustar dmueydmuey#!perl -T use Test::More; eval 'use Test::Pod 1.14'; plan skip_all => 'Test::Pod 1.14 required for testing POD' if $@; all_pod_files_ok();