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   512943 use 5.008;
  5         16  
  5         208  
4              
5 5     5   26 use warnings;
  5         14  
  5         172  
6 5     5   28 use strict;
  5         12  
  5         177  
7 5     5   2907 use utf8;
  5         55  
  5         27  
8 5     5   212 use Carp;
  5         9  
  5         387  
9 5     5   2051 use IO::File;
  5         15973  
  5         560  
10 5     5   2531 use IO::String;
  5         9754  
  5         175  
11              
12 5     5   2433 use version; our $VERSION = qv('0.2.1');
  5         7610  
  5         31  
13 5     5   967 use Moo::Role;
  5         16768  
  5         45  
14 5     5   3906 use MooX::Aliases;
  5         13343  
  5         30  
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   2409 my $self = shift;
28              
29             ## If stringified input is a readable file, treat it like that
30 38 100       47 if ( -r "${\ $self->file }" ) {
  38 100       452  
    50          
31              
32 15         196 my $fh = IO::File->new( $self->file, "r" );
33              
34             ## set it to the (possibly) specified encoding
35 15 100       1571 if ( defined $self->encoding ) {
36 2 50   2   88 binmode $fh, sprintf(":encoding(%s)", $self->encoding) or confess $!;
  2         14  
  2         2  
  2         13  
37             }
38 15         2305 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         62 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       37 if ( defined $self->encoding ) {
53 2 50       44 binmode $self->file, sprintf(":encoding(%s)", $self->encoding) or confess $!;
54             }
55 12         131 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 603 my $self = shift;
105 38         108 $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.1 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 a method "fh" that gives an at least readable file
135             handle to the contents of the file.
136              
137             =head1 SYNOPSIS
138              
139             package MyClassThatDoesStuffToAFile;
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 in some nearby code:
150              
151             my $obj = MyClassThatDoesStuffToAFile->new("some_file.txt");
152             # or #
153             my $obj = MyClassThatDoesStuffToAFile->new(file => "some_file.txt");
154             ## optinally:
155              
156             my $obj = MyClassThatDoesStuffToAFile->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 now responsible for encoding on this handle!
166              
167             my $obj = MyClassThatDoesStuffToAFile->new( file => $fh );
168              
169             ## no filename nor file size available
170              
171             ## - OR -
172              
173             my $file_content = read_file( "some_file.txt" );
174             my $obj = MyClassThatDoesStuffToAFile->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")
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             Construction tests the argument and if it's a path to a file reachable
206             on the local file system records its C<filename> and C<size> in those
207             two attributes.
208              
209             When it checks if the argument if a local filename, it checks "$file",
210             allowing objects that stringify to paths to work correctly. This
211             applies among others to Path::Tiny.
212              
213             If a reference to something is passed (or an object), it is assumed to
214             be something that can be read with <> and passed for a handle.
215              
216             =head2 fh
217              
218             Returns ro handle (IO::File for files, IO::String for content) to the
219             contents of the input, be it a file or a sclar reference or an already
220             opened file handle
221              
222             If the input argument is assumed to be a readable handle to content,
223             it is passed straight through with this method.
224              
225             =head2 parse
226              
227             A required method that you must write! It is run in BUILD
228              
229             =head1 DIAGNOSTICS
230              
231             =over
232              
233             =item C<< Cannot work with input file >>
234              
235             The file argument is neither an existing file, an object nor a
236             reference to content
237              
238             =back
239              
240             =head1 DEPENDENCIES
241              
242             =over
243              
244             =item * L<Moo>
245              
246             =item * L<Moo::Role>
247              
248             =item * L<IO::String>
249              
250             =item * L<Pod::Coverage::Moose>
251              
252             =item * L<Test::Most>
253              
254             =item * L<Test::Perl::Critic>
255              
256             =item * L<Test::Pod>
257              
258             =item * L<Test::Pod::Coverage>
259              
260             =item * L<Test::Output>
261              
262             =item * L<Test::Warnings>
263              
264             =item * L<MooX::Aliases>
265              
266             =back
267              
268             =head1 INCOMPATIBILITIES
269              
270             None reported.
271              
272             =head1 BUGS AND LIMITATIONS
273              
274             No bugs have been reported.
275              
276             Please report any bugs or feature requests to
277             C<bug-file-parser-role@rt.cpan.org>, or through the web interface at
278             L<http://rt.cpan.org>.
279              
280             =head1 AUTHOR
281              
282             Torbjørn Lindahl C<< <torbjorn.lindahl@gmail.com> >>
283              
284             =head1 LICENCE AND COPYRIGHT
285              
286             Copyright (c) 2012, Torbjørn Lindahl C<<
287             <torbjorn.lindahl@gmail.com> >>. All rights reserved.
288              
289             This module is free software; you can redistribute it and/or
290             modify it under the same terms as Perl itself. See L<perlartistic>.
291              
292              
293             =head1 DISCLAIMER OF WARRANTY
294              
295             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
296             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
297             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
298             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
299             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
300             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
301             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
302             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
303             NECESSARY SERVICING, REPAIR, OR CORRECTION.
304              
305             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
306             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
307             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
308             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
309             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
310             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
311             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
312             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
313             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
314             SUCH DAMAGES.