File Coverage

blib/lib/IO/Any.pm
Criterion Covered Total %
statement 114 147 77.5
branch 56 82 68.2
condition 11 18 61.1
subroutine 18 23 78.2
pod 5 5 100.0
total 204 275 74.1


line stmt bran cond sub pod time code
1             package IO::Any;
2              
3 2     2   163338 use warnings;
  2         5  
  2         62  
4 2     2   8 use strict;
  2         3  
  2         68  
5              
6             our $VERSION = '0.08';
7              
8 2     2   9 use Carp 'confess';
  2         6  
  2         78  
9 2     2   9 use Scalar::Util 'blessed';
  2         3  
  2         69  
10 2     2   1561 use IO::String;
  2         6632  
  2         50  
11 2     2   768 use IO::File;
  2         8521  
  2         250  
12 2     2   1451 use IO::AtomicFile;
  2         1229  
  2         75  
13 2     2   12 use File::Spec;
  2         4  
  2         43  
14 2     2   10 use Fcntl qw(:flock);
  2         4  
  2         293  
15 2     2   1662 use List::MoreUtils qw(none any);
  2         2164  
  2         2988  
16              
17             sub new {
18 23     23 1 3450 my $class = shift;
19 23         32 my $what = shift;
20 23   50     54 my $how = shift || '<';
21 23   100     81 my $opt = shift || {};
22 23 50       52 confess 'too many arguments'
23             if @_;
24              
25 23 100       61 confess '$what is missing'
26             if not defined $what;
27              
28 21 50       57 confess 'expecting hash ref'
29             if ref $opt ne 'HASH';
30 21         59 foreach my $key (keys %$opt) {
31             confess 'unknown option '.$key
32 17 100   51   81 if (none { $key eq $_ } qw(atomic LOCK_SH LOCK_EX LOCK_NB));
  51         149  
33             }
34              
35 19         58 my ($type, $proper_what) = $class->_guess_what($what);
36              
37              
38 19 100       59 if ($type eq 'string') { return IO::String->new($proper_what) }
  3         24  
39 16 100       33 if ($type eq 'file') {
40 15 100       91 my $fh = $opt->{'atomic'} ? IO::AtomicFile->new() : IO::File->new();
41 15 50       507 $fh->open($proper_what, $how)
42             or confess 'error opening file "'.$proper_what.'" - '.$!;
43              
44             # locking if requested
45 15 100 100     1292 if ($opt->{'LOCK_SH'} or $opt->{'LOCK_EX'}) {
46 8 100       170 flock($fh,
    100          
    100          
    100          
47             ($opt->{'LOCK_SH'} ? LOCK_SH : 0)
48             | ($opt->{'LOCK_EX'} ? LOCK_EX : 0)
49             | ($opt->{'LOCK_NB'} ? LOCK_NB : 0)
50             ) or confess 'flock failed - '.$!;
51             }
52              
53 11         53 return $fh;
54             }
55 1 50       4 if ($type eq 'iofile') { return $proper_what }
  1         4  
56 0 0       0 if ($type eq 'iostring') { return $proper_what }
  0         0  
57 0 0       0 if ($type eq 'http') { die 'no http support yet :-|' }
  0         0  
58             }
59              
60             sub _guess_what {
61 31     31   7175 my $class = shift;
62 31         42 my $what = shift;
63              
64 31 100       141 if (!blessed($what)) { } # not blessed, do nothing
    50          
    50          
    0          
65 0         0 elsif ($what->isa('Path::Class::File')) { $what = $what->stringify }
66 1     1   7 elsif (any { $what->isa($_) } qw(IO::File IO::AtomicFile IO::Uncompress::Bunzip2)) {
67 1 50       9 confess 'passed unopened IO::File'
68             if not $what->opened;
69 1         14 return ('iofile', $what);
70             }
71 0         0 elsif ($what->isa('IO::String')) { return ('iostring', $what) }
72 0         0 else { confess 'no support for '.blessed($what) };
73              
74 30         48 my $ref_what = ref($what);
75 30 100       77 if ($ref_what eq 'ARRAY') { return ('file', File::Spec->catfile(@{$what})) }
  16 100       17  
  16 50       309  
76 1         3 elsif ($ref_what eq 'SCALAR') { return ('string', $what) }
77             elsif ($ref_what eq '') {} # do nothing here if not reference
78 0         0 else { confess 'no support for ref '.(ref $what) }
79              
80             # check for typeglobs
81 13 100 100     46 if ((ref \$what eq 'GLOB') and (my $fh = *{$what}{IO})) {
  1         15  
82 1         22 return ('iofile', $fh);
83             }
84              
85 12 100       31 if ($what =~ m{^file://(.+)$}) { return ('file', $1) } # local file
  1         6  
86 11 100       28 if ($what =~ m{^https?://}) { return ('http', $what) } # http link
  2         6  
87 9 100       20 if ($what =~ m{^<}) { return ('string', $what) } # xml string
  1         5  
88 8 100       17 if ($what =~ m(^{)) { return ('string', $what) } # json string
  2         7  
89 6 100       15 if ($what =~ m{^\[}) { return ('string', $what) } # json string
  1         3  
90 5 100       14 if ($what =~ m{\n[\s\w]}) { return ('string', $what) } # multi-line string
  1         6  
91 4 100       12 if ($what eq '') { return ('string', '') } # empty string
  2         7  
92 2         2 { return ('file', $what) } # default is filename
  2         7  
93             }
94              
95             sub read {
96 10     10 1 3971 my $class = shift;
97 10         42 my $what = shift;
98 10         13 my $opt = shift;
99 10 50       25 confess 'too many arguments'
100             if @_;
101              
102 10         32 return $class->new($what, '<', $opt);
103             }
104              
105             sub write {
106 2     2 1 1331 my $class = shift;
107 2         4 my $what = shift;
108 2         4 my $opt = shift;
109 2 50       8 confess 'too many arguments'
110             if @_;
111              
112 2         7 return $class->new($what, '>', $opt);
113             }
114              
115             sub slurp {
116 4     4 1 2990 my $class = shift;
117 4         8 my $what = shift;
118 4         8 my $opt = shift;
119 4 50       18 confess 'too many arguments'
120             if @_;
121              
122 4         14 my $fh = $class->read($what, $opt);
123              
124             # use event loop when AnyEvent is loaded (skip IO::String, doesn't work and makes no sense)
125             # not supported under MSWin32
126 4 0 33     43 if ($INC{'AnyEvent.pm'} and not $fh->isa('IO::String') and ($^O ne 'MSWin32')) {
      33        
127 0 0       0 eval 'use AnyEvent::Handle'
128             if not $INC{'AnyEvent/Handle.pm'};
129 0         0 my $eof = AnyEvent->condvar;
130 0         0 my $content = '';
131             my $hdl = AnyEvent::Handle->new(
132             fh => $fh,
133             on_read => sub {
134 0     0   0 $content .= delete $_[0]->{'rbuf'};
135             },
136             on_eof => sub {
137 0     0   0 $eof->send;
138             },
139             on_error => sub {
140 0     0   0 my ($hdl, $fatal, $msg) = @_;
141 0         0 $hdl->destroy;
142 0         0 $eof->croak($msg);
143             }
144 0         0 );
145              
146 0         0 $eof->recv;
147 0         0 $hdl->destroy;
148 0         0 close $fh;
149 0         0 return $content;
150             }
151              
152 4         6 my $content = do { local $/; <$fh> };
  4         13  
  4         108  
153 4         44 close $fh;
154 4         30 return $content;
155             }
156              
157             sub spew {
158 5     5 1 2991 my $class = shift;
159 5         8 my $what = shift;
160 5         8 my $data = shift;
161 5         6 my $opt = shift;
162 5 50       18 confess 'too many arguments'
163             if @_;
164              
165             # "parade" to allow safe locking
166 5         13 my $fh = $class->new($what, '+>>', $opt);
167 4         78 $fh->seek(0,0);
168 4         65 $fh->truncate(0);
169              
170             # use event loop when AnyEvent is loaded (skip IO::String, doesn't work and makes no sense)
171 4 50 33     128 if ($INC{'AnyEvent.pm'} and not $fh->isa('IO::String')) {
172 0 0       0 eval 'use AnyEvent::Handle'
173             if not $INC{'AnyEvent/Handle.pm'};
174              
175 0         0 my $eof = AnyEvent->condvar;
176             my $hdl = AnyEvent::Handle->new(
177             fh => $fh,
178             on_drain => sub {
179 0     0   0 $eof->send;
180             },
181             on_error => sub {
182 0     0   0 my ($hdl, $fatal, $msg) = @_;
183 0         0 $hdl->destroy;
184 0         0 $eof->croak($msg);
185             }
186 0         0 );
187              
188 0         0 $hdl->push_write($data);
189              
190 0         0 $eof->recv;
191 0         0 $hdl->destroy;
192 0         0 close $fh;
193 0         0 return;
194             }
195              
196 4         46 print $fh $data;
197 4 50       41 $fh->close || confess 'failed to close file - '.$!;
198 4         301 return;
199             }
200              
201             1;
202              
203              
204             __END__