File Coverage

blib/lib/IO/Easy.pm
Criterion Covered Total %
statement 93 127 73.2
branch 23 36 63.8
condition 4 8 50.0
subroutine 21 28 75.0
pod 20 20 100.0
total 161 219 73.5


line stmt bran cond sub pod time code
1             package IO::Easy;
2              
3 6     6   441430 use Class::Easy;
  6         96254  
  6         117  
4              
5 6     6   1089 use vars qw($VERSION);
  6         11  
  6         430  
6             $VERSION = '0.16';
7              
8 6     6   39 use File::Spec;
  6         13  
  6         3464  
9              
10             my $stat_methods = [qw(dev inode mode nlink uid gid rdev size atime mtime ctime blksize blocks)];
11             my $stat_methods_hash = {};
12              
13             sub import {
14 28     28   7363 my $pack = shift;
15 28         75 my $callpkg = caller;
16 28         50 my @params = @_;
17            
18 28 50       114 my $import_ok = (scalar grep {$_ eq 'no_script'} @params) ? 0 : 1;
  0         0  
19 28 50       79 my $script_ok = (scalar grep {$_ eq 'project'} @params) ? 1 : 0;
  0         0  
20              
21             # probably check for try_to_use is enough
22             return
23 28         428 if defined *{"$callpkg\::file"}{CODE}
  9         52  
24 28 100 66     42 and Class::Easy::sub_fullname (*{"$callpkg\::file"}{CODE}) eq 'IO::Easy::__ANON__';
25            
26 19 50 33     361 if ($script_ok || $import_ok) {
27            
28 19 50       62 my $callpkg = $script_ok ? 'main' : caller;
29            
30 19         5077 require IO::Easy::File;
31 19         4300 require IO::Easy::Dir;
32            
33 19         315 my $io_easy_subclass = eval {$callpkg->isa ('IO::Easy')};
  19         432  
34            
35 19 50       284 return if $io_easy_subclass;
36            
37 19         49 foreach my $type (qw(file dir)) {
38             make_accessor ($callpkg, $type, default => sub {
39 9     9   84 my $class = 'IO::Easy::' . ucfirst ($type);
40 9 100       86 return $class->new (@_)
41             if @_ > 0;
42 6         212 $class
43 38         1228 });
44             }
45             }
46             }
47              
48             foreach my $i (0 .. $#$stat_methods) {
49             has ($stat_methods->[$i], default => sub {
50 7     7   9673 my $self = shift;
51 7         42 my $stat = $self->stat;
52              
53 7         45 return $stat->[$i];
54             });
55             $stat_methods_hash->{$stat_methods->[$i]} = $i;
56             }
57              
58             use overload
59 6         46 '""' => 'path',
60 6     6   13117 'cmp' => '_compare';
  6         9508  
61              
62             our $FS = 'File::Spec';
63              
64             sub new {
65 43     43 1 192 my $class = shift;
66 43         88 my $path = shift;
67 43   50     347 my $args = shift || {};
68            
69 43         599 my $self = bless {%$args, path => $path}, $class;
70            
71 43         72 my $initialized = $self;
72 43 100       322 $initialized = $self->_init
73             if $self->can ('_init');
74            
75 43         246 return $initialized;
76             }
77              
78             sub attach_interface {
79 25     25 1 39 my $self = shift;
80            
81 25 100       716 if (-f $self->{path}) {
    50          
82 12         43 return $self->as_file;
83             } elsif (-d $self->{path}) {
84 13         235 return $self->as_dir;
85             }
86             }
87              
88             sub name {
89 7     7 1 37 my $self = shift;
90            
91 7         89 my ($vol, $dir, $file) = $FS->splitpath ($self->{path});
92            
93 7         20 return $file;
94             }
95              
96             sub base_name {
97 0     0 1 0 my $self = shift;
98            
99 0         0 my $file_name = $self->name;
100            
101 0         0 my $base_name = ($file_name =~ /(.*?)(?:\.[^\.]+)?$/)[0];
102            
103 0         0 return $base_name;
104             }
105              
106             sub extension {
107 0     0 1 0 my $self = shift;
108            
109 0         0 my $file_name = $self->name;
110            
111 0         0 my $extension = ($file_name =~ /(?:.*?)(?:\.([^\.]+))?$/)[0];
112            
113 0         0 return $extension;
114             }
115              
116             sub as_file {
117 17     17 1 29 my $self = shift;
118            
119 17         166 my $file_object = {%$self};
120 17         88 try_to_use ('IO::Easy::File');
121 17         4588 bless $file_object, 'IO::Easy::File';
122             }
123              
124             sub as_dir {
125 16     16 1 35 my $self = shift;
126            
127 16         61 my $file_object = {%$self};
128 16         164 try_to_use ('IO::Easy::Dir');
129 16         3594 bless $file_object, 'IO::Easy::Dir';
130             }
131              
132             sub append {
133 26     26 1 51 my $self = shift;
134            
135 26         476 my $appended = File::Spec->join ($self->{path}, @_);
136 26         185 return IO::Easy->new ($appended);
137             }
138              
139             sub file_io {
140 2     2 1 11 my $self = shift;
141            
142 2         23 my $appended = File::Spec->join ($self->{path}, @_);
143 2         15 return IO::Easy::File->new ($appended);
144             }
145              
146             sub dir_io {
147 4     4 1 20 my $self = shift;
148            
149 4         217 my $appended = File::Spec->join ($self->{path}, @_);
150 4         34 return IO::Easy::Dir->new ($appended);
151             }
152              
153             sub append_in_place {
154 0     0 1 0 my $self = shift;
155            
156 0         0 my $appended = File::Spec->join ($self->{path}, @_);
157 0         0 $self->{path} = $appended;
158            
159 0         0 return $self;
160             }
161              
162             sub path {
163 82     82 1 2898 my $self = shift;
164            
165 82         2580 return $self->{path};
166             }
167              
168             sub _compare { # for overload only
169 4     4   81 my $self = shift;
170 4         7 my $value = shift;
171 4         24 return $self->{path} cmp $value;
172             }
173              
174             # we need ability to create abstract file object without any
175             # filesystem checks, but when call any method, assigned to
176             # concrete class, we must create another object and call this method
177              
178             sub touch {
179 3     3 1 4002173 my $self = shift;
180              
181 3 100       25 if (! -e $self) {
182 1         6 return $self->as_file->touch;
183             }
184 2         17 return $self->attach_interface->touch;
185             }
186              
187             sub abs_path {
188 3     3 1 7 my $self = shift;
189            
190 3         10 my $pack = ref $self;
191            
192 3 100       330 if ($FS->file_name_is_absolute ($self->{path})) {
193 1         21 return $self;
194             } else {
195 2         63 return $pack->new ($FS->rel2abs ($self->{path}))
196             }
197            
198             }
199              
200             sub rel_path {
201 13     13 1 70 my $self = shift;
202 13         15 my $relative_to = shift;
203            
204 13         30 my $path = $self->{path};
205 13 50       66 $path = $self->abs_path
206             if $FS->file_name_is_absolute ($relative_to);
207            
208 13         140 return $FS->abs2rel ($path, $relative_to);
209             }
210              
211             sub path_components {
212 0     0 1 0 my $self = shift;
213 0         0 my $relative = shift;
214            
215 0         0 my $path = $self->{path};
216            
217 0 0       0 if ($relative) {
218 0         0 $path = $FS->abs2rel ($path, $relative);
219             }
220            
221 0         0 return $FS->splitdir ($path);
222            
223             }
224              
225             sub stat {
226 15     15 1 84 my $self = shift;
227            
228 15         359 my $stat = [stat $self->{path}];
229            
230 15 100       64 return $stat
231             unless @_;
232            
233 8         19 my $result = [];
234            
235 8         24 foreach my $stat_opt (@_) {
236 24 50       125 if ($stat_opt =~ /^(\d+)$/) {
    50          
237 0         0 push @$result, $stat->[$1];
238             } elsif (exists $stat_methods_hash->{$stat_opt}) {
239 24         66 push @$result, $stat->[$stat_methods_hash->{$stat_opt}];
240             } else {
241 0         0 die "unknown stat field: $stat_opt";
242             }
243             }
244            
245 8         40 return @$result;
246             }
247              
248             # TODO: rename to last_modified, add sub modified_since?
249             sub modified {
250 0     0 1   my $self = shift;
251            
252 0           my $stat = $self->stat;
253 0           return $stat->[9];
254             }
255              
256             sub parent {
257 0     0 1   my $self = shift;
258            
259 0           return $self->up (@_);
260             }
261              
262             sub up {
263 0     0 1   my $self = shift;
264            
265 0           my @chunks = $FS->splitdir ($self->path);
266 0           pop @chunks;
267            
268 0           my $updir = $FS->catdir (@chunks);
269            
270 0           try_to_use ('IO::Easy::Dir');
271            
272 0 0         $updir = IO::Easy::Dir->current
273             if $updir eq '';
274            
275 0           return IO::Easy::Dir->new ($updir);
276             }
277              
278              
279             1;
280              
281             =head1 NAME
282              
283             IO::Easy - is easy to use class for operations with filesystem objects.
284              
285             =head1 ABSTRACT
286              
287             We wanted to provide Perl with the interface for file system objects
288             with the simplicity similar to shell. The following operations can be
289             used as an example: operations for recursive creation (mkdir -p) and
290             removing (rm -rf), touching file.
291              
292             IO::Easy transparently handles OS path delimiters (e.g., Win* or *nix) using
293             File::Spec module and does not require a lot of additional modules from CPAN.
294              
295             For better understanding of IO::Easy processing principles you should
296             keep in mind that it operates with "Path Context". "Path Context" means
297             that for any path in any file system IO::Easy takes path parts which are
298             between path separators, but doesn't include path separators themselves,
299             and tries to build the path in the current system using these path parts.
300             This way it can substitute different path separators from system to system
301             (as long as they may differ depending on operating system, this also
302             includes drive specification e.g. for Windows) and doesn't depend on
303             some system specifics of paths representation.
304              
305             =head1 SYNOPSIS
306              
307             use IO::Easy;
308            
309             # abstract filesystem i/o interface
310             my $io = IO::Easy->new ('.');
311            
312             # directory interface
313             my $dir = $io->as_dir;
314            
315             # or easy
316             $dir = dir->current;
317             $dir = dir->new ('.');
318            
319             # or even easier
320             $dir = dir ('.');
321              
322             # file object "./example.txt" for unix
323             my $file = $io->append ('example.txt')->as_file;
324            
325             # or
326             $file = $io->file_io ('example.txt');
327              
328             my $content = "Some text goes here!";
329            
330             # Overwrite file contents with $content
331             $file->store ($content);
332            
333             or
334            
335             # easier scripts: you can replace IO::Easy::Dir for dir and so on
336             use IO::Easy qw(script);
337            
338             my $abs_path = dir->current->abs_path; # IO::Easy::Dir->current->abs_path;
339              
340             my $test_file = file->new ('test');
341              
342             $test_file->touch;
343              
344             print "ok"
345             if -f $test_file and $test_file->size eq 0;
346            
347             =head1 METHODS
348              
349             =head2 new
350              
351             Creates new IO::Easy object, takes path as parameter. IO::Easy object
352             for abstract file system path. For operating with typed objects there
353             were 2 additional modules created:
354             IO::Easy::File
355             IO::Easy::Dir
356              
357             You can use method attach_interface for automatic object conversion
358             for existing filesystem object or force type by using methods
359             as_file or as_dir.
360              
361             Init file object:
362              
363             my $io = IO::Easy->new ('/');
364              
365             my $file = $io->append(qw(home user my_stuff.bak file.txt));
366              
367             In examples we will use this object to show results of method call.
368              
369             =cut
370              
371             =head2 filesystem object path manipulation
372              
373             =head3 path
374              
375             return current filesystem object path, also available as overload of "" # ???
376              
377             # example :
378             $file->path # /home/user/my_stuff/file.txt
379              
380             =cut
381              
382             =head3 name
383              
384             return current filesystem object name, without path (filename in most of cases)
385              
386             # example :
387             $file->name # file.txt
388              
389             =cut
390              
391             =head3 base_name, extension
392              
393             name part before last dot and after last dot
394              
395             # example :
396             $file->base_name # file
397             $file->extension # txt
398              
399             =cut
400              
401             =head2 as_file, as_dir
402              
403             rebless object with specified type (currently 'dir' or 'file')
404              
405             =cut
406              
407             =head3 abs_path
408              
409             absolute path
410              
411             # example :
412             $file->abs_path # /home/user/my_stuff.bak/file.txt
413              
414             =cut
415              
416             =head3 append, append_in_place
417              
418             append filesystem objects to IO::Easy object
419              
420             my $config = IO::Easy::Dir->current->append (qw(etc config.json));
421              
422             produce ./etc/config.json on unix
423              
424             =cut
425              
426             =head3 file_io, dir_io
427              
428             append filesystem objects to IO::Easy subclass object
429              
430             my $config = IO::Easy::Dir->current->file_io (qw(etc config.json));
431              
432             produce ./etc/config.json on unix, blessed into IO::Easy::File
433              
434             =cut
435              
436             =head3 up, parent
437              
438             directory container for io object
439              
440             my $config = IO::Easy::Dir->current->append (qw(etc config.json)); # './etc/config.json'
441             my $config_dir = $config->up; # './etc'
442              
443             =cut
444              
445             =head3 rel_path
446              
447             relative path to specified directory
448            
449             my $current = IO::Easy::Dir->current; # '.'
450             my $config = $current->append (qw(etc config.json)); # './etc/config.json'
451             my $config_rel = $config->rel_path ($current); # 'etc/config.json'
452              
453             =cut
454              
455             =head3 path_components
456              
457             path, split by filesystem separators
458              
459             =cut
460              
461             =cut
462              
463             =head2 filesystem object manipulation
464              
465             =head3 attach_interface
466              
467             rebless object with autodetected filesystem object type
468              
469             =cut
470              
471             =head3 stat, modified, dev, inode, mode, nlink, uid, gid, rdev, size, atime, mtime, ctime, blksize, blocks
472              
473             complete stat array or this array accessors
474              
475             =cut
476              
477             =head3 touch
478              
479             constructor for IO::Easy::Dir object
480            
481             my $current = IO::Easy::Dir->current; # '.'
482             my $config = $current->append (qw(etc config.json)); # './etc/config.json'
483             $config->touch; # file created
484              
485             =cut
486              
487             =cut
488              
489             =head1 AUTHOR
490              
491             Ivan Baktsheev, C<< >>
492              
493             =head1 BUGS
494              
495             Please report any bugs or feature requests to my email address,
496             or through the web interface at L.
497             I will be notified, and then you'll automatically be notified
498             of progress on your bug as I make changes.
499              
500             =head1 SUPPORT
501              
502              
503              
504             =head1 ACKNOWLEDGEMENTS
505              
506              
507              
508             =head1 COPYRIGHT & LICENSE
509              
510             Copyright 2007-2009 Ivan Baktsheev
511              
512             This program is free software; you can redistribute it and/or modify it
513             under the same terms as Perl itself.
514              
515              
516             =cut