File Coverage

blib/lib/IO/Simple.pm
Criterion Covered Total %
statement 64 104 61.5
branch 20 60 33.3
condition 3 10 30.0
subroutine 14 30 46.6
pod 12 23 52.1
total 113 227 49.7


line stmt bran cond sub pod time code
1             package IO::Simple;
2            
3 1     1   22029 use 5.006;
  1         4  
  1         38  
4 1     1   5 use strict;
  1         2  
  1         34  
5 1     1   5 use warnings;
  1         6  
  1         67  
6            
7             require Exporter;
8             require IO::File;
9            
10             our @ISA = qw(Exporter IO::File);
11            
12 1     1   3520 use Data::Dumper;
  1         12992  
  1         171  
13             our %EXPORT_TAGS = ( 'all' => [ qw(
14             file slurp
15             ) ] );
16            
17             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
18            
19             our @EXPORT = qw(
20             );
21            
22             our $VERSION = '0.04';
23            
24 1     1   11 use Carp;
  1         3  
  1         1960  
25            
26             =head1 NAME
27            
28             IO::Simple - Adds error checking to file handles and provides per file handle options.
29            
30             =head1 SYNOPSIS
31            
32             You can export the C method as below
33            
34             use IO::Simple ':all';
35            
36             my $fh = file('test.txt', 'w'); #dies if file can't be opened
37             $fh->say("This is a line"); #say appends new line
38             $fh->print("This has no new line!!!"); #regular print behavior
39             $fh->close(); #dies on failure
40            
41             my $contents = file('test.txt')->slurp();
42            
43             Or you can use the C class method
44            
45             use IO::Simple;
46             my $fh = new IO::Simple('test.txt');
47            
48             =head1 DESCRIPTION
49            
50             IO::Simple provides a thin layer over IO::File. This layer causes files to
51             default to opening in read mode and to croaking on failure opening, closeing
52             or printing to files. It provides methods to set $\, $/, $:, $^L and $, on a
53             per handle basis and a slurp function.
54            
55             =head1 REASONING
56            
57             You can get similar results using a combination of IO::All, Fatal and
58             File::Slurp. I found that fatal didn't provide as descriptive as errors as I
59             wanted, and IO::All was overly bloated so this module was born to fill in
60             those gaps.
61            
62             =head1 METHODS
63            
64             =over 4
65            
66             =cut
67            
68             my $data = {};
69 0     0 0 0 sub data { $data };
70            
71             # internal method for inside out object operation
72             sub id {
73 13     13 0 17 my $self = shift;
74 13         190 return 0+$self;
75             }
76            
77             =item new IO::Simple ( [FILENAME [,MODE, [OPTIONS]]])
78            
79             Passes any arguments to C<< $self->open >> for processing, otherwise simply
80             returns a new object.
81            
82             =cut
83            
84             sub new {
85 3     3 1 5 my $type = shift;
86 3   50     22 my $class = ref($type) || $type || "IO::Simple";
87 3         22 my $self = $class->SUPER::new();
88            
89 3         114 $data->{id($self)} = {
90             file_name => '',
91             mode => '',
92             autochomp => 1
93             };
94            
95 3 50       11 $self->open(@_) if (@_);
96 3         6 return $self;
97             }
98            
99             =item $fh->open ( FILENAME [,MODE, [OPTIONS]])
100            
101             C accepts up to three parameters. If only FILENAME is supplied then
102             the default mode is 'read' The mode can be one of 'r',' 'read', 'w', 'write',
103             'a', 'append' which translate to '<', '>', and '>>'. The third parameter is
104             a hash of options. It also adds some magic so that the '-' file name will
105             cause STDIN or STDOUT to be opened depending on the mode.
106            
107             Option Sets
108             line_break_characters $:
109             format_formfeed $^L
110             output_field_separator $,
111             output_record_separator $\
112             input_record_separator $/
113             autochomp auto chomp on readline or slurp
114            
115             =cut
116            
117             sub open {
118 3     3 1 6 my $self = shift;
119 3         4 my $file_name = shift;
120 3   100     12 my $mode = shift || 'r';
121 3 50       8 if ($file_name eq '-') {
122 0 0       0 if ($mode =~ /r|
123 0 0       0 $self->fdopen('STDIN', $mode) or croak "Opening '$file_name' for '$mode' failed: $!";
124             } else {
125 0 0       0 $self->fdopen('STDOUT', $mode) or croak "Opening '$file_name' for '$mode' failed: $!";
126             }
127             } else {
128 3 100       17 $self->SUPER::open($file_name, $mode) or croak "Opening '$file_name' for '$mode' failed: $!";
129             }
130 2         6 $data->{id($self)} = {
131 2         244 %{$data->{id($self)}},
132             @_
133             };
134 2         9 return $self;
135             };
136            
137             =item C ( [MODE] )
138            
139             Reopen a previously opened file with the same mode or a new mode.
140            
141             my $fh = file('test'); #open it for reading;
142             $fh->close;
143             $fh->reopen('a'); #reopen the file for writing.
144            
145             =cut
146            
147             sub reopen {
148 0     0 1 0 my $self = shift;
149 0         0 my $data = $data->{id($self)};
150 0   0     0 my $mode = shift || $data->{mode};
151 0 0       0 croak "No file has been opened to be reopened." unless defined $data;
152 0         0 $self->open($data->{file_name}, $mode);
153             }
154            
155             =item C ( FILENAME [,MODE, [OPTIONS]])
156            
157             C accepts up to three parameters. If only one is supplied then the
158             default mode is 'read' The mode can be one of 'r',' 'read', 'w', 'write',
159             'a', 'append' which translate to '<', '>', and '>>'. The third parameter
160             is a hash of options. By default C is on, but you can use this
161             to disable it if you prefer. which would cause the slurp method to chomp
162             each line in array context. C shoft for "Input Record Seperator" lets
163             you set a default value for C<$\> that will be used for C and
164             C operations.
165            
166             my $read = file('test');
167             my $write = file('test', 'w');
168            
169             my $not_chomped = file('test', 'r', autochomp => 0)
170             my @lines = $not_chomped->slurp();
171            
172             my $pipedel = file('test', 'r', irs => '|')
173             my @fields = $pipedel->slurp();
174            
175             =cut
176            
177             sub file {
178 3     3 1 1298 my $self = new IO::Simple;
179 3 50       12 $self->open(@_) if @_;
180 2         7 return $self;
181             }
182            
183             =item $fh->close
184            
185             Wrapper for IO::Handle close with added error handling.
186            
187             =cut
188            
189             sub close {
190 1     1 1 6 my $self = shift;
191 1         3 my $data = $data->{id($self)};
192 1 50       28 croak "File '$data->{file_name}' is not open." unless $self->opened;
193 1 50       15 $self->SUPER::close() or croak "Failed to close '$data->{file_name}' : $!";
194             }
195            
196             #internal function to wrap functions in error catching
197             sub _protect {
198 0     0   0 my $function = shift;
199 0         0 my $self = shift;
200 0         0 my $data = $data->{id($self)};
201            
202 0 0       0 croak "File '$data->{file_name}' is not opened." unless $self->opened;
203 0         0 $" = ',';
204 0 0       0 $self->can("SUPER::$function")->($self,@_) or croak "Failed to $function(@_): $!";
205             }
206            
207             # Protect functions from IO::Seekable
208 0     0 1 0 sub seek { _protect('seek', @_) }
209 0     0 1 0 sub tell { _protect('tell', @_) }
210 0     0 0 0 sub truncate { _protect('truncate', @_) }
211 0     0 1 0 sub sysseek { _protect('sysseek', @_) }
212 0     0 1 0 sub setpos { _protect('setpos', @_) }
213 0     0 1 0 sub getpos { _protect('getpos', @_) }
214            
215            
216             =item PerlVar per Handle Methods
217            
218             Stores your choice and later localizes the perlvar and sets it appropriately
219             during output operations. Returns current value if no STR is provided.
220            
221             $fh->format_line_break_characters( [STR] ) $:
222             $fh->format_formfeed( [STR]) $^L
223             $fh->output_field_separator( [STR] ) $,
224             $fh->output_record_separator( [STR] ) $\
225            
226             Stores your choice and later localizes the perlvar and sets it appropriately
227             during input operations. Returns current value if no STR is provided.
228            
229             $fh->input_record_separator( [STR] ) $/
230            
231            
232             =cut
233             sub set_option {
234 0     0 0 0 my $self = shift;
235 0 0 0     0 @_ == 1 || @_ == 2 or croak "usage: \$fh->set_option('option'[ ,value]);";
236 0         0 my $option = shift;
237 0 0       0 return $self->can("SUPER::$option")->(@_) unless ref($self);
238            
239 0         0 my $data = $data->{id($self)};
240 0 0       0 return $data->{$option} unless @_ == 1;
241 0         0 $data->{$option} = shift;
242             }
243            
244 0     0 0 0 sub format_line_break_characters { shift->set_option('format_line_break_characters', @_); }
245 0     0 0 0 sub format_formfeed { shift->set_option('format_formfeed' , @_); }
246 0     0 0 0 sub output_field_separator { shift->set_option('output_field_separator' , @_); }
247 0     0 0 0 sub output_record_separator { shift->set_option('output_record_separator' , @_); }
248 0     0 0 0 sub input_record_separator { shift->set_option('input_record_separator' , @_); }
249            
250            
251             =item $fh->print
252            
253             Wrapper for IO::Handle C with added error handling and localizes
254             C<$:>, C<$^L>, C<$,>, C<$\> and sets them properly for each file handle.
255            
256             =cut
257            
258 0     0 0 0 sub say { shift->print(@_,"\n"); }
259             sub print {
260 1     1 1 541 my $self = shift;
261 1 50       17 croak "File '$data->{file_name}' is not opened." unless $self->opened;
262 1         11 my $data = $data->{id($self)};
263            
264 1 50       9 local $: = exists $data->{format_line_break_characters} ? $data->{format_line_break_characters} : $:;
265 1 50       6 local $^L = exists $data->{format_formfeed} ? $data->{format_formfeed} : $^L;
266 1 50       5 local $, = exists $data->{output_field_separator} ? $data->{output_field_separator} : $,;
267 1 50       5 local $\ = exists $data->{output_record_separator} ? $data->{output_record_separator} : $\;
268            
269 1 50       21 print $self @_ or croak "Failed to print to '$data->{file_name}': $!";
270 1         6 return $self;
271             }
272            
273             =item IO::Simple::slurp(FILE [,SEP])
274            
275             Takes a file name an slurps up the file. In list context it uses the SEP
276             and outputs an array of lines in scalar context it ignores the SEP and
277             returns the entire file in a scalar.
278            
279             use IO::Simple qw/slurp/;
280             my $content = slurp('test');
281            
282             =item $fh->slurp([SEP])
283            
284             C returns the remaining contents of the file handle. If used in list
285             context it returns the lines of the file in an array (setting $/ = SEP),
286             otherwise it returns the entire file slurped into a scalar. Unless disablled
287             with autochomp, lines returned in list context will be chomped.
288            
289             my $content = file('test')->slurp();
290            
291             =cut
292            
293             sub slurp {
294 1     1 1 2 my $self = shift;
295 1 50       7 unless ($self->isa('IO::Simple')) {
296 0         0 $self = file($self);
297             }
298 1 50       4 croak "File '$data->{file_name}' is not opened." unless $self->opened;
299 1 50       8 if (wantarray) {
300 0         0 return $self->readline(@_);
301             } else {
302 1         7 return $self->readline(undef);
303             }
304             }
305            
306             sub readline {
307 1     1 0 3 my $self = shift;
308            
309 1         3 my $data = $data->{id($self)};
310 1 50       4 croak "File '$data->{file_name}' is not opened." unless $self->opened;
311 1         10 local $/ = $/;
312 1 50       5 if (@_) {
    0          
313 1         3 $/ = shift;
314             } elsif (exists $data->{input_record_separator}) {
315 0         0 $/ = $data->{input_record_separator};
316             }
317            
318 1 50       26 if (wantarray) {
319 0         0 my @lines = <$self>;
320 0 0       0 chomp(@lines) if $data->{autochomp};
321 0         0 return @lines;
322             } else {
323 1         29 my $line = <$self>;
324 1 50       5 chomp($line) if $data->{autochomp};;
325 1         6 return $line;
326             }
327             }
328            
329             sub DESTROY {
330 3     3   934 my $self = shift;
331 3         8 delete $data->{id($self)};
332             }
333            
334             1;
335            
336             =back
337            
338             =head1 EXPORT
339            
340             Optionaly exports two functions C and C or use C<:all> to
341             import both. No methods are exported by default.
342            
343             =head1 CAVEAT
344            
345             The error checking only works when you use the object methods. This
346             allows you to use the builtins when wish to handle your own error checking.
347            
348             my $fh = file('test.txt');
349             $fh->print("Hello Wolrd"); #results in error
350             print $fh "Hello World"; #doesn't through error.
351            
352             my $fh = new IO::Simple;
353             $fh->open('test'); #throws error if test doesn't exist
354             open($fh, '<', 'test'); #allows you to handle errors on your own.
355            
356             If you don't use the open method the errors will not know which file you
357             opened or what mode you opened it in.
358            
359             =head1 SEE ALSO
360            
361             L,
362             L,
363             L,
364             L,
365             L
366            
367             =head1 AUTHOR
368            
369             Eric Hodges
370            
371             =head1 COPYRIGHT AND LICENSE
372            
373             Copyright (C) 2007 by Eric Hodges
374            
375             This library is free software; you can redistribute it and/or modify
376             it under the same terms as Perl itself, either Perl version 5.8.8 or,
377             at your option, any later version of Perl 5 you may have available.
378            
379            
380             =cut