File Coverage

blib/lib/Data/Handle.pm
Criterion Covered Total %
statement 154 159 96.8
branch 40 44 90.9
condition 7 9 77.7
subroutine 37 37 100.0
pod 1 1 100.0
total 239 250 95.6


line stmt bran cond sub pod time code
1 4     4   135636 use strict;
  4         10  
  4         251  
2 4     4   20 use warnings;
  4         9  
  4         181  
3              
4             package Data::Handle;
5             BEGIN {
6 4     4   134 $Data::Handle::AUTHORITY = 'cpan:KENTNL';
7             }
8             {
9             $Data::Handle::VERSION = '0.02001003';
10             }
11              
12             # ABSTRACT: A Very simple interface to the __DATA__ file handle.
13              
14              
15              
16              
17              
18             my %datastash;
19 4     4   4568 use Symbol qw( gensym );
  4         4749  
  4         440  
20 4     4   27 use Scalar::Util qw( weaken );
  4         7  
  4         389  
21 4     4   3843 use parent qw( IO::File );
  4         1437  
  4         30  
22 4     4   77052 use Package::Stash 0.15; # has_symbol
  4         28114  
  4         113  
23 4     4   33 use Carp ();
  4         9  
  4         69  
24 4     4   2923 use Data::Handle::Exception;
  4         15  
  4         130  
25 4     4   2569 use Data::Handle::IO;
  4         10  
  4         94  
26 4     4   20 use Try::Tiny;
  4         19  
  4         6395  
27              
28              
29             sub new {
30 28     28 1 15612 my ( $class, $targetpackage ) = @_;
31              
32 28 100       100 _e('NoSymbol')->throw("$targetpackage has no DATA symbol")
33             if ( !$class->_has_data_symbol($targetpackage) );
34              
35 27 100       153 if ( !$class->_is_valid_data_tell($targetpackage) ) {
36 1         5 _e('BadFilePos')
37             ->throw( "$targetpackage has a DATA symbol, but the filepointer"
38             . " is well beyond the __DATA__ section.\n"
39             . " We can't work out safely where it is.\n"
40             . $class->_stringify_metadata($targetpackage)
41             . "\n" );
42             }
43              
44 26         112 my $sym = gensym();
45 26         349 my $xsym = $sym;
46 26         85 weaken($xsym);
47              
48             ## no critic( ProhibitTies )
49 26         41 tie *{$sym}, 'Data::Handle::IO', { self => $xsym };
  26         292  
50 26         50 ${ *{$sym} }{stash} = {};
  26         40  
  26         110  
51 26         64 bless $sym, $class;
52 26         119 $sym->_stash->{start_offset} = $class->_get_start_offset($targetpackage);
53 26         74 $sym->_stash->{targetpackage} = $targetpackage;
54 26         78 $sym->_stash->{current_offset} = $class->_get_start_offset($targetpackage);
55 26         84 $sym->_stash->{filehandle} = $class->_get_data_symbol($targetpackage);
56 26         148 return $sym;
57              
58             }
59              
60             sub _has_data_symbol {
61 73     73   119 my ( $self, $package ) = @_;
62 73         112 my $rval = undef;
63             try {
64 73     73   2430 my $object = Package::Stash->new($package);
65 70 100       717 return unless $object->has_symbol('DATA');
66 69         357 my $fh = $object->get_symbol('DATA');
67 69         130 $rval = defined fileno *{$fh};
  69         501  
68             }
69             catch {
70 3 50   3   46 if ( $_ =~ /is not a module name/ ) {
71 3         4 $rval = undef;
72 3         12 return;
73             }
74             ## no critic (RequireCarping)
75 0         0 die $_;
76 73         499 };
77 73         1191 return $rval;
78             }
79              
80             sub _get_data_symbol {
81 35     35   150 my ( $self, $package ) = @_;
82 35 100       94 if ( !$self->_has_data_symbol($package) ) {
83 1         5 _e('Internal::BadGet')
84             ->throw(
85             '_get_data_symbol was called when there is no data_symbol to get');
86             }
87 34         391 return Package::Stash->new($package)->get_symbol('DATA');
88             }
89              
90             sub _get_start_offset {
91 57     57   168 my ( $self, $package ) = @_;
92              
93 57 100       308 return $datastash{$package}->{offset}
94             if ( exists $datastash{$package}->{offset} );
95              
96 5 100       18 if ( !$self->_has_data_symbol($package) ) {
97 1         4 _e('Internal::BadGet')
98             ->throw(
99             '_get_start_offset was called when there is no data_symbol to get');
100             }
101 4         16 my $fd = $self->_get_data_symbol($package);
102 4         25 my $position = tell $fd;
103              
104 4         16 $datastash{$package}->{offset} = $position;
105              
106 4         13 return $position;
107             }
108              
109             sub _is_valid_data_tell {
110 28     28   99 my ( $self, $package ) = @_;
111 28 100 66     247 return 1
112             if ( exists $datastash{$package} && $datastash{$package}->{valid} == 1 );
113 5 100       15 if ( !$self->_has_data_symbol($package) ) {
114 1         5 _e('Internal::BadGet')
115             ->throw(
116             '_is_valid_data_tell was called when there is no data_symbol to get');
117             }
118              
119 4         22 my $fh = $self->_get_data_symbol($package);
120 4         25 my $offset = $self->_get_start_offset($package);
121              
122             # The offset to the start of __DATA__ is 9 bytes because it includes the
123             # trailing \n
124             #
125 4         11 my $checkfor = qq{__DATA__\n};
126 4         50 seek $fh, ( $offset - length $checkfor ), 0;
127 4         104 read $fh, my ($buffer), length $checkfor;
128 4         40 seek $fh, $offset, 0;
129              
130 4         16 $datastash{$package}->{previous_bytes} = $buffer;
131              
132 4 100       17 if ( $buffer eq $checkfor ) {
133 3         9 $datastash{$package}->{valid} = 1;
134 3         17 return 1;
135             }
136             else {
137 1         5 $datastash{$package}->{valid} = 0;
138 1         4 return;
139             }
140             }
141              
142             sub _stringify_metadata {
143 1     1   3 my ( $self, $package ) = @_;
144 1         2 my @lines = ();
145 1 50       5 if ( not exists $datastash{$package} ) {
146 0         0 push @lines, "Nothing known about $package\n";
147 0         0 return join "\n", @lines;
148             }
149             else {
150 1         6 push @lines, q{Offset : } . $datastash{$package}->{offset};
151 1         5 push @lines, q{Prelude : '} . $datastash{$package}->{previous_bytes} . q{'};
152 1         5 push @lines, q{Valid: } . $datastash{$package}->{valid};
153 1         22 return join "\n", @lines;
154             }
155             }
156              
157             sub _readline {
158 22     22   29 my ( $self, @args ) = @_;
159              
160 22 100       54 _e('API::Invalid::Params')->throw('_readline() takes no parameters') if @args;
161              
162 21         36 my $fh = $self->_fh;
163 21         46 $self->_restore_pos();
164 21 100       67 if (wantarray) {
165 1         16 my @result = <$fh>;
166 1         4 $self->_set_pos();
167 1         6 return @result;
168             }
169 20         165 my $result = <$fh>;
170 20         57 $self->_set_pos();
171 20         93 return $result;
172             }
173              
174             sub _read {
175 5     5   13 my ( $self, undef, $len, $offset ) = @_;
176              
177             ## no critic ( ProhibitMagicNumbers )
178 5 100 100     48 _e('API::Invalid::Params')->throw('_read() takes 2 or 3 parameters.')
179             if ( scalar @_ < 3 or scalar @_ > 4 );
180              
181 3         10 $self->_restore_pos();
182 3         7 my $return;
183 3 100       10 if ( defined $offset ) {
184 2         8 $return = read $self->_fh, $_[1], $len, $offset;
185             }
186             else {
187 1         4 $return = read $self->_fh, $_[1], $len;
188             }
189 3         12 $self->_set_pos();
190 3         12 return $return;
191             }
192              
193             sub _getc {
194 37     37   61 my ($self) = @_;
195 37 100       86 _e('API::Invalid::Params')->throw('_get() takes 0 parameters.')
196             if scalar @_ > 1;
197 36         66 $self->_restore_pos();
198 36         109 my $return = getc $self->_fh;
199 36         104 $self->_set_pos();
200 36         158 return $return;
201             }
202              
203             sub _seek {
204 8     8   18 my ( $self, $position, $whence ) = @_;
205              
206             ## no critic ( ProhibitMagicNumbers )
207              
208 8 100       29 _e('API::Invalid::Params')->throw('_seek() takes 2 params.')
209             if scalar @_ != 3;
210              
211 7         16 my $fh = $self->_stash->{filehandle};
212              
213 7 100       25 if ( $whence == 0 ) {
    50          
    50          
214 6         14 $position = $self->_stash->{start_offset} + $position;
215             }
216             elsif ( $whence == 1 ) {
217 0         0 $whence = 0;
218 0         0 $position = $self->_stash->{current_offset} + $position;
219             }
220             elsif ( $whence == 2 ) {
221             }
222             else {
223 1         5 _e('API::Invalid::Whence')->throw('Expected whence values are 0,1,2');
224             }
225 6         39 my $return = seek $fh, $position, $whence;
226 6         18 $self->_set_pos();
227 6         60 return $return;
228             }
229              
230             sub _tell {
231 3     3   5 my ($self) = shift;
232 3 100       15 _e('API::Invalid::Params')->throw('_tell() takes no params.') if @_;
233 2         4 return $self->_stash->{current_offset} - $self->_stash->{start_offset};
234             }
235              
236             sub _eof {
237 43     43   53 my $self = shift;
238 43 100 66     227 _e('API::Invalid::Params')->throw("_eof() takes no params : @_ ")
239             if @_ && $_[0] != 1;
240 42         77 $self->_restore_pos();
241 42         158 return eof $self->_stash->{filehandle};
242             }
243              
244             sub _restore_pos {
245 102     102   114 my $self = shift;
246 102         189 return seek $self->_stash->{filehandle}, $self->_stash->{current_offset}, 0;
247             }
248              
249             sub _set_pos {
250 66     66   84 my $self = shift;
251 66         110 return ( $self->_stash->{current_offset} = tell $self->_stash->{filehandle} );
252             }
253              
254 559     559   564 sub _stash { return ${ *{ $_[0] } }{stash} }
  559         517  
  559         3313  
255 1     1   6 sub _fileno { return }
256 25     25   322 sub _e { return 'Data::Handle::Exception::' . shift }
257 60     60   111 sub _fh { return shift->_stash->{filehandle} }
258              
259             sub _binmode {
260 2     2   7 return _e('API::NotImplemented')
261             ->throw('_binmode() is difficult on Data::Handle and not implemented yet.');
262             }
263              
264             sub _open {
265 2     2   10 return _e('API::Invalid')->throw('_open() is invalid on Data::Handle.');
266             }
267              
268             sub _close {
269 2     2   11 return _e('API::Invalid')->throw('_close() is invalid on Data::Handle');
270             }
271              
272             sub _printf {
273 2     2   13 return _e('API::Invalid')->throw('_printf() is invalid on Data::Handle.');
274             }
275              
276             sub _print {
277 2     2   16 return _e('API::Invalid')->throw('_print() is invalid on Data::Handle.');
278             }
279              
280             sub _write {
281 2     2   11 return _e('API::Invalid')->throw('_write() is invalid on Data::Handle.');
282             }
283              
284              
285             1;
286              
287             __END__