File Coverage

blib/lib/Path/Class/Each.pm
Criterion Covered Total %
statement 61 62 98.3
branch 22 26 84.6
condition 3 3 100.0
subroutine 13 14 92.8
pod 0 4 0.0
total 99 109 90.8


line stmt bran cond sub pod time code
1             package Path::Class::Each;
2              
3 2     2   173532 use warnings;
  2         5  
  2         66  
4 2     2   10 use strict;
  2         5  
  2         66  
5              
6 2     2   9 use Carp qw( croak );
  2         7  
  2         108  
7 2     2   786 use Path::Class;
  2         71774  
  2         1017  
8              
9             our $VERSION = '0.03';
10              
11             =head1 NAME
12              
13             Path::Class::Each - Iterate lines in a file
14              
15             =head1 VERSION
16              
17             This document describes Path::Class::Each version 0.03
18              
19             =head1 SYNOPSIS
20              
21             use Path::Class;
22             use Path::Class::Each;
23              
24             ## Files ##
25              
26             # Iterator interface
27             my $iter = file( 'foo', 'bar' )->iterator;
28             while ( defined( my $line = $iter->() ) ) {
29             print "Line: $line\n";
30             }
31              
32             # 'next' interface
33             my $file = file( 'foo', 'bar' );
34             while ( defined( my $line = $file->next ) ) {
35             print "Line: $line\n";
36             }
37              
38             # Callback interface
39             file( 'foo', 'bar' )->each(
40             sub {
41             print "Line: $_\n";
42             }
43             );
44              
45             ## Directories ##
46            
47             # Iterator interface
48             my $iter = dir( 'foo', 'bar' )->iterator;
49             while ( defined( my $file = $iter->() ) ) {
50             print "File: $file\n";
51             }
52              
53             # 'next' interface
54             my $file = dir( 'foo', 'bar' );
55             while ( defined( my $file = $dir->next_file ) ) {
56             print "File: $file\n";
57             }
58              
59             # Callback interface
60             dir( 'foo', 'bar' )->each(
61             sub {
62             print "File: $_\n";
63             }
64             );
65              
66             =head1 DESCRIPTION
67              
68             C augments L and
69             L to provide three different ways of iterating over
70             the lines of a file and three ways of iterating the files recursively
71             contained in a directory.
72              
73             C provides a C method that returns the
74             contents of a file (either as a scalar or an array) but has no support
75             for reading a file a line at a time. For large files it may be desirable
76             to iterate through the lines; that's where this module comes in.
77              
78             C provides C which returns the files and
79             directories immediately contained in a directory but does not expose a
80             'find' interface to recursively search a directory. This module provides
81             iterators that visit all the files in the directory tree below a
82             directory.
83              
84             =head1 INTERFACE
85              
86             =head2 File Iterators
87              
88             =head3 C<< Path::Class::File->iterator >>
89              
90             Get an iterator that returns the lines in a file. Returns C when
91             there are no more lines to return.
92              
93             my $iter = file( 'foo', 'bar' )->iterator;
94             while ( defined( my $line = $iter->() ) ) {
95             print "Line: $line\n";
96             }
97              
98             If the file can not be opened an exception will be thrown (using
99             C).
100              
101             The following options may be passed as key, value pairs:
102              
103             =over
104              
105             =item C<< chomp >>
106              
107             Newlines will be trimmed from each line read.
108              
109             =item C<< iomode >>
110              
111             Passed as the C argument to C. See
112             L for details. If omitted defaults to 'r'
113             (read-only).
114              
115             =back
116              
117             Here's how options are passed:
118              
119             my $chomper = file('foo', 'bar')->iterator( chomp => 1 );
120              
121             =cut
122              
123             sub Path::Class::File::iterator {
124 6     6 0 1052 my $self = shift;
125 6         10 my @opt = @_;
126              
127 6 50       16 croak "each requires a number of name => value options"
128             if @opt % 2;
129              
130 6         16 my %opt = ( @opt, iomode => 'r' );
131 6         11 my $mode = delete $opt{iomode};
132 6         8 my $chomp = delete $opt{chomp};
133              
134 6 50       13 croak "Unknown options: ", join ', ', sort keys %opt
135             if keys %opt;
136              
137 6 50       17 my $fh = $self->open( $mode ) or croak "Can't read $self: $!\n";
138             return sub {
139 42     42   172 my $line = <$fh>;
140 42 100       94 return unless defined $line;
141 36 100       57 chomp $line if $chomp;
142 36         72 return $line;
143 6         755 };
144             }
145              
146             =head3 C<< Path::Class::File->next >>
147              
148             Return the next line from a file. Returns C when all lines have
149             been read.
150              
151             Internally L is called if necessary to create a new iterator.
152             The same options that L accepts may be passed to C:
153              
154             my $file = file( 'foo', 'bar' );
155             while ( defined( my $line = $file->next( chomp => 1 ) ) ) {
156             print "Line: $line\n";
157             }
158              
159             =head4 NOTE
160              
161             It may be tempting to use an idiom like:
162              
163             # DON'T DO THIS
164             while ( my $line = file('foo')->next ) {
165             ...
166             }
167              
168             That will create a new C and, therefore, a new
169             iterator each time it is called with the result that the first line of
170             the file will be returned repeatedly.
171              
172             =head3 C<< Path::Class::File->each >>
173              
174             Call a supplied callback for each line in a file. The same options that
175             L accepts may be passed:
176              
177             file( 'foo', 'bar' )->each( chomp => 1, sub { print "Line: $_\n" } );
178              
179             Within the callback the current line will be in C<$_>.
180              
181             =head2 Directory Iterators
182              
183             =head3 C<< Path::Class::Dir->iterator >>
184              
185             Return an iterator that returns each of the files in and below a
186             directory.
187              
188             By default only files are returned. The following options may be
189             supplied to modify this behaviour:
190              
191             =over
192              
193             =item C<< dirs >>
194              
195             Return directories as well as files.
196              
197             =item C<< no_files >>
198              
199             Return directories only.
200              
201             =back
202              
203             =cut
204              
205             sub Path::Class::Dir::iterator {
206 5     5 0 1593 my ( $self, @opt ) = @_;
207              
208 5 50       14 croak "each requires a number of name => value options"
209             if @opt % 2;
210              
211 5         11 my %opt = @opt;
212 5         9 my $dirs = delete $opt{dirs};
213 5         9 my $no_files = delete $opt{no_files};
214              
215 5         17 my @queue = $self->children( @opt );
216             return sub {
217 25 100       54 TRY: {
218 19     19   92 return unless @queue;
219 20         26 my $obj = shift @queue;
220 20 100       89 if ( $obj->isa( 'Path::Class::Dir' ) ) {
221 5         24 unshift @queue, $obj->children( @opt );
222 5 100 100     1711 redo TRY unless $dirs || $no_files;
223             }
224             else {
225 15 100       39 redo TRY if $no_files;
226             }
227 14         28 return $obj;
228             }
229 5         8243 };
230             }
231              
232             =head3 C<< Path::Class::Dir->next_file >>
233              
234             Return the next file from a recursive search of a directory. Returns
235             C when all lines have been read.
236              
237             Internally L is called if necessary to create a new iterator.
238             The same options that L accepts may be passed to C:
239              
240             my $dir = dir( 'foo', 'bar' );
241             while ( defined( my $file = $dir->next_file ) ) {
242             print "File: $file\n";
243             }
244              
245             =head3 C<< Path::Class::Dir->next_dir >>
246              
247             Return the next directory from a recursive search of a directory.
248              
249             =cut
250              
251 2     2 0 1326 sub Path::Class::Dir::next_dir { shift->next_file( no_files => 1 ) }
252              
253             =head3 C<< Path::Class::Dir->each >>
254              
255             Call a supplied callback for each file in a directory. The same options
256             that L accepts may be passed:
257              
258             dir( 'foo', 'bar' )->each( dirs => 1, sub { print "Object: $_\n" } );
259              
260             Within the callback the current file will be in C<$_>.
261              
262             =head3 C<< Path::Class::Dir->each_dir >>
263              
264             Call a supplied callback for each subdirectory in a directory.
265              
266             =cut
267              
268 0     0 0   sub Path::Class::Dir::each_dir { shift->each( no_files => 1, @_ ) }
269              
270             BEGIN {
271 2     2   6 my @extend = qw( Path::Class::File Path::Class::Dir );
272 2         5 for my $class ( @extend ) {
273 2     2   12 no strict 'refs';
  2         3  
  2         397  
274 4         22 *{"${class}::each"} = sub {
275 3     3   4014 my ( $self, @opt ) = @_;
276 3         5 my $cb = pop @opt;
277 3         10 my $iter = $self->iterator( @opt );
278 3         7 while ( defined( local $_ = $iter->() ) ) { $cb->() }
  15         29  
279 4         22 };
280 4 100       14 my $next = $class eq 'Path::Class::Dir' ? 'next_file' : 'next';
281 4         65 *{"${class}::${next}"} = sub {
282 20     20   2002 my $self = shift;
283 20 100       49 $self->{_iter} = $self->iterator( @_ ) unless $self->{_iter};
284 20         34 my $line = $self->{_iter}->();
285 20 100       59 delete $self->{_iter} unless defined $line;
286 20         44 return $line;
287 4         13 };
288             }
289             }
290              
291             1;
292             __END__