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   292829 use strict;
  6         18  
  6         253  
4 6     6   34 use warnings;
  6         10  
  6         168  
5 6     6   8501 use Path::Class;
  6         863690  
  6         584  
6 6     6   69 use Carp;
  6         12  
  6         1056  
7 6     6   7867 use Iterator;
  6         236222  
  6         258  
8              
9 6     6   550 use base qw/ Class::Accessor::Fast /;
  6         13  
  6         11381  
10              
11             our $VERSION = '0.07';
12             our $Err;
13             our $debug = $ENV{PERL_TEST} || $ENV{PERL_DEBUG} || 0;
14              
15             if ($debug)
16             {
17             require Data::Dump;
18             }
19              
20             my @acc = qw/
21             root
22             start
23             follow_symlinks
24             follow_hidden
25             iterator
26             error_handler
27             error
28             show_warnings
29             breadth_first
30             interesting
31             push_queue
32             pop_queue
33             queue
34             depth
35              
36             /;
37              
38             sub _listing
39             {
40 37     37   78 my $self = shift;
41 37         52 my $path = shift;
42              
43 37         145 my $d = $path->open;
44              
45 37 50       3134 unless (defined $d)
46             {
47 0         0 $self->error("cannot open $path: $!");
48 0 0       0 if ($self->error_handler->($self, $path, $!))
49             {
50 0     0   0 return Iterator->new(sub { Iterator::is_done(); return undef });
  0         0  
  0         0  
51             }
52             else
53             {
54 0         0 croak "can't open $path: $!";
55             }
56             }
57              
58             return Iterator->new(
59             sub {
60              
61             # Get next file, skipping . and ..
62 111     111   1270 my $next;
63 111         121 while (1)
64             {
65 193         1431 $next = $d->read;
66              
67 193 100       1926 if (!defined $next)
68             {
69 37         64 undef $d; # allow garbage collection
70 37         109 Iterator::is_done();
71             }
72              
73 156 100 66     386 next if !$self->follow_hidden && $next =~ m/^\./o;
74              
75 74 50 33     906 last if $next ne '.' && $next ne '..';
76             }
77              
78             # Return this item
79 74         379 my $f = Path::Class::Iterator::Dir->new($path, $next);
80 74 100       5063 if (-d $f)
81             {
82 33         1136 $self->{_depth} =
83             (scalar($f->cleanup->dir_list) - $self->{_root_depth});
84              
85             }
86             else
87             {
88 41         1762 $f = Path::Class::Iterator::File->new($path, $next);
89 41         4566 my $p = $f->parent->cleanup;
90 41         3456 $self->{_depth} =
91             (scalar($p->dir_list) - $self->{_root_depth} + 1);
92              
93             }
94              
95 74         3066 return $f;
96              
97             }
98 37         317 );
99             }
100              
101             sub next
102             {
103 74     74 1 106 my $self = shift;
104 74         143 my $depth = $self->cur_depth;
105 74         183 my $n = $self->iterator->value;
106 74         2969 $n->depth($depth);
107 74         505 return $n;
108             }
109              
110             sub done
111             {
112 78     78 1 11682 my $self = shift;
113 78         220 return $self->iterator->is_exhausted;
114             }
115              
116 74     74 1 153 sub cur_depth { return $_[0]->{_depth} }
117              
118             sub new
119             {
120 5     5 1 25431 my $proto = shift;
121 5   33     53 my $class = ref($proto) || $proto;
122 5         16 my $self = {};
123 5         35 my %opts = @_;
124 5         38 @$self{keys %opts} = values %opts;
125 5         21 bless($self, $class);
126 5         508 $self->mk_accessors(@acc);
127              
128 5         5599 $self->start(time());
129 5         83 $self->{_depth} = 0; # internal tracking
130              
131 5 50       25 $self->root or croak "root param required";
132 5         58 $self->root(dir($self->root));
133 5 100       699 unless ($self->root->open)
134             {
135 1         128 $Err = $self->root . " cannot be opened: $!";
136 1         47 return undef;
137             }
138              
139 4         620 $self->{_root_depth} = scalar($self->root->dir_list);
140              
141             $self->error_handler(
142             sub {
143 0     0   0 my ($self, $path, $msg) = @_;
144 0 0       0 warn "skipping $path: $msg" if $self->show_warnings;
145 0         0 return 1;
146             }
147             )
148 4 100       1325 unless $self->error_handler;
149              
150             $self->breadth_first
151             ? $self->pop_queue(
152             sub {
153 9     9   46 my $self = shift;
154 9         12 return pop(@{$self->{queue}});
  9         37  
155             }
156             )
157             : $self->pop_queue(
158             sub {
159 24     24   97 my $self = shift;
160 24         26 return shift(@{$self->{queue}});
  24         77  
161             }
162 4 100       51 );
163              
164 4     33   132 $self->push_queue(sub { my $self = shift; push(@{$self->{queue}}, @_); });
  33         155  
  33         43  
  33         95  
165              
166 4         36 my $files = $self->_listing($self->root);
167              
168 4         137 $self->queue([]);
169             $self->iterator(
170             Iterator->new(
171             sub {
172              
173             # If no more files in current directory,
174             # get next directory off the queue
175 78     78   987 while ($files->is_exhausted)
176             {
177              
178             # Nothing else on the queue? Then we're done .
179 37 100       7987 if (!$self->queue->[0])
180             {
181 4         26 undef $files; # allow garbage collection
182 4         14 Iterator::is_done();
183             }
184              
185             # Create an iterator to return the files in that directory
186 33 50       397 carp Data::Dump::pp($self->queue) if $debug;
187              
188 33         100 $files = $self->_listing($self->pop_queue->($self));
189             }
190              
191             # Get next file in current directory
192 74         1068 my $next = $files->value;
193              
194 74 100       42309 if (!$self->follow_symlinks)
195             {
196 47   33     331 while (-l $next && $files->isnt_exhausted)
197             {
198 0         0 $next = $files->value;
199             }
200             }
201              
202             # remember dirs for recursing later
203             # unless they exceed depth
204 74 50       2103 carp join("\n", '=' x 50, "$next", $self->cur_depth) if $debug;
205 74 100       257 if (-d $next)
206             {
207              
208             # BUG?? does checking cur_depth() here invoke our bug?
209              
210 33 50 33     1067 unless ( $self->depth
211             && $self->cur_depth > $self->depth)
212             {
213 33         271 $self->push_queue->($self, $next);
214 33 100       104 if ($self->interesting)
215             {
216 3         22 my $new = $self->interesting->($self, $self->queue);
217 3 50       218 croak
218             "return value from interesting() must be an ARRAY ref"
219             unless ref $new eq 'ARRAY';
220 3         9 $self->queue($new);
221             }
222             }
223             }
224              
225 74         2309 return $next;
226             }
227             )
228 4         61 );
229              
230 4         97 return $self;
231             }
232              
233             1;
234              
235              
236             package Path::Class::Iterator::File;
237 6     6   88539 use base qw( Path::Class::File Class::Accessor::Fast );
  6         18  
  6         2426  
238             __PACKAGE__->mk_accessors('depth');
239              
240             1;
241              
242             package Path::Class::Iterator::Dir;
243 6     6   36 use base qw( Path::Class::Dir Class::Accessor::Fast );
  6         10  
  6         952  
244             __PACKAGE__->mk_accessors('depth');
245              
246             1;
247              
248              
249             __END__