File Coverage

blib/lib/Plack/Middleware/AdaptFilehandleRead/Proxy.pm
Criterion Covered Total %
statement 31 33 93.9
branch 12 16 75.0
condition 2 2 100.0
subroutine 5 5 100.0
pod 1 2 50.0
total 51 58 87.9


line stmt bran cond sub pod time code
1 2     2   147630 use strict;
  2         6  
  2         71  
2 2     2   11 use warnings;
  2         4  
  2         1014  
3              
4             package Plack::Middleware::AdaptFilehandleRead::Proxy;
5              
6             sub new {
7 3     3 0 4490 my ($class, $target, $chunksize) = @_;
8 3 50       48 die "$target doens't have a read method" unless $target->can('read');
9 3   100     61 return bless +{ _t => $target, _cs => ($chunksize|| 65536), _buff=> ''}, $class;
10             }
11              
12             sub getline {
13 30614     30614 1 148671 my $fh = (my $self = shift)->{_t};
14              
15 30614 50       77994 if(!defined($/)) {
    100          
    50          
16             # slurp mode, we pray we never see this..
17 0         0 die "Slurping not supported, patches welcomed";
18             } elsif(ref($/)) {
19             # fixed chunk mode
20 22612         23833 my $chunk;
21 22612         23252 my $len = +${$/}; # can be \4096 or \'4096' some numify
  22612         34461  
22 22612 100       66129 if($fh->read($chunk,$len)) {
23 22610         159310 return $chunk;
24             } else {
25 2         21 return;
26             }
27             } elsif($/ eq "") {
28             # Nullstring mode
29 0         0 die "Null not supported, patches welcomed";
30             } else {
31             # We assume character delim mode... There's a few other weird options
32             # but I doubt we'll see them in the context of a Plack handler.
33              
34             # If the buffer is undef, that means we've read it all
35 8002 50       16445 return unless defined($self->{_buff});
36             # If the current temporary read buffer has a match for $/
37 8002 100       22411 if( (my $idx = index($self->{_buff}, $/)) >= 0) {
38             #remove from the start of the buffer to the match and return it
39 8000         15638 my $line = substr($self->{_buff}, 0, $idx+1);
40 8000         73103 $self->{_buff} = substr($self->{_buff},$idx+1); # truncate current buffer
41 8000         22356 return $line;
42             } else {
43             # read a chunk into the temporary buffer and try again
44 2         5 my $chunk;
45 2         16 $fh->read($chunk,$self->{_cs});
46 2 100       113 if($chunk) {
47 1         91 $self->{_buff} .= $chunk;
48 1         9 return $self->getline;
49             } else {
50             # no more chunks? just return what is left and set the buffer to undef
51             # so we know to stop asking.
52 1         8 return my $last_line = delete $self->{_buff};
53             }
54             }
55             }
56             }
57              
58             sub AUTOLOAD {
59 1     1   16 my ($self, @args) = @_;
60 1         13 my ($method) = (our $AUTOLOAD =~ /([^:]+)$/);
61 1         16 return $self->{_t}->$method(@args)
62             }
63              
64             1;
65              
66             =head1 NAME
67            
68             Plack::Middleware::AdaptFilehandleRead::Proxy - Wrap an object to supply missing getline
69              
70             =head1 SYNOPSIS
71              
72             my $new_fh = Plack::Middleware::AdaptFilehandleRead::Proxy->new($old_fh);
73             my $line = $new_fh->getline;
74            
75             =head1 DESCRIPTION
76              
77             Wraps a filehandle like object that has a read method and provides a getline
78             method that works as in L All other method calls will be
79             delegated to the original object.
80              
81             This is used primarily to adapt a filehandle like object that does C
82             but not C so that you can use it as the body value for a L
83             response. For example, L can return such a custom filehandle
84             like object and you may wish to use that response to stream via a L
85             application.
86              
87             When adapting C to C we examine the state of C<$/> in order to
88             figure out what to do. For the normal case, if $/ is a simple value (such as
89             /n or newline, which is the default) we call C and ask for chunks of 65536
90             bytes. This may or may not be ideal for your data, in which case you may wish
91             to override it as so:
92              
93             my $new_fh = Plack::Middleware::AdaptFilehandleRead::Proxy
94             ->new($old_fh, $chunksize);
95              
96             Please be aware that the chunksize is read into memory.
97              
98             We then examine the chunk and return records as indicated by the deliminator.
99             When the chunk does not have such a match, we read chunks until it does or we
100             read the entire file.
101              
102             For the case when $/ is a scalar ref, such as $/ = \'4096' we will instead read
103             fixed sized chunks from ->read and ignore any C settings (you can
104             always localize $/ yourself if you prefer more control).
105              
106             Typically plack prefers C to return chunks of a predefined length and
107             all the common plack handers such as L, L set $/ when calling
108             C although as the L specification indicates this support of "$/"
109             is considered optional for a custom filehandle like body. We choose to support
110             it since it can help avoid a situation where your entire file gets read into
111             memory when the file does not contain newlines (or whatever $/ is set to).
112              
113             Currently this object delegates all method calls beyond C to the
114             underlying proxied object via AUTOLOAD. We do not attempt to proxy any
115             Ling (patches for this welcomed).
116              
117             =head1 METHODS
118              
119             This class defines the following methods
120              
121             =head2 getline
122              
123             returns a line from the file, as described by L, suitable for
124             the L requirement of a filehandle like object. It work by calling C
125             in chunks, and returns lines.
126              
127             =head2 AUTOLOAD
128              
129             Used to delegate all other method calls to the underlying wrapped instance.
130            
131             =head1 SEE ALSO
132            
133             L, L.
134            
135             =head1 AUTHOR
136            
137             John Napiorkowski L
138            
139             =head1 COPYRIGHT & LICENSE
140            
141             Copyright 2014, John Napiorkowski L
142            
143             This library is free software; you can redistribute it and/or modify it under
144             the same terms as Perl itself.
145            
146             =cut