File Coverage

blib/lib/Mojo/File.pm
Criterion Covered Total %
statement 161 161 100.0
branch 62 68 91.1
condition 27 31 87.1
subroutine 53 53 100.0
pod 34 34 100.0
total 337 347 97.1


line stmt bran cond sub pod time code
1             package Mojo::File;
2 83     1864   1370984 use Mojo::Base -strict;
  83         118  
  83         456  
3 83     83   35407 use overload '@{}' => sub { shift->to_array }, bool => sub {1}, '""' => sub { ${$_[0]} }, fallback => 1;
  83     2148   94653  
  83     2629   795  
  1857     43   2846  
  100         476  
  5327         41936  
  5327         95012  
4              
5 83     83   7005 use Carp qw(croak);
  83         108  
  83         4115  
6 83     83   354 use Cwd qw(getcwd);
  83         117  
  83         3705  
7 83     83   322 use Exporter qw(import);
  83         103  
  83         1833  
8 83     83   424 use File::Basename ();
  83         200  
  83         1651  
9 83     83   36551 use File::Copy qw(copy move);
  83         254230  
  83         4786  
10 83     83   519 use File::Path ();
  83         105  
  83         1960  
11 83     83   28218 use File::Spec::Functions qw(abs2rel canonpath catfile file_name_is_absolute rel2abs splitdir);
  83         50729  
  83         5822  
12 83     83   34950 use File::stat ();
  83         460012  
  83         1966  
13 83     83   74635 use File::Temp ();
  83         1013609  
  83         2274  
14 83     83   26574 use IO::File ();
  83         52130  
  83         2103  
15 83     83   34229 use Mojo::Collection;
  83         1560  
  83         4018  
16 83     83   446 use Mojo::Util qw(decode encode);
  83         104  
  83         206689  
17              
18             our @EXPORT_OK = ('curfile', 'path', 'tempdir', 'tempfile');
19              
20 174     174 1 632 sub basename { File::Basename::basename ${shift()}, @_ }
  174         9330  
21              
22 500     500 1 42632 sub child { $_[0]->new(${shift()}, @_) }
  500         1428  
23              
24             sub chmod {
25 4     4 1 8 my ($self, $mode) = @_;
26 4 100       172 chmod $mode, $$self or croak qq{Can't chmod file "$$self": $!};
27 3         8 return $self;
28             }
29              
30             sub copy_to {
31 2     2 1 6 my ($self, $to) = @_;
32 2 50       9 copy($$self, $to) or croak qq{Can't copy file "$$self" to "$to": $!};
33 2 100       326 return $self->new(-d $to ? ($to, File::Basename::basename $self) : $to);
34             }
35              
36 288     288 1 245513 sub curfile { __PACKAGE__->new(Cwd::realpath((caller)[1])) }
37              
38 97     97 1 564 sub dirname { $_[0]->new(scalar File::Basename::dirname ${$_[0]}) }
  97         3297  
39              
40             sub download {
41 13   100 13 1 126 my ($self, $url, $options) = (shift, shift, shift // {});
42             my $ua = $options->{ua}
43 13   66     50 || do { require Mojo::UserAgent; Mojo::UserAgent->new(max_redirects => 10, max_response_size => 0) };
44 13   100     68 my $tx = _download_error($ua->transactor->download($ua->head($url => $options->{headers} // {}), $$self));
45 10 100       142 return $tx ? !!_download_error($ua->start($tx)) : 1;
46             }
47              
48 96 100   96 1 313 sub extname { shift->basename =~ /.+\.([^.]+)$/ ? $1 : '' }
49              
50 51     51 1 66 sub is_abs { file_name_is_absolute ${shift()} }
  51         261  
51              
52             sub list {
53 31   100 31 1 125 my ($self, $options) = (shift, shift // {});
54              
55 31 100       415 return Mojo::Collection->new unless -d $$self;
56 29 50       812 opendir(my $dir, $$self) or croak qq{Can't open directory "$$self": $!};
57 29 100       709 my @files = grep { $_ ne '.' && $_ ne '..' } readdir $dir;
  250         557  
58 29 100       92 @files = grep { !/^\./ } @files unless $options->{hidden};
  172         240  
59 29         45 @files = map { catfile $$self, $_ } @files;
  188         462  
60 29 100       73 @files = grep { !-d } @files unless $options->{dir};
  170         1487  
61              
62 29         105 return Mojo::Collection->new(map { $self->new($_) } sort @files);
  157         201  
63             }
64              
65             sub list_tree {
66 236   100 236 1 1072 my ($self, $options) = (shift, shift // {});
67 236 100       3451 return Mojo::Collection->new unless -d $$self;
68              
69 227         439 my (@results, $walk);
70             $walk = sub {
71 740     740   1262 my ($path, $depth) = @_;
72 740 50       23064 opendir my $dh, $path or return;
73 740 100       13975 my @names = sort grep { $_ ne '.' && $_ ne '..' } readdir $dh;
  4137         10652  
74 740 100       2080 @names = grep { !/^\./ } @names unless $options->{hidden};
  2583         4061  
75 740         1169 for my $name (@names) {
76 2649         3994 my $child = $self->new($path, $name);
77 2649         30950 my $is_dir = -d $$child;
78 2649 100 100     7533 push @results, $child if $options->{dir} || !$is_dir;
79 2649 100 100     11696 $walk->($$child, $depth + 1) if $is_dir && (!$options->{max_depth} || $depth + 1 < $options->{max_depth});
      100        
80             }
81 227         1517 };
82 227         672 $walk->($$self, 0);
83              
84 227         2227 return Mojo::Collection->new(@results);
85             }
86              
87 1     1 1 799 sub lstat { File::stat::lstat(${shift()}) }
  1         4  
88              
89             sub make_path {
90 23     23 1 30 my $self = shift;
91 23         2163 File::Path::make_path $$self, @_;
92 23         93 return $self;
93             }
94              
95             sub move_to {
96 10     10 1 26 my ($self, $to) = @_;
97 10 50       43 move($$self, $to) or croak qq{Can't move file "$$self" to "$to": $!};
98 10 100       651 return $self->new(-d $to ? ($to, File::Basename::basename $self) : $to);
99             }
100              
101             sub new {
102 11162     11162 1 230625 my $class = shift;
103 11162 100       13634 croak 'Invalid path' if grep { !defined } @_;
  19683         30486  
104 11160 100       39776 my $value = @_ == 1 ? $_[0] : @_ > 1 ? catfile @_ : canonpath getcwd;
    100          
105 11160   66     39266 return bless \$value, ref $class || $class;
106             }
107              
108             sub open {
109 141     141 1 1363 my $self = shift;
110 141         981 my $handle = IO::File->new;
111 141 100       4669 $handle->open($$self, @_) or croak qq{Can't open file "$$self": $!};
112 140         12379 return $handle;
113             }
114              
115 3352     3352 1 259136 sub path { __PACKAGE__->new(@_) }
  43         3632  
116              
117 919     919 1 3987 sub realpath { $_[0]->new(Cwd::realpath ${$_[0]}) }
  919         52791  
118              
119             sub remove {
120 52     52 1 105 my ($self, $mode) = @_;
121 52 100 66     4964 unlink $$self or croak qq{Can't remove file "$$self": $!} if -e $$self;
122 51         967 return $self;
123             }
124              
125             sub remove_tree {
126 2     2 1 2 my $self = shift;
127 2         902 File::Path::remove_tree $$self, @_;
128 2         13 return $self;
129             }
130              
131             sub sibling {
132 283     283 1 436 my $self = shift;
133 283         5576 return $self->new(scalar File::Basename::dirname($self), @_);
134             }
135              
136             sub slurp {
137 131     131 1 1031 my ($self, $encoding) = @_;
138              
139 131 100       4811 CORE::open my $file, '<', $$self or croak qq{Can't open file "$$self": $!};
140 130         554 my $ret = my $content = '';
141 130         959 while ($ret = $file->sysread(my $buffer, 131072, 0)) { $content .= $buffer }
  129         4071  
142 130 50       1140 croak qq{Can't read from file "$$self": $!} unless defined $ret;
143              
144 130 100       1867 return $encoding ? decode($encoding, $content) : $content;
145             }
146              
147             sub spew {
148 55     55 1 170 my ($self, $content, $encoding) = @_;
149 55 100       132 $content = encode($encoding, $content) if $encoding;
150 55 100       3248 CORE::open my $file, '>', $$self or croak qq{Can't open file "$$self": $!};
151 54 100 50     736 ($file->syswrite($content) // -1) == length $content or croak qq{Can't write to file "$$self": $!};
152 52         2740 return $self;
153             }
154              
155 1     1 1 5 sub spurt { shift->spew(join '', @_) }
156              
157 4     4 1 14 sub stat { File::stat::stat(${shift()}) }
  4         15  
158              
159 1     1 1 8 sub tap { shift->Mojo::Base::tap(@_) }
160              
161 38     38 1 244998 sub tempdir { __PACKAGE__->new(File::Temp->newdir(@_)) }
162              
163 53     53 1 4401 sub tempfile { __PACKAGE__->new(File::Temp->new(@_)) }
164              
165 142     142 1 253 sub to_abs { $_[0]->new(rel2abs ${$_[0]}) }
  142         494  
166              
167 2292     2292 1 2557 sub to_array { [splitdir ${shift()}] }
  2292         3936  
168              
169 2165     2165 1 2422 sub to_rel { $_[0]->new(abs2rel(${$_[0]}, $_[1])) }
  2165         4250  
170              
171 3341     3341 1 5607 sub to_string {"${$_[0]}"}
  3341         33247  
172              
173             sub touch {
174 4     4 1 734 my $self = shift;
175 4 100       72 $self->open('>') unless -e $$self;
176 4 50       58 utime undef, undef, $$self or croak qq{Can't touch file "$$self": $!};
177 4         18 return $self;
178             }
179              
180 1     1 1 673 sub with_roles { shift->Mojo::Base::with_roles(@_) }
181              
182             sub _download_error {
183 22     22   39 my $tx = shift;
184              
185 22 100       75 return $tx unless my $err = $tx->error;
186 6 100 100     58 return undef if $err->{message} eq 'Download complete' || $err->{message} eq 'Download incomplete';
187 3 100       307 croak "$err->{code} response: $err->{message}" if $err->{code};
188 2         553 croak "Download error: $err->{message}";
189             }
190              
191             1;
192              
193             =encoding utf8
194              
195             =head1 NAME
196              
197             Mojo::File - File system paths
198              
199             =head1 SYNOPSIS
200              
201             use Mojo::File;
202              
203             # Portably deal with file system paths
204             my $path = Mojo::File->new('/home/sri/.vimrc');
205             say $path->slurp;
206             say $path->dirname;
207             say $path->basename;
208             say $path->extname;
209             say $path->sibling('.bashrc');
210              
211             # Use the alternative constructor
212             use Mojo::File qw(path);
213             my $path = path('/tmp/foo/bar')->make_path;
214             $path->child('test.txt')->spew('Hello Mojo!');
215              
216             =head1 DESCRIPTION
217              
218             L is a scalar-based container for file system paths that provides a friendly API for dealing with different
219             operating systems.
220              
221             # Access scalar directly to manipulate path
222             my $path = Mojo::File->new('/home/sri/test');
223             $$path .= '.txt';
224              
225             =head1 FUNCTIONS
226              
227             L implements the following functions, which can be imported individually.
228              
229             =head2 curfile
230              
231             my $path = curfile;
232              
233             Construct a new scalar-based L object for the absolute path to the current source file.
234              
235             =head2 path
236              
237             my $path = path;
238             my $path = path('/home/sri/.vimrc');
239             my $path = path('/home', 'sri', '.vimrc');
240             my $path = path(File::Temp->newdir);
241              
242             Construct a new scalar-based L object, defaults to using the current working directory.
243              
244             # "foo/bar/baz.txt" (on UNIX)
245             path('foo', 'bar', 'baz.txt');
246              
247             =head2 tempdir
248              
249             my $path = tempdir;
250             my $path = tempdir('tempXXXXX');
251              
252             Construct a new scalar-based L object for a temporary directory with L.
253              
254             # Longer version
255             my $path = path(File::Temp->newdir('tempXXXXX'));
256              
257             =head2 tempfile
258              
259             my $path = tempfile;
260             my $path = tempfile(DIR => '/tmp');
261              
262             Construct a new scalar-based L object for a temporary file with L.
263              
264             # Longer version
265             my $path = path(File::Temp->new(DIR => '/tmp'));
266              
267             =head1 METHODS
268              
269             L implements the following methods.
270              
271             =head2 basename
272              
273             my $name = $path->basename;
274             my $name = $path->basename('.txt');
275              
276             Return the last level of the path with L.
277              
278             # ".vimrc" (on UNIX)
279             path('/home/sri/.vimrc')->basename;
280              
281             # "test" (on UNIX)
282             path('/home/sri/test.txt')->basename('.txt');
283              
284             =head2 child
285              
286             my $child = $path->child('.vimrc');
287              
288             Return a new L object relative to the path.
289              
290             # "/home/sri/.vimrc" (on UNIX)
291             path('/home')->child('sri', '.vimrc');
292              
293             =head2 chmod
294              
295             $path = $path->chmod(0644);
296              
297             Change file permissions.
298              
299             =head2 copy_to
300              
301             my $destination = $path->copy_to('/home/sri');
302             my $destination = $path->copy_to('/home/sri/.vimrc.backup');
303              
304             Copy file with L and return the destination as a L object.
305              
306             =head2 dirname
307              
308             my $name = $path->dirname;
309              
310             Return all but the last level of the path with L as a L object.
311              
312             # "/home/sri" (on UNIX)
313             path('/home/sri/.vimrc')->dirname;
314              
315             =head2 download
316              
317             my $bool = $path->download('https://example.com/test.tar.gz');
318             my $bool = $path->download('https://example.com/test.tar.gz', {headers => {Accept => '*/*'}});
319             my $bool = $path->download('https://example.com/test.tar.gz', {ua => Mojo::UserAgent->new});
320              
321             Download file from URL, returns true once the file has been downloaded completely. Incomplete downloads are resumed.
322             Follows C<10> redirects by default and does not limit the size of the response, which will be streamed memory
323             efficiently. Note that this method is B and might change without warning!
324              
325             =head2 extname
326              
327             my $ext = $path->extname;
328              
329             Return file extension of the path.
330              
331             # "js"
332             path('/home/sri/test.js')->extname;
333              
334             =head2 is_abs
335              
336             my $bool = $path->is_abs;
337              
338             Check if the path is absolute.
339              
340             # True (on UNIX)
341             path('/home/sri/.vimrc')->is_abs;
342              
343             # False (on UNIX)
344             path('.vimrc')->is_abs;
345              
346             =head2 list
347              
348             my $collection = $path->list;
349             my $collection = $path->list({hidden => 1});
350              
351             List all files in the directory and return a L object containing the results as L
352             objects. The list does not include C<.> and C<..>.
353              
354             # List files
355             say for path('/home/sri/myapp')->list->each;
356              
357             These options are currently available:
358              
359             =over 2
360              
361             =item dir
362              
363             dir => 1
364              
365             Include directories.
366              
367             =item hidden
368              
369             hidden => 1
370              
371             Include hidden files.
372              
373             =back
374              
375             =head2 list_tree
376              
377             my $collection = $path->list_tree;
378             my $collection = $path->list_tree({hidden => 1});
379              
380             List all files recursively in the directory and return a L object containing the results as
381             L objects. The list does not include C<.> and C<..>.
382              
383             # List all templates
384             say for path('/home/sri/myapp/templates')->list_tree->each;
385              
386             These options are currently available:
387              
388             =over 2
389              
390             =item dir
391              
392             dir => 1
393              
394             Include directories.
395              
396             =item hidden
397              
398             hidden => 1
399              
400             Include hidden files and directories.
401              
402             =item max_depth
403              
404             max_depth => 3
405              
406             Maximum number of levels to descend when searching for files.
407              
408             =back
409              
410             =head2 lstat
411              
412             my $stat = $path->lstat;
413              
414             Return a L object for the symlink.
415              
416             # Get symlink size
417             say path('/usr/sbin/sendmail')->lstat->size;
418              
419             # Get symlink modification time
420             say path('/usr/sbin/sendmail')->lstat->mtime;
421              
422             =head2 make_path
423              
424             $path = $path->make_path;
425             $path = $path->make_path({mode => 0711});
426              
427             Create the directories if they don't already exist, any additional arguments are passed through to L.
428              
429             =head2 move_to
430              
431             my $destination = $path->move_to('/home/sri');
432             my $destination = $path->move_to('/home/sri/.vimrc.backup');
433              
434             Move file with L and return the destination as a L object.
435              
436             =head2 new
437              
438             my $path = Mojo::File->new;
439             my $path = Mojo::File->new('/home/sri/.vimrc');
440             my $path = Mojo::File->new('/home', 'sri', '.vimrc');
441             my $path = Mojo::File->new(File::Temp->new);
442             my $path = Mojo::File->new(File::Temp->newdir);
443              
444             Construct a new L object, defaults to using the current working directory.
445              
446             # "foo/bar/baz.txt" (on UNIX)
447             Mojo::File->new('foo', 'bar', 'baz.txt');
448              
449             =head2 open
450              
451             my $handle = $path->open('+<');
452             my $handle = $path->open('r+');
453             my $handle = $path->open(O_RDWR);
454             my $handle = $path->open('<:encoding(UTF-8)');
455              
456             Open file with L.
457              
458             # Combine "fcntl.h" constants
459             use Fcntl qw(O_CREAT O_EXCL O_RDWR);
460             my $handle = path('/home/sri/test.pl')->open(O_RDWR | O_CREAT | O_EXCL);
461              
462             =head2 realpath
463              
464             my $realpath = $path->realpath;
465              
466             Resolve the path with L and return the result as a L object.
467              
468             =head2 remove
469              
470             $path = $path->remove;
471              
472             Delete file.
473              
474             =head2 remove_tree
475              
476             $path = $path->remove_tree;
477             $path = $path->remove_tree({keep_root => 1});
478              
479             Delete this directory and any files and subdirectories it may contain, any additional arguments are passed through to
480             L.
481              
482             =head2 sibling
483              
484             my $sibling = $path->sibling('.vimrc');
485              
486             Return a new L object relative to the directory part of the path.
487              
488             # "/home/sri/.vimrc" (on UNIX)
489             path('/home/sri/.bashrc')->sibling('.vimrc');
490              
491             # "/home/sri/.ssh/known_hosts" (on UNIX)
492             path('/home/sri/.bashrc')->sibling('.ssh', 'known_hosts');
493              
494             =head2 slurp
495              
496             my $bytes = $path->slurp;
497             my $chars = $path->slurp('UTF-8');
498              
499             Read all data at once from the file. If an encoding is provided, an attempt will be made to decode the content.
500              
501             =head2 spew
502              
503             $path = $path->spew($bytes);
504             $path = $path->spew($chars, 'UTF-8');
505              
506             Write all data at once to the file. If an encoding is provided, an attempt to encode the content will be made prior to
507             writing.
508              
509             =head2 spurt
510              
511             $path = $path->spurt(@bytes);
512              
513             Alias for L that writes multiple chunks of bytes.
514              
515             =head2 stat
516              
517             my $stat = $path->stat;
518              
519             Return a L object for the path.
520              
521             # Get file size
522             say path('/home/sri/.bashrc')->stat->size;
523              
524             # Get file modification time
525             say path('/home/sri/.bashrc')->stat->mtime;
526              
527             =head2 tap
528              
529             $path = $path->tap(sub {...});
530              
531             Alias for L.
532              
533             =head2 to_abs
534              
535             my $absolute = $path->to_abs;
536              
537             Return absolute path as a L object, the path does not need to exist on the file system.
538              
539             =head2 to_array
540              
541             my $parts = $path->to_array;
542              
543             Split the path on directory separators.
544              
545             # "home:sri:.vimrc" (on UNIX)
546             join ':', @{path('/home/sri/.vimrc')->to_array};
547              
548             =head2 to_rel
549              
550             my $relative = $path->to_rel('/some/base/path');
551              
552             Return a relative path from the original path to the destination path as a L object.
553              
554             # "sri/.vimrc" (on UNIX)
555             path('/home/sri/.vimrc')->to_rel('/home');
556              
557             =head2 to_string
558              
559             my $str = $path->to_string;
560              
561             Stringify the path.
562              
563             =head2 touch
564              
565             $path = $path->touch;
566              
567             Create file if it does not exist or change the modification and access time to the current time.
568              
569             # Safely read file
570             say path('.bashrc')->touch->slurp;
571              
572             =head2 with_roles
573              
574             my $new_class = Mojo::File->with_roles('Mojo::File::Role::One');
575             my $new_class = Mojo::File->with_roles('+One', '+Two');
576             $path = $path->with_roles('+One', '+Two');
577              
578             Alias for L.
579              
580             =head1 OPERATORS
581              
582             L overloads the following operators.
583              
584             =head2 array
585              
586             my @parts = @$path;
587              
588             Alias for L.
589              
590             =head2 bool
591              
592             my $bool = !!$path;
593              
594             Always true.
595              
596             =head2 stringify
597              
598             my $str = "$path";
599              
600             Alias for L.
601              
602             =head1 SEE ALSO
603              
604             L, L, L.
605              
606             =cut