File Coverage

blib/lib/IO/Handle/Prototype/Fallback.pm
Criterion Covered Total %
statement 125 154 81.1
branch 41 54 75.9
condition 7 8 87.5
subroutine 29 40 72.5
pod 0 1 0.0
total 202 257 78.6


line stmt bran cond sub pod time code
1             package IO::Handle::Prototype::Fallback;
2              
3 2     2   41507 use strict;
  2         5  
  2         79  
4 2     2   13 use warnings;
  2         9  
  2         98  
5              
6 2     2   15 use Carp ();
  2         3  
  2         55  
7              
8 2     2   6392 use parent qw(IO::Handle::Prototype);
  2         372  
  2         15  
9              
10             sub new {
11 20     20 0 13973 my ( $class, @args ) = @_;
12              
13 20         76 $class->SUPER::new(
14             $class->_process_callbacks(@args),
15             );
16             }
17              
18 19     19   74 sub __write { shift->_cb(__write => @_) }
19 0     0   0 sub __read { shift->_cb(__read => @_) }
20              
21             sub _process_callbacks {
22 35     35   245 my ( $class, %user_cb ) = @_;
23              
24 35 100       114 if ( keys %user_cb == 1 ) {
25             # these callbacks require wrapping of the user's callback to add
26             # buffering, so we short circuit the entire process
27 20         48 foreach my $fallback (qw(__read read getline)) {
28 45 100       483 if ( my $cb = $user_cb{$fallback} ) {
29 15         43 my $method = "_default_${fallback}_callbacks";
30              
31 15         63 return $class->_process_callbacks(
32             $class->$method($cb),
33             );
34             }
35             }
36             }
37              
38 20         69 my @fallbacks = $class->_base_callbacks;
39              
40             # additional fallbacks based on explicitly provided callbacks
41              
42 20         72 foreach my $fallback (qw(__write print write syswrite)) {
43 71 100       188 if ( exists $user_cb{$fallback} ) {
44 5         20 push @fallbacks, $class->_default_write_callbacks($fallback);
45 5         16 last;
46             }
47             }
48              
49 20 100       93 if ( exists $user_cb{getline} ) {
50 15         48 push @fallbacks, $class->_simple_getline_callbacks;
51             }
52              
53 20 100       66 if ( exists $user_cb{read} ) {
54 15         41 push @fallbacks, $class->_simple_read_callbacks;
55             }
56              
57             # merge everything
58 20         193 my %cb = (
59             @fallbacks,
60             %user_cb,
61             );
62              
63 20         204 return \%cb;
64             }
65              
66             sub _base_callbacks {
67 20     20   34 my $class = shift;
68              
69             return (
70 0     0   0 fileno => sub { undef },
71 0     0   0 stat => sub { undef },
72 0     0   0 opened => sub { 1 },
73             blocking => sub {
74 0     0   0 my ( $self, @args ) = @_;
75              
76 0 0       0 Carp::croak("Can't set blocking mode on iterator") if @args;
77              
78 0         0 return 1;
79             },
80 20         205 );
81             }
82              
83             sub _make_read_callbacks {
84 15     15   32 my ( $class, $read ) = @_;
85              
86 2     2   1065 no warnings 'uninitialized';
  2         5  
  2         4096  
87              
88             return (
89             # these fallbacks must wrap the underlying reading mechanism
90             __read => sub {
91 0     0   0 my $self = shift;
92 0 0       0 if ( exists $self->{buf} ) {
93 0         0 return delete $self->{buf};
94             } else {
95 0         0 my $ret = $self->$read;
96              
97 0 0       0 unless ( defined $ret ) {
98 0         0 $self->{eof}++;
99             }
100              
101 0         0 return $ret;
102             }
103             },
104             getline => sub {
105 65     65   86 my $self = shift;
106              
107 65 100       201 return undef if $self->{eof};
108              
109 60 100       185 if ( ref $/ ) {
    100          
110 5         12 $self->read(my $ret, ${$/});
  5         26  
111 5         20 return $ret;
112             } elsif ( defined $/ ) {
113             getline: {
114 50 100 100     58 if ( defined $self->{buf} and (my $off = index($self->{buf}, $/)) > -1 ) {
  174         1010  
115 45         301 return substr($self->{buf}, 0, $off + length($/), '');
116             } else {
117 129 100       315 if ( defined( my $chunk = $self->$read ) ) {
118 124         707 $self->{buf} .= $chunk;
119 124         211 redo getline;
120             } else {
121 5         40 $self->{eof}++;
122              
123 5 50       26 if ( length( my $buf = delete $self->{buf} ) ) {
124 0         0 return $buf;
125             } else {
126 5         25 return undef;
127             }
128             }
129             }
130             }
131             } else {
132 5         19 my $ret = delete $self->{buf};
133              
134 5         15 while ( defined( my $chunk = $self->$read ) ) {
135 26         153 $ret .= $chunk;
136             }
137              
138 5         32 $self->{eof}++;
139              
140 5         24 return $ret;
141             }
142             },
143             read => sub {
144 35     35   82 my ( $self, undef, $length, $offset ) = @_;
145              
146 35 50       121 return 0 if $self->{eof};
147              
148 35 50 66     120 if ( $offset and length($_[1]) < $offset ) {
149 0         0 $_[1] .= "\0" x ( $offset - length($_[1]) );
150             }
151              
152 35         115 while (length($self->{buf}) < $length) {
153 58 100       113 if ( defined(my $next = $self->$read) ) {
154 53         413 $self->{buf} .= $next;
155             } else {
156             # data ended but still under $length, return all that remains and
157             # empty the buffer
158 5         28 my $ret = length($self->{buf});
159              
160 5 50       12 if ( $offset ) {
161 0         0 substr($_[1], $offset) = delete $self->{buf};
162             } else {
163 5         17 $_[1] = delete $self->{buf};
164             }
165              
166 5         14 $self->{eof}++;
167 5         25 return $ret;
168             }
169             }
170              
171 30         40 my $read;
172 30 50       80 if ( $length > length($self->{buf}) ) {
173 0         0 $read = delete $self->{buf};
174             } else {
175 30         92 $read = substr($self->{buf}, 0, $length, '');
176             }
177              
178 30 100       62 if ( $offset ) {
179 5         10 substr($_[1], $offset) = $read;
180             } else {
181 25         43 $_[1] = $read;
182             }
183              
184 30         126 return length($read);
185             },
186             eof => sub {
187 50     50   70 my $self = shift;
188 50         385 $self->{eof};
189             },
190             ungetc => sub {
191 5     5   12 my ( $self, $ord ) = @_;
192              
193 5         25 substr( $self->{buf}, 0, 0, chr($ord) );
194              
195 5         16 return;
196             },
197 15         252 );
198             }
199              
200             sub _default___read_callbacks {
201 6     6   14 my ( $class, $read ) = @_;
202              
203 6         20 $class->_make_read_callbacks($read);
204             }
205              
206             sub _default_read_callbacks {
207 3     3   7 my ( $class, $read ) = @_;
208              
209             $class->_make_read_callbacks(sub {
210 6     6   8 my $self = shift;
211              
212 6 50       33 if ( $self->$read(my $buf, ref $/ ? ${ $/ } : 4096) ) {
  0 100       0  
213 3         32 return $buf;
214             } else {
215 3         34 return undef;
216             }
217 3         27 });
218             }
219              
220             sub _default_getline_callbacks {
221 6     6   14 my ( $class, $getline ) = @_;
222              
223             $class->_make_read_callbacks(sub {
224 106 100   106   367 local $/ = ref $/ ? $/ : \4096;
225 106         260 $_[0]->$getline;
226 6         34 });
227             }
228              
229             sub _simple_read_callbacks {
230 15     15   26 my $class = shift;
231              
232             return (
233             # these are generic fallbacks defined in terms of the wrapping ones
234             sysread => sub {
235 0     0   0 shift->read(@_);
236             },
237             getc => sub {
238 10     10   27 my $self = shift;
239              
240 10 50       31 if ( $self->read(my $str, 1) ) {
241 10         48 return $str;
242             } else {
243 0         0 return undef;
244             }
245             },
246 15         87 );
247             }
248              
249             sub _simple_getline_callbacks {
250 15     15   23 my $class = shift;
251              
252             return (
253             getlines => sub {
254 5     5   13 my $self = shift;
255              
256 5         8 my @accum;
257              
258 5         20 while ( defined(my $next = $self->getline) ) {
259 25         96 push @accum, $next;
260             }
261              
262 5         38 return @accum;
263             }
264 15         80 );
265             }
266              
267             sub _default_write_callbacks {
268 5     5   11 my ( $class, $canonical ) = @_;
269              
270             return (
271 0     0   0 autoflush => sub { 1 },
272 0     0   0 sync => sub { },
273 0     0   0 flush => sub { },
274              
275             # these are defined in terms of a canonical print method, either write,
276             # syswrite or print
277             __write => sub {
278 14     14   31 my ( $self, $str ) = @_;
279 14         36 local $\;
280 14         17 local $,;
281 14         55 $self->$canonical($str);
282             },
283             print => sub {
284 14     14   28 my $self = shift;
285 14 100       39 my $ofs = defined $, ? $, : '';
286 14 100       48 my $ors = defined $\ ? $\ : '';
287 14         70 $self->__write( join($ofs, @_) . $ors );
288             },
289              
290             (map { $_ => sub {
291 5     5   16 my ( $self, $str, $len, $offset ) = @_;
292 5 100       23 $len = length($str) unless defined $len;
293 5   100     24 $offset ||= 0;
294 5         23 $self->__write(substr($str, $offset, $len));
295 10         103 } } qw(write syswrite)),
296              
297             # wrappers for print
298             printf => sub {
299 3     3   11 my ( $self, $f, @args ) = @_;
300 3         21 $self->print(sprintf $f, @args);
301             },
302             say => sub {
303 4     4   20 local $\ = "\n";
304 4         20 shift->print(@_);
305             },
306             printflush => sub {
307 0     0     my $self = shift;
308 0           my $autoflush = $self->autoflush;
309 0           my $ret = $self->print(@_);
310 0           $self->autoflush($autoflush);
311 0           return $ret;
312             }
313 5         48 );
314             }
315              
316             __PACKAGE__
317              
318             # ex: set sw=4 et:
319              
320             __END__
321              
322             =pod
323              
324             =head1 NAME
325              
326             IO::Handle::Prototype::Fallback - Create L<IO::Handle> like objects using a set
327             of callbacks.
328              
329             =head1 SYNOPSIS
330              
331             my $fh = IO::Handle::Prototype::Fallback->new(
332             getline => sub {
333             my $fh = shift;
334              
335             ...
336             },
337             );
338              
339             =head1 DESCRIPTION
340              
341             This class provides a way to define a filehandle based on callbacks.
342              
343             Fallback implementations are provided to the extent possible based on the
344             provided callbacks, for both writing and reading.
345              
346             =head1 SPECIAL CALLBACKS
347              
348             This class provides two additional methods on top of L<IO::Handle>, designed to
349             let you implement things with a minimal amount of baggage.
350              
351             The fallback methods are all best implemented using these, though these can be
352             implemented in terms of Perl's standard methods too.
353              
354             However, to provide the most consistent semantics, it's better to do this:
355              
356             IO::Handle::Prototype::Fallback->new(
357             __read => sub {
358             shift @array;
359             },
360             );
361              
362             Than this:
363              
364             IO::Handle::Prototype::Fallback->new(
365             getline => sub {
366             shift @array;
367             },
368             );
369              
370             Because the fallback implementation of C<getline> implements all of the extra
371             crap you'd need to handle to have a fully featured implementation.
372              
373             =over 4
374              
375             =item __read
376              
377             Return a chunk of data of any size (could use C<$/> or not, it depends on you,
378             unlike C<getline> which probably I<should> respect the value of C<$/>).
379              
380             This avoids the annoying C<substr> stuff you need to do with C<read>.
381              
382             =item __write $string
383              
384             Write out a string.
385              
386             This is like a simplified C<print>, which can disregard C<$,> and C<$\> as well
387             as multiple argument forms, and does not have the extra C<substr> annoyance of
388             C<write> or C<syswrite>.
389              
390             =back
391              
392             =head1 WRAPPING
393              
394             If you provide a B<single> reading related callback (C<__read>, C<getline> or
395             C<read>) then your callback will be used to implement all of the other reading
396             primitives using a string buffer.
397              
398             These implementations handle C<$/> in all forms (C<undef>, ref to number and
399             string), all the funny calling conventions for C<read>, etc.
400              
401             =head1 FALLBACKS
402              
403             Any callback that can be defined purely in terms of other callbacks in a way
404             will be added. For instance C<getc> can be implemented in terms of C<read>,
405             C<say> can be implemented in terms of C<print>, C<print> can be implemented in
406             terms of C<write>, C<write> can be implemented in terms of C<print>, etc.
407              
408             None of these require special wrapping and will always be added if their
409             dependencies are present.
410              
411             =head1 GLOB OVERLOADING
412              
413             When overloaded as a glob a tied handle will be returned. This allows you to
414             use the handle in Perl's IO builtins. For instance:
415              
416             my $line = <$fh>
417              
418             will not call the C<getline> method natively, but the tied interface arranges
419             for that to happen.
420              
421             =cut