File Coverage

blib/lib/Path/Class/Iterator.pm
Criterion Covered Total %
statement 101 111 90.9
branch 27 38 71.0
condition 6 15 40.0
subroutine 18 20 90.0
pod 4 4 100.0
total 156 188 82.9


line stmt bran cond sub pod time code
1             package Path::Class::Iterator;
2              
3 6     6   348624 use strict;
  6         15  
  6         260  
4 6     6   110 use warnings;
  6         12  
  6         181  
5 6     6   7728 use Path::Class;
  6         816558  
  6         503  
6 6     6   68 use Carp;
  6         14  
  6         484  
7 6     6   7740 use Iterator;
  6         188871  
  6         247  
8              
9 6     6   92 use base qw/ Class::Accessor::Fast /;
  6         14  
  6         24884  
10              
11             our $VERSION = '0.07_03';
12             our $Err;
13             our $debug = $ENV{PERL_TEST} || $ENV{PERL_DEBUG} || 0;
14              
15             if ($debug) {
16             require Data::Dump;
17             }
18              
19             my @acc = qw/
20             root
21             start
22             follow_symlinks
23             follow_hidden
24             iterator
25             error_handler
26             error
27             show_warnings
28             breadth_first
29             interesting
30             push_queue
31             pop_queue
32             queue
33             depth
34              
35             /;
36              
37             sub _listing {
38 40     40   114 my $self = shift;
39 40         74 my $path = shift;
40              
41 40         379 my $d = $path->open;
42              
43 40 50       11033 unless ( defined $d ) {
44 0         0 $self->error("cannot open $path: $!");
45 0 0       0 if ( $self->error_handler->( $self, $path, $! ) ) {
46 0     0   0 return Iterator->new( sub { Iterator::is_done(); return undef } );
  0         0  
  0         0  
47             }
48             else {
49 0         0 croak "can't open $path: $!";
50             }
51             }
52              
53             return Iterator->new(
54             sub {
55              
56             # Get next file, skipping . and ..
57 121     121   2190 my $next;
58 121         165 while (1) {
59 209         3671 $next = $d->read;
60              
61 209 100       5885 if ( !defined $next ) {
62 40         1051 undef $d; # allow garbage collection
63 40         178 Iterator::is_done();
64             }
65              
66 169 100 66     2833 next if !$self->follow_hidden && $next =~ m/^\./o;
67              
68 81 50 33     1704 last if $next ne '.' && $next ne '..';
69             }
70              
71             # Return this item
72 81         1075 my $f = Path::Class::Iterator::Dir->new( $path, $next );
73 81 100       12098 if ( -d $f ) {
74 36         7680 $self->{_depth} = (
75             scalar( $f->cleanup->dir_list ) - $self->{_root_depth} );
76              
77             }
78             else {
79 45         3118 $f = Path::Class::Iterator::File->new( $path, $next );
80 45         28550 my $p = $f->parent->cleanup;
81 45         7311 $self->{_depth}
82             = ( scalar( $p->dir_list ) - $self->{_root_depth} + 1 );
83              
84             }
85              
86 81         6543 return $f;
87              
88             }
89 40         501 );
90             }
91              
92             sub next {
93 81     81 1 188 my $self = shift;
94 81         370 my $depth = $self->cur_depth;
95 81         1377 my $n = $self->iterator->value;
96 81         3827 $n->depth($depth);
97 81         735 return $n;
98             }
99              
100             sub done {
101 85     85 1 35656 my $self = shift;
102 85         485 return $self->iterator->is_exhausted;
103             }
104              
105 81     81 1 183 sub cur_depth { return $_[0]->{_depth} }
106              
107             sub new {
108 5     5 1 8424 my $proto = shift;
109 5   33     141 my $class = ref($proto) || $proto;
110 5         16 my $self = {};
111 5         35 my %opts = @_;
112 5         34 @$self{ keys %opts } = values %opts;
113 5         20 bless( $self, $class );
114 5         88 $self->mk_accessors(@acc);
115              
116 5         3760 $self->start( time() );
117 5         88 $self->{_depth} = 0; # internal tracking
118              
119 5 50       27 $self->root or croak "root param required";
120 5         54 $self->root( dir( $self->root ) );
121 5 100       792 unless ( $self->root->open ) {
122 1         149 $Err = $self->root . " cannot be opened: $!";
123 1         59 return undef;
124             }
125              
126 4         603 $self->{_root_depth} = scalar( $self->root->dir_list );
127              
128             $self->error_handler(
129             sub {
130 0     0   0 my ( $self, $path, $msg ) = @_;
131 0 0       0 warn "skipping $path: $msg" if $self->show_warnings;
132 0         0 return 1;
133             }
134 4 100       239 ) unless $self->error_handler;
135              
136             $self->breadth_first
137             ? $self->pop_queue(
138             sub {
139 9     9   46 my $self = shift;
140 9         15 return pop( @{ $self->{queue} } );
  9         33  
141             }
142             )
143             : $self->pop_queue(
144             sub {
145 27     27   153 my $self = shift;
146 27         38 return shift( @{ $self->{queue} } );
  27         128  
147             }
148 4 100       50 );
149              
150             $self->push_queue(
151 4     36   112 sub { my $self = shift; push( @{ $self->{queue} }, @_ ); } );
  36         199  
  36         588  
  36         106  
152              
153 4         32 my $files = $self->_listing( $self->root );
154              
155 4         259 $self->queue( [] );
156             $self->iterator(
157             Iterator->new(
158             sub {
159              
160             # If no more files in current directory,
161             # get next directory off the queue
162 85     85   2904 while ( $files->is_exhausted ) {
163              
164             # Nothing else on the queue? Then we're done .
165 40 100       16453 if ( !$self->queue->[0] ) {
166 4         25 undef $files; # allow garbage collection
167 4         14 Iterator::is_done();
168             }
169              
170             # Create an iterator to return the files in that directory
171 36 50       580 carp Data::Dump::pp( $self->queue ) if $debug;
172              
173 36         135 $files = $self->_listing( $self->pop_queue->($self) );
174             }
175              
176             # Get next file in current directory
177 81         2486 my $next = $files->value;
178              
179 81 100       50517 if ( !$self->follow_symlinks ) {
180 54   33     2071 while ( -l $next && $files->isnt_exhausted ) {
181 0         0 $next = $files->value;
182             }
183             }
184              
185             # remember dirs for recursing later
186             # unless they exceed depth
187 81 50       7733 carp join( "\n", '=' x 50, "$next", $self->cur_depth )
188             if $debug;
189 81 100       316 if ( -d $next ) {
190              
191             # BUG?? does checking cur_depth() here invoke our bug?
192              
193 36 50 33     1344 unless ( $self->depth
194             && $self->cur_depth > $self->depth )
195             {
196 36         351 $self->push_queue->( $self, $next );
197 36 100       741 if ( $self->interesting ) {
198 3         23 my $new
199             = $self->interesting->( $self, $self->queue );
200 3 50       180 croak
201             "return value from interesting() must be an ARRAY ref"
202             unless ref $new eq 'ARRAY';
203 3         11 $self->queue($new);
204             }
205             }
206             }
207              
208 81         4600 return $next;
209             }
210             )
211 4         77 );
212              
213 4         102 return $self;
214             }
215              
216             1;
217              
218             package Path::Class::Iterator::File;
219 6     6   61753 use base qw( Path::Class::File Class::Accessor::Fast );
  6         15  
  6         1941  
220             __PACKAGE__->mk_accessors('depth');
221              
222             1;
223              
224             package Path::Class::Iterator::Dir;
225 6     6   37 use base qw( Path::Class::Dir Class::Accessor::Fast );
  6         10  
  6         975  
226             __PACKAGE__->mk_accessors('depth');
227              
228             1;
229              
230             __END__