File Coverage

lib/Path/Extended/File.pm
Criterion Covered Total %
statement 158 173 91.3
branch 75 90 83.3
condition 24 34 70.5
subroutine 33 33 100.0
pod 19 19 100.0
total 309 349 88.5


line stmt bran cond sub pod time code
1             package Path::Extended::File;
2              
3 27     27   2248 use strict;
  27         35  
  27         869  
4 27     27   111 use warnings;
  27         38  
  27         690  
5 27     27   102 use base qw( Path::Extended::Entity );
  27         30  
  27         10910  
6 27     27   15819 use IO::Handle;
  27         150831  
  27         1273  
7 27     27   183 use Sub::Install;
  27         35  
  27         184  
8              
9             sub _initialize {
10 170     170   347 my ($self, @args) = @_;
11              
12 170         1032 my $file = File::Spec->catfile( @args );
13 170         1692 $self->{_stringify_absolute} = 1; # always true for ::Extended::File
14 170         251 $self->{is_dir} = 0;
15 170         417 $self->_set_path($file);
16             }
17              
18             sub basename {
19 18     18 1 54 my $self = shift;
20 18         70 require File::Basename;
21 18         673 return File::Basename::basename( $self->{abs_path} );
22             }
23              
24             sub open {
25 116     116 1 1125 my ($self, $mode) = @_;
26              
27 116 100       394 $self->close if $self->is_open;
28              
29 116   100     232 $mode ||= 'r';
30              
31 116         102 my $fh;
32 116 100       285 if ( $mode =~ /:/ ) {
33             open $fh, $mode, $self->_absolute
34 5 50       26 or do { $self->log( error => "Can't open $self: $!" ); return; };
  0         0  
  0         0  
35             }
36             else {
37             open $fh, IO::Handle::_open_mode_string($mode), $self->{abs_path}
38 111 50       512 or do { $self->log( error => "Can't open $self: $!" ); return; };
  0         0  
  0         0  
39             }
40              
41 116 100 100     7371 return $fh if $self->{_compat} && defined wantarray;
42              
43 105         216 $self->{handle} = $fh;
44              
45 105         259 $self;
46             }
47              
48 4     4 1 11 sub openr { shift->open('r') }
49 54     54 1 119 sub openw { shift->open('w') }
50              
51             sub sysopen {
52 2     2 1 4 my $self = shift;
53              
54 2 100       30 $self->close if $self->is_open;
55              
56             CORE::sysopen my $fh, $self->_absolute, @_
57 2 50       9 or do { $self->log( error => "Can't open $self: $!" ); return; };
  0         0  
  0         0  
58              
59 2         5 $self->{handle} = $fh;
60              
61 2         8 $self;
62             }
63              
64             sub close {
65 120     120 1 439 my $self = shift;
66              
67 120 100       401 if ( my $fh = delete $self->{handle} ) {
68 107         1535 CORE::close $fh;
69             }
70             }
71              
72             sub binmode {
73 4     4 1 5 my $self = shift;
74              
75 4 100       11 return unless $self->is_open;
76              
77 3         5 my $fh = $self->{handle};
78              
79 3 50       12 if ( @_ ) {
80 0         0 CORE::binmode $fh, shift;
81             }
82             else {
83 3         7 CORE::binmode $fh;
84             }
85             }
86              
87             BEGIN {
88 27     27   14222 my @io_methods = qw(
89             print printf say getline getlines read sysread write syswrite
90             autoflush flush printflush getc ungetc truncate blocking
91             eof fileno error sync fcntl ioctl
92             );
93              
94 27         75 foreach my $method (@io_methods) {
95             Sub::Install::install_sub({
96             as => $method,
97             code => sub {
98 96 100   96   1829 return unless $_[0]->is_open; shift->{handle}->$method(@_);
  86         1196  
99             },
100 594         20090 });
101             }
102             }
103              
104 2 100   2 1 7 sub lock_ex { return unless $_[0]->is_open; shift->_lock }
  1         3  
105 2 100   2 1 7 sub lock_sh { return unless $_[0]->is_open; shift->_lock('share') }
  1         3  
106              
107             sub _lock {
108 2     2   3 my ($self, $mode) = @_;
109              
110 2         4 my $fh = $self->{handle};
111              
112 2         11 require Fcntl;
113 2 100 66     22 flock $fh, ( $mode && $mode eq 'share' )
114             ? Fcntl::LOCK_SH()
115             : Fcntl::LOCK_EX();
116             }
117              
118             sub seek {
119 2     2 1 340 my ($self, $pos, $whence) = @_;
120              
121 2 100       5 return unless $self->is_open;
122              
123 1         3 my $fh = $self->{handle};
124              
125 1         6 seek $fh, $pos, $whence;
126             }
127              
128             sub sysseek {
129 2     2 1 5 my ($self, $pos, $whence) = @_;
130              
131 2 100       5 return unless $self->is_open;
132              
133 1         2 my $fh = $self->{handle};
134              
135 1         5 sysseek $fh, $pos, $whence;
136             }
137              
138             sub tell {
139 2     2 1 326 my $self = shift;
140              
141 2 100       9 return unless $self->is_open;
142              
143 1         1 my $fh = $self->{handle};
144              
145 1         4 tell $fh;
146             }
147              
148             sub slurp {
149 18     18 1 1927 my ($self, @args) = @_;
150              
151 18 100 66     89 my $options = ( @args == 1 and ref $args[0] eq 'HASH' )
152             ? $args[0]
153             : { @args };
154              
155 18   100     73 my $iomode = $options->{iomode} || 'r';
156 18         35 $self->open($iomode);
157 18 50       46 unless ( $self->is_open ) {
158 0         0 $self->log( warn => "Can't read", $self->{abs_path}, $! );
159 0         0 return;
160             }
161              
162 18 100       42 $self->binmode if $options->{binmode};
163 18         20 my @callbacks;
164             my $callback = sub {
165 25     25   24 my $line = shift;
166 25         33 for my $subr (@callbacks) { $line = $subr->(local $_ = $line) }
  12         52  
167 25         78 $line;
168 18         73 };
169 18 100       39 if ( $options->{chomp} ) {
170 3     7   8 push @callbacks, sub { my $line = shift; chomp $line; $line };
  7         5  
  7         12  
  7         13  
171             }
172 18 100       83 if ( $options->{decode} ) {
173 2         12 require Encode;
174             push @callbacks, sub {
175 2     2   9 Encode::decode( $options->{decode}, shift )
176 2         7 };
177             }
178 18 100       33 if ( $options->{callback} ) {
179 2         4 push @callbacks, $options->{callback};
180             }
181 18         17 my $filter;
182 18 100       36 if ( my $rule = $options->{filter} ) {
183 2         16 $filter = qr/$rule/;
184             }
185 18 50       38 $options->{ignore_return_value} = 1 if !defined wantarray;
186              
187             # shortcut
188 18 100 66     60 if (!@callbacks and !$filter and !wantarray) {
      66        
189 7         9 my $got = do { local $/; $self->getline };
  7         18  
  7         24  
190 7         247 $self->close;
191 7         62 return $got;
192             }
193              
194 11         14 my @lines;
195 11         48 while( defined (my $line = $self->getline )) {
196 25         601 $line = $callback->($line);
197 25 100 100     85 next if $filter && $line !~ /$filter/;
198 22 50       66 push @lines, $line unless $options->{ignore_return_value};
199             }
200 11         235 $self->close;
201 11 100       109 return wantarray ? @lines : join '', @lines;
202             }
203              
204             sub grep { # just a spoonful of sugar
205 2     2 1 2323 my ($self, $rule, @args) = @_;
206              
207 2 50 33     11 my $options = ( @args == 1 and ref $args[0] eq 'HASH' )
208             ? $args[0]
209             : { @args };
210              
211 2         5 $options->{filter} = $rule;
212              
213 2         6 $self->slurp($options);
214             }
215              
216             sub save {
217 32     32 1 142 my ($self, $content, @args) = @_;
218              
219 32 100 66     138 my $options = ( @args == 1 and ref $args[0] eq 'HASH' )
220             ? $args[0]
221             : { @args };
222              
223 32 100       84 if ( $options->{mkdir} ) {
224 5         54 $self->parent->mkdir;
225             }
226 32 50 33     216 my $mode = $options->{mode} || $options->{append} ? '>>' : '>';
227 32         84 $self->open($mode);
228 32 50       93 unless ( $self->is_open ) {
229 0         0 $self->log( warn => "Can't save", $self->_absolute, $! );
230 0         0 return;
231             }
232              
233 32 50       81 if ( $options->{lock} ) {
234 0 0       0 unless ( $self->lock_ex ) {
235 0         0 $self->log( warn => "Can't lock", $self->{abs_path}, $! );
236 0         0 return;
237             }
238             }
239 32 100       79 $self->binmode if $options->{binmode};
240              
241 32         39 my @callbacks;
242             my $callback = sub {
243 32     32   45 my $line = shift;
244 32         63 for my $subr (@callbacks) { $line = $subr->(local $_ = $line) }
  4         42  
245             $line
246 32         142 };
  32         171  
247 32 100       87 if ( $options->{encode} ) {
248 2         2262 require Encode;
249             push @callbacks, sub {
250 2     2   8 Encode::encode( $options->{encode}, shift )
251 2         7245 };
252             }
253 32 100       88 if ( $options->{callback} ) {
254 2         3 push @callbacks, $options->{callback};
255             }
256              
257             $self->print(
258 32         65 map { $callback->($_) }
  0         0  
259 32 50       107 ref $content eq 'ARRAY' ? @{ $content } : $content
260             );
261 32         399 $self->close;
262              
263 32 100       136 if ( $options->{mtime} ) {
264 1         5 $self->mtime( $options->{mtime} );
265             }
266              
267 32         219 $self;
268             }
269              
270             sub touch {
271 53     53 1 69 my $self = shift;
272              
273 53 100       130 if ( $self->exists ) {
274 1         8 $self->mtime(time);
275             }
276             else {
277 52 50       202 $self->openw or return;
278 52         170 $self->close;
279             }
280 53         186 $self;
281             }
282              
283 24   66 24 1 517 sub size { return -s ( $_[0]->{handle} || $_[0]->{abs_path} ) }
284              
285             sub mtime {
286 6     6 1 515 my $self = shift;
287              
288 6 100       17 return unless $self->exists;
289              
290 5 100       18 if ( @_ ) {
291 3         7 my $mtime = shift;
292 3         10 utime $mtime, $mtime, $self->_absolute;
293             }
294             else {
295 2         14 return $self->stat->mtime;
296             }
297             }
298              
299 2     2 1 968 sub remove { shift->unlink(@_) }
300              
301             1;
302              
303             __END__