File Coverage

blib/lib/Data/Handle.pm
Criterion Covered Total %
statement 155 160 96.8
branch 40 44 90.9
condition 7 9 77.7
subroutine 37 37 100.0
pod 1 1 100.0
total 240 251 95.6


line stmt bran cond sub pod time code
1 5     5   98593 use 5.006;
  5         15  
2 5     5   24 use strict;
  5         7  
  5         119  
3 5     5   21 use warnings;
  5         5  
  5         411  
4              
5             package Data::Handle;
6              
7             our $VERSION = '1.000001';
8              
9             # ABSTRACT: A Very simple interface to the __DATA__ file handle.
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13              
14              
15              
16              
17              
18              
19              
20              
21              
22              
23              
24              
25              
26              
27              
28              
29              
30              
31              
32              
33              
34              
35              
36              
37              
38              
39              
40              
41              
42              
43              
44              
45              
46              
47              
48              
49              
50              
51              
52              
53              
54              
55              
56              
57              
58              
59              
60              
61              
62              
63              
64              
65              
66              
67              
68              
69              
70              
71              
72              
73              
74              
75              
76              
77              
78              
79              
80              
81              
82              
83              
84              
85              
86              
87              
88              
89              
90              
91              
92              
93              
94              
95              
96              
97             my %datastash;
98 5     5   2566 use Symbol qw( gensym );
  5         3512  
  5         338  
99 5     5   27 use Scalar::Util qw( weaken );
  5         6  
  5         432  
100 5     5   2216 use parent qw( IO::File );
  5         1308  
  5         23  
101 5     5   38858 use Package::Stash 0.15; # has_symbol
  5         25259  
  5         157  
102 5     5   33 use Carp ();
  5         7  
  5         166  
103 5     5   1851 use Data::Handle::Exception;
  5         15  
  5         148  
104 5     5   2191 use Data::Handle::IO;
  5         8  
  5         129  
105 5     5   20 use Try::Tiny qw( try catch );
  5         5  
  5         6821  
106              
107              
108              
109              
110              
111              
112              
113              
114              
115             sub new {
116 28     28 1 17805 my ( $class, $targetpackage ) = @_;
117              
118 28 100       89 _e('NoSymbol')->throw("$targetpackage has no DATA symbol")
119             if ( !$class->_has_data_symbol($targetpackage) );
120              
121 27 100       110 if ( !$class->_is_valid_data_tell($targetpackage) ) {
122 1         3 _e('BadFilePos')
123             ->throw( "$targetpackage has a DATA symbol, but the filepointer"
124             . " is well beyond the __DATA__ section.\n"
125             . " We can't work out safely where it is.\n"
126             . $class->_stringify_metadata($targetpackage)
127             . "\n" );
128             }
129              
130 26         106 my $sym = gensym();
131 26         704 my $xsym = $sym;
132 26         85 weaken($xsym);
133              
134             ## no critic( ProhibitTies )
135 26         34 tie *{$sym}, 'Data::Handle::IO', { self => $xsym };
  26         361  
136 26         48 ${ *{$sym} }{stash} = {};
  26         32  
  26         120  
137 26         76 bless $sym, $class;
138 26         108 $sym->_stash->{start_offset} = $class->_get_start_offset($targetpackage);
139 26         60 $sym->_stash->{targetpackage} = $targetpackage;
140 26         55 $sym->_stash->{current_offset} = $class->_get_start_offset($targetpackage);
141 26         77 $sym->_stash->{filehandle} = $class->_get_data_symbol($targetpackage);
142 26         126 return $sym;
143              
144             }
145              
146             sub _has_data_symbol {
147 73     73   129 my ( undef, $package ) = @_;
148 73         81 my $rval = undef;
149             try {
150 73     73   2857 my $stash = Package::Stash->new($package);
151 70 100       751 return unless $stash->has_symbol('DATA');
152 69         285 my $fh = $stash->get_symbol('DATA');
153 69         85 $rval = defined fileno *{$fh};
  69         598  
154             }
155             catch {
156 3 50   3   46 if (/is not a module name/) {
157 3         5 $rval = undef;
158 3         9 return;
159             }
160             ## no critic (RequireCarping)
161 0         0 die $_;
162 73         523 };
163 73         1426 return $rval;
164             }
165              
166             sub _get_data_symbol {
167 35     35   160 my ( $self, $package ) = @_;
168 35 100       64 if ( !$self->_has_data_symbol($package) ) {
169 1         3 _e('Internal::BadGet')->throw('_get_data_symbol was called when there is no data_symbol to get');
170             }
171 34         447 return Package::Stash->new($package)->get_symbol('DATA');
172             }
173              
174             sub _get_start_offset {
175 57     57   163 my ( $self, $package ) = @_;
176              
177             return $datastash{$package}->{offset}
178 57 100       463 if ( exists $datastash{$package}->{offset} );
179              
180 5 100       17 if ( !$self->_has_data_symbol($package) ) {
181 1         4 _e('Internal::BadGet')->throw('_get_start_offset was called when there is no data_symbol to get');
182             }
183 4         11 my $fd = $self->_get_data_symbol($package);
184 4         27 my $position = tell $fd;
185              
186 4         13 $datastash{$package}->{offset} = $position;
187              
188 4         8 return $position;
189             }
190              
191             sub _is_valid_data_tell {
192 28     28   110 my ( $self, $package ) = @_;
193             return 1
194 28 100 66     255 if ( exists $datastash{$package} && 1 == $datastash{$package}->{valid} );
195 5 100       15 if ( !$self->_has_data_symbol($package) ) {
196 1         11 _e('Internal::BadGet')->throw('_is_valid_data_tell was called when there is no data_symbol to get');
197             }
198              
199 4         15 my $fh = $self->_get_data_symbol($package);
200 4         17 my $offset = $self->_get_start_offset($package);
201              
202             # The offset to the start of __DATA__ is 9 bytes because it includes the
203             # trailing \n
204             #
205 4         7 my $checkfor = qq{__DATA__\n};
206 4         21 seek $fh, ( $offset - length $checkfor ), 0;
207 4         59 read $fh, my ($buffer), length $checkfor;
208 4         15 seek $fh, $offset, 0;
209              
210 4         11 $datastash{$package}->{previous_bytes} = $buffer;
211              
212 4 100       15 if ( $buffer eq $checkfor ) {
213 3         8 $datastash{$package}->{valid} = 1;
214 3         16 return 1;
215             }
216             else {
217 1         2 $datastash{$package}->{valid} = 0;
218 1         4 return;
219             }
220             }
221              
222             sub _stringify_metadata {
223 1     1   2 my ( undef, $package ) = @_;
224 1         2 my @lines = ();
225 1 50       3 if ( not exists $datastash{$package} ) {
226 0         0 push @lines, "Nothing known about $package\n";
227 0         0 return join "\n", @lines;
228             }
229             else {
230 1         3 push @lines, q{Offset : } . $datastash{$package}->{offset};
231 1         2 push @lines, q{Prelude : '} . $datastash{$package}->{previous_bytes} . q{'};
232 1         3 push @lines, q{Valid: } . $datastash{$package}->{valid};
233 1         15 return join "\n", @lines;
234             }
235             }
236              
237             sub _readline {
238 22     22   29 my ( $self, @args ) = @_;
239              
240 22 100       47 _e('API::Invalid::Params')->throw('_readline() takes no parameters') if @args;
241              
242 21         37 my $fh = $self->_fh;
243 21         44 $self->_restore_pos();
244 21 100       48 if (wantarray) {
245 1         15 my @result = <$fh>;
246 1         5 $self->_set_pos();
247 1         8 return @result;
248             }
249 20         137 my $result = <$fh>;
250 20         44 $self->_set_pos();
251 20         85 return $result;
252             }
253              
254             sub _read {
255 5     5   12 my ( $self, undef, $len, $offset ) = @_;
256              
257             ## no critic ( ProhibitMagicNumbers )
258 5 100 100     34 _e('API::Invalid::Params')->throw('_read() takes 2 or 3 parameters.')
259             if ( scalar @_ < 3 or scalar @_ > 4 );
260              
261 3         9 $self->_restore_pos();
262 3         4 my $return;
263 3 100       7 if ( defined $offset ) {
264 2         5 $return = read $self->_fh, $_[1], $len, $offset;
265             }
266             else {
267 1         3 $return = read $self->_fh, $_[1], $len;
268             }
269 3         9 $self->_set_pos();
270 3         6 return $return;
271             }
272              
273             sub _getc {
274 37     37   43 my ($self) = @_;
275 37 100       87 _e('API::Invalid::Params')->throw('_get() takes 0 parameters.')
276             if scalar @_ > 1;
277 36         56 $self->_restore_pos();
278 36         84 my $return = getc $self->_fh;
279 36         83 $self->_set_pos();
280 36         139 return $return;
281             }
282              
283             sub _seek {
284 8     8   15 my ( $self, $position, $whence ) = @_;
285              
286             ## no critic ( ProhibitMagicNumbers )
287              
288 8 100       30 _e('API::Invalid::Params')->throw('_seek() takes 2 params.')
289             if scalar @_ != 3;
290              
291 7         51 my $fh = $self->_stash->{filehandle};
292              
293 7 100       29 if ( 0 == $whence ) {
    50          
    50          
294 6         14 $position = $self->_stash->{start_offset} + $position;
295             }
296             elsif ( 1 == $whence ) {
297 0         0 $whence = 0;
298 0         0 $position = $self->_stash->{current_offset} + $position;
299             }
300             elsif ( 2 == $whence ) {
301             }
302             else {
303 1         4 _e('API::Invalid::Whence')->throw('Expected whence values are 0,1,2');
304             }
305 6         46 my $return = seek $fh, $position, $whence;
306 6         17 $self->_set_pos();
307 6         23 return $return;
308             }
309              
310             sub _tell {
311 3     3   5 my ($self) = shift;
312 3 100       13 _e('API::Invalid::Params')->throw('_tell() takes no params.') if @_;
313 2         3 return $self->_stash->{current_offset} - $self->_stash->{start_offset};
314             }
315              
316             sub _eof {
317 43     43   45 my $self = shift;
318 43 100 66     233 _e('API::Invalid::Params')->throw("_eof() takes no params : @_ ")
319             if @_ && $_[0] != 1;
320 42         63 $self->_restore_pos();
321 42         92 return eof $self->_stash->{filehandle};
322             }
323              
324             sub _restore_pos {
325 102     102   96 my $self = shift;
326 102         138 return seek $self->_stash->{filehandle}, $self->_stash->{current_offset}, 0;
327             }
328              
329             sub _set_pos {
330 66     66   70 my $self = shift;
331 66         93 return ( $self->_stash->{current_offset} = tell $self->_stash->{filehandle} );
332             }
333              
334 559     559   448 sub _stash { return ${ *{ $_[0] } }{stash} }
  559         403  
  559         2331  
335 1     1   5 sub _fileno { return }
336 25     25   328 sub _e { return 'Data::Handle::Exception::' . shift }
337 60     60   82 sub _fh { return shift->_stash->{filehandle} }
338              
339             sub _binmode {
340 2     2   48 return _e('API::NotImplemented')->throw('_binmode() is difficult on Data::Handle and not implemented yet.');
341             }
342              
343             sub _open {
344 2     2   14 return _e('API::Invalid')->throw('_open() is invalid on Data::Handle.');
345             }
346              
347             sub _close {
348 2     2   15 return _e('API::Invalid')->throw('_close() is invalid on Data::Handle');
349             }
350              
351             sub _printf {
352 2     2   13 return _e('API::Invalid')->throw('_printf() is invalid on Data::Handle.');
353             }
354              
355             sub _print {
356 2     2   15 return _e('API::Invalid')->throw('_print() is invalid on Data::Handle.');
357             }
358              
359             sub _write {
360 2     2   13 return _e('API::Invalid')->throw('_write() is invalid on Data::Handle.');
361             }
362              
363              
364              
365              
366              
367              
368              
369              
370              
371              
372              
373             1;
374              
375             __END__