File Coverage

blib/lib/File/Parser/Role.pm
Criterion Covered Total %
statement 46 47 97.8
branch 11 14 78.5
condition n/a
subroutine 13 13 100.0
pod 0 1 0.0
total 70 75 93.3


line stmt bran cond sub pod time code
1             package File::Parser::Role;
2              
3 5     5   542964 use 5.008;
  5         16  
  5         228  
4              
5 5     5   24 use warnings;
  5         11  
  5         138  
6 5     5   18 use strict;
  5         11  
  5         147  
7 5     5   2901 use utf8;
  5         42  
  5         21  
8 5     5   172 use Carp;
  5         7  
  5         414  
9 5     5   1543 use IO::File;
  5         15857  
  5         635  
10 5     5   2705 use IO::String;
  5         12031  
  5         155  
11              
12 5     5   2136 use version; our $VERSION = qv('0.2.4');
  5         7255  
  5         28  
13 5     5   965 use Moo::Role;
  5         15691  
  5         44  
14 5     5   3744 use MooX::Aliases;
  5         12342  
  5         25  
15              
16             # File things
17             has file => ( is => "ro", alias => [qw/path filepath/] );
18             has size => ( is => "ro" );
19             has filename => ( is => "ro" );
20             has encoding => ( is => "ro" );
21             has fh => ( is => "lazy" );
22              
23             requires "parse";
24              
25             sub _build_fh {
26              
27 38     38   2046 my $self = shift;
28              
29             ## If stringified input is a readable file, treat it like that
30 38 100       48 if ( -r "${\ $self->file }" ) {
  38 100       420  
    50          
31              
32 15         173 my $fh = IO::File->new( $self->file, "r" );
33              
34             ## set it to the (possibly) specified encoding
35 15 100       1389 if ( defined $self->encoding ) {
36 2 50   2   60 binmode $fh, sprintf(":encoding(%s)", $self->encoding) or confess $!;
  2         46  
  2         5  
  2         13  
37             }
38 15         2092 return $fh;
39              
40             }
41              
42             ## A scalar reference is assumed to be content to be parsed
43             elsif ( ref $self->file eq "SCALAR" ) {
44 11         58 return IO::String->new( $self->file );
45             }
46              
47             ## If it's any kind of object, assume it can be <read> from
48             elsif ( ref $self->file ) {
49              
50             ## assume its something that can be read from as a file handle
51             ## set encoding and use it
52 12 100       33 if ( defined $self->encoding ) {
53 2 50       34 binmode $self->file, sprintf(":encoding(%s)", $self->encoding) or confess $!;
54             }
55 12         116 return $self->file;
56              
57             }
58              
59             ## can't grok it
60             else {
61 0         0 confess "Cannot work with input file - its neither a readable path nor a reference";
62             }
63              
64             }
65              
66             around BUILDARGS => sub {
67              
68             my ($orig, $class) = (shift, shift);
69              
70             my @args = @_;
71              
72             if ( @args == 1 and (ref( $args[0])||'') ne 'HASH' ) {
73             @args = ({ file => $args[0] });
74             }
75              
76             if ( not exists $args[0]->{file} and defined $args[0]->{filename} ) {
77             ## filename gets deleted for now and only re-inserted later on
78             ## if proven to be a valid filename
79             $args[0]->{file} = delete $args[0]->{filename};
80             }
81              
82             ## capture the aliases this way
83             my $obj = $class->$orig(@args);
84             my $f = $obj->{file};
85              
86             ## test if it seems to be a path to a file
87             if ( defined $f and -e "$f" ) {
88              
89             ## size (most likely) and filename can now be set
90              
91             ## only sets/overrides size if it isn't already set
92             $obj->{size} = -s "$f" unless exists $obj->{size};
93              
94             ## set filename if not already set
95             $obj->{filename} = "$f" unless defined $obj->{filename};
96              
97             }
98              
99             return $obj;
100              
101             };
102              
103             sub BUILD {
104 38     38 0 562 my $self = shift;
105 38         98 $self->parse;
106             };
107              
108             1; # Magic true value required at end of module
109             __END__
110              
111             =encoding utf8
112              
113             =head1 NAME
114              
115             File::Parser::Role - Read and prepare parsing of file (or glob) data
116             from some source
117              
118             =head1 VERSION
119              
120             This document describes File::Parser::Role version 0.2.4 This is a
121             Moo::Role for reading (and then parsing) single data files. It makes
122             the constructor support 3 kinds of file sources:
123              
124             =over
125              
126             =item a path to a readable file
127              
128             =item a file handle or anything that can be read like one
129              
130             =item a scalar references to content
131              
132             =back
133              
134             It also provides an attribute C<fh> that is a handle to the contents
135             of the file argument.
136              
137             =head1 SYNOPSIS
138              
139             package MyClassThatDoesStuffWithAFile;
140              
141             sub parse {
142             my $self = shift;
143              
144             # ... do stuff, $self->fh available
145             }
146              
147             with "File::Parser::Role";
148              
149             And then in some nearby code:
150              
151             my $obj = MyClassThatDoesStuffWithAFile->new("some_file.txt");
152             # or #
153             my $obj = MyClassThatDoesStuffWithAFile->new(file => "some_file.txt");
154              
155             ## and with encoding:
156             my $obj = MyClassThatDoesStuffWithAFile->new( file => "some_file.txt", encoding => "utf8" );
157             ## encoding can be anything that binmode's encoding() can understand.
158              
159             print $obj->filename; # "some_file.txt"
160             print $obj->size; # size of some_file.txt
161              
162             ## - OR -
163              
164             my $fh = IO::File->new( "< some_file.txt" );
165             ## you are responsible for encoding on this handle!
166              
167             my $obj = MyClassThatDoesStuffWithAFile->new( file => $fh );
168              
169             ## no filename nor file size available
170              
171             ## - OR -
172              
173             my $file_content = slurp_file( "some_file.txt" );
174             my $obj = MyClassThatDoesStuffWithAFile->new( file => \$file_content );
175              
176             ## you are also responsible for encoding on this data
177             ## no file name nor file size available
178              
179             =head1 DESCRIPTION
180              
181             This role provides all the bare necessities, and then some, that you
182             expect when you want to parse or otherwise handle a chunk of content
183             typically provided as a file.
184              
185             It is motivated by, and ideal for, objects that parse files.
186              
187             =head1 INTERFACE
188              
189             =head2 new
190              
191             The constructor is meant to be all expected kinds of flexible:
192              
193             =over
194              
195             =item * new("file") # a local filename
196              
197             =item * new($fh)
198              
199             =item * new( file => "file", encoding => "utf8" ); # also works with: C<new({ ... })>
200              
201             =item * new( \"some content" )
202              
203             =back
204              
205             The constructor tests the argument to see if it's a path to a local
206             file. If so, it records its C<filename> and C<size> in those two
207             attributes.
208              
209             It stringifies the file argument for this check, allowing file objects
210             that stringify to paths to work correctly. This applies among others
211             to Path::Tiny.
212              
213             If a reference or an object is passed as the argument, (that does not
214             stringity fo a readable local file), it is assumed to be something
215             that can be read with the <> operator and passed unchanged to the
216             C<fh> attribute of the class.
217              
218             =head2 fh
219              
220             Returns ro handle (IO::File for files, IO::String for content) to the
221             contents of the input, be it a file or a sclar reference or an already
222             opened file handle
223              
224             If the input argument is assumed to be a readable handle to content,
225             it is passed straight through with this method.
226              
227             =head2 parse
228              
229             A required method that you must write! It is run in BUILD
230              
231             =head1 DIAGNOSTICS
232              
233             =over
234              
235             =item C<< Cannot work with input file >>
236              
237             The file argument is neither an existing file, an object nor a
238             reference to content
239              
240             =back
241              
242             =head1 DEPENDENCIES
243              
244             =over
245              
246             =item * L<Moo>
247              
248             =item * L<Moo::Role>
249              
250             =item * L<MooX::Aliases>
251              
252             =item * L<IO::String>
253              
254             =item * L<Test::Most>
255              
256             =item * L<Test::Output>
257              
258             =item * L<Test::Warnings>
259              
260             =back
261              
262             =head1 INCOMPATIBILITIES
263              
264             None reported.
265              
266             =head1 REPOSITORY
267              
268             L<https://github.com/torbjorn/File-Parser-Role>
269              
270             =head1 BUGS AND LIMITATIONS
271              
272             No bugs have been reported.
273              
274             Please report any bugs or feature requests to
275             C<bug-file-parser-role@rt.cpan.org>, or through the web interface at
276             L<http://rt.cpan.org>.
277              
278             =head1 AUTHOR
279              
280             Torbjørn Lindahl C<< <torbjorn.lindahl@gmail.com> >>
281              
282             =head1 LICENCE AND COPYRIGHT
283              
284             Copyright (c) 2012, Torbjørn Lindahl C<<
285             <torbjorn.lindahl@gmail.com> >>. All rights reserved.
286              
287             This module is free software; you can redistribute it and/or
288             modify it under the same terms as Perl itself. See L<perlartistic>.
289              
290              
291             =head1 DISCLAIMER OF WARRANTY
292              
293             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
294             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
295             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
296             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
297             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
298             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
299             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
300             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
301             NECESSARY SERVICING, REPAIR, OR CORRECTION.
302              
303             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
304             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
305             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
306             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
307             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
308             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
309             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
310             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
311             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
312             SUCH DAMAGES.