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     1296   2333253 use Mojo::Base -strict;
  83         169  
  83         2157  
3 83     83   47245 use overload '@{}' => sub { shift->to_array }, bool => sub {1}, '""' => sub { ${$_[0]} }, fallback => 1;
  83     3352   140201  
  83     1988   1094  
  1857     43   4844  
  98         718  
  5319         73991  
  5319         158938  
4              
5 83     83   9913 use Carp qw(croak);
  83         200  
  83         5706  
6 83     83   503 use Cwd qw(getcwd);
  83         155  
  83         4902  
7 83     83   507 use Exporter qw(import);
  83         171  
  83         2891  
8 83     83   648 use File::Basename ();
  83         312  
  83         2653  
9 83     83   49324 use File::Copy qw(copy move);
  83         357148  
  83         6276  
10 83     83   648 use File::Path ();
  83         181  
  83         2756  
11 83     83   38592 use File::Spec::Functions qw(abs2rel canonpath catfile file_name_is_absolute rel2abs splitdir);
  83         74424  
  83         9234  
12 83     83   49518 use File::stat ();
  83         670348  
  83         2649  
13 83     83   93937 use File::Temp ();
  83         1498928  
  83         3182  
14 83     83   37161 use IO::File ();
  83         79407  
  83         3029  
15 83     83   47883 use Mojo::Collection;
  83         347  
  83         5509  
16 83     83   679 use Mojo::Util qw(decode encode);
  83         265  
  83         326265  
17              
18             our @EXPORT_OK = ('curfile', 'path', 'tempdir', 'tempfile');
19              
20 174     174 1 932 sub basename { File::Basename::basename ${shift()}, @_ }
  174         12899  
21              
22 498     498 1 52103 sub child { $_[0]->new(${shift()}, @_) }
  498         2171  
23              
24             sub chmod {
25 4     4 1 14 my ($self, $mode) = @_;
26 4 100       312 chmod $mode, $$self or croak qq{Can't chmod file "$$self": $!};
27 3         15 return $self;
28             }
29              
30             sub copy_to {
31 2     2 1 11 my ($self, $to) = @_;
32 2 50       11 copy($$self, $to) or croak qq{Can't copy file "$$self" to "$to": $!};
33 2 100       545 return $self->new(-d $to ? ($to, File::Basename::basename $self) : $to);
34             }
35              
36 288     288 1 440643 sub curfile { __PACKAGE__->new(Cwd::realpath((caller)[1])) }
37              
38 97     97 1 765 sub dirname { $_[0]->new(scalar File::Basename::dirname ${$_[0]}) }
  97         5013  
39              
40             sub download {
41 13   100 13 1 154 my ($self, $url, $options) = (shift, shift, shift // {});
42             my $ua = $options->{ua}
43 13   66     52 || do { require Mojo::UserAgent; Mojo::UserAgent->new(max_redirects => 10, max_response_size => 0) };
44 13   100     44 my $tx = _download_error($ua->transactor->download($ua->head($url => $options->{headers} // {}), $$self));
45 10 100       79 return $tx ? !!_download_error($ua->start($tx)) : 1;
46             }
47              
48 96 100   96 1 447 sub extname { shift->basename =~ /.+\.([^.]+)$/ ? $1 : '' }
49              
50 49     49 1 96 sub is_abs { file_name_is_absolute ${shift()} }
  49         385  
51              
52             sub list {
53 31   100 31 1 182 my ($self, $options) = (shift, shift // {});
54              
55 31 100       888 return Mojo::Collection->new unless -d $$self;
56 29 50       1454 opendir(my $dir, $$self) or croak qq{Can't open directory "$$self": $!};
57 29 100       7192 my @files = grep { $_ ne '.' && $_ ne '..' } readdir $dir;
  250         896  
58 29 100       153 @files = grep { !/^\./ } @files unless $options->{hidden};
  172         380  
59 29         65 @files = map { catfile $$self, $_ } @files;
  188         791  
60 29 100       146 @files = grep { !-d } @files unless $options->{dir};
  170         9171  
61              
62 29         167 return Mojo::Collection->new(map { $self->new($_) } sort @files);
  157         346  
63             }
64              
65             sub list_tree {
66 236   100 236 1 1642 my ($self, $options) = (shift, shift // {});
67 236 100       4959 return Mojo::Collection->new unless -d $$self;
68              
69 227         627 my (@results, $walk);
70             $walk = sub {
71 740     740   1963 my ($path, $depth) = @_;
72 740 50       32438 opendir my $dh, $path or return;
73 740 100       21070 my @names = sort grep { $_ ne '.' && $_ ne '..' } readdir $dh;
  4137         15927  
74 740 100       3061 @names = grep { !/^\./ } @names unless $options->{hidden};
  2583         5938  
75 740         1595 for my $name (@names) {
76 2649         6761 my $child = $self->new($path, $name);
77 2649         48642 my $is_dir = -d $$child;
78 2649 100 100     12771 push @results, $child if $options->{dir} || !$is_dir;
79 2649 100 100     19501 $walk->($$child, $depth + 1) if $is_dir && (!$options->{max_depth} || $depth + 1 < $options->{max_depth});
      100        
80             }
81 227         2225 };
82 227         914 $walk->($$self, 0);
83              
84 227         2616 return Mojo::Collection->new(@results);
85             }
86              
87 1     1 1 2315 sub lstat { File::stat::lstat(${shift()}) }
  1         22  
88              
89             sub make_path {
90 23     23 1 45 my $self = shift;
91 23         3242 File::Path::make_path $$self, @_;
92 23         146 return $self;
93             }
94              
95             sub move_to {
96 10     10 1 53 my ($self, $to) = @_;
97 10 50       77 move($$self, $to) or croak qq{Can't move file "$$self" to "$to": $!};
98 10 100       995 return $self->new(-d $to ? ($to, File::Basename::basename $self) : $to);
99             }
100              
101             sub new {
102 11157     11157 1 386029 my $class = shift;
103 11157 100       23078 croak 'Invalid path' if grep { !defined } @_;
  19676         70937  
104 11155 100       60634 my $value = @_ == 1 ? $_[0] : @_ > 1 ? catfile @_ : canonpath getcwd;
    100          
105 11155   66     68849 return bless \$value, ref $class || $class;
106             }
107              
108             sub open {
109 141     141 1 5176 my $self = shift;
110 141         1394 my $handle = IO::File->new;
111 141 100       7044 $handle->open($$self, @_) or croak qq{Can't open file "$$self": $!};
112 140         17104 return $handle;
113             }
114              
115 3349     3349 1 487496 sub path { __PACKAGE__->new(@_) }
  43         2610  
116              
117 919     919 1 7750 sub realpath { $_[0]->new(Cwd::realpath ${$_[0]}) }
  919         89164  
118              
119             sub remove {
120 52     52 1 169 my ($self, $mode) = @_;
121 52 100 66     13268 unlink $$self or croak qq{Can't remove file "$$self": $!} if -e $$self;
122 51         1472 return $self;
123             }
124              
125             sub remove_tree {
126 2     2 1 5 my $self = shift;
127 2         1487 File::Path::remove_tree $$self, @_;
128 2         18 return $self;
129             }
130              
131             sub sibling {
132 283     283 1 615 my $self = shift;
133 283         7052 return $self->new(scalar File::Basename::dirname($self), @_);
134             }
135              
136             sub slurp {
137 130     130 1 4456 my ($self, $encoding) = @_;
138              
139 130 100       7522 CORE::open my $file, '<', $$self or croak qq{Can't open file "$$self": $!};
140 129         782 my $ret = my $content = '';
141 129         1489 while ($ret = $file->sysread(my $buffer, 131072, 0)) { $content .= $buffer }
  128         5505  
142 129 50       1712 croak qq{Can't read from file "$$self": $!} unless defined $ret;
143              
144 129 100       2558 return $encoding ? decode($encoding, $content) : $content;
145             }
146              
147             sub spew {
148 55     55 1 277 my ($self, $content, $encoding) = @_;
149 55 100       219 $content = encode($encoding, $content) if $encoding;
150 55 100       4908 CORE::open my $file, '>', $$self or croak qq{Can't open file "$$self": $!};
151 54 100 50     1227 ($file->syswrite($content) // -1) == length $content or croak qq{Can't write to file "$$self": $!};
152 52         4638 return $self;
153             }
154              
155 1     1 1 8 sub spurt { shift->spew(join '', @_) }
156              
157 4     4 1 25 sub stat { File::stat::stat(${shift()}) }
  4         26  
158              
159 1     1 1 8 sub tap { shift->Mojo::Base::tap(@_) }
160              
161 38     38 1 364895 sub tempdir { __PACKAGE__->new(File::Temp->newdir(@_)) }
162              
163 53     53 1 13175 sub tempfile { __PACKAGE__->new(File::Temp->new(@_)) }
164              
165 142     142 1 404 sub to_abs { $_[0]->new(rel2abs ${$_[0]}) }
  142         793  
166              
167 2292     2292 1 3686 sub to_array { [splitdir ${shift()}] }
  2292         6779  
168              
169 2165     2165 1 3730 sub to_rel { $_[0]->new(abs2rel(${$_[0]}, $_[1])) }
  2165         6981  
170              
171 3341     3341 1 8462 sub to_string {"${$_[0]}"}
  3341         54850  
172              
173             sub touch {
174 4     4 1 1194 my $self = shift;
175 4 100       123 $self->open('>') unless -e $$self;
176 4 50       120 utime undef, undef, $$self or croak qq{Can't touch file "$$self": $!};
177 4         24 return $self;
178             }
179              
180 1     1 1 723 sub with_roles { shift->Mojo::Base::with_roles(@_) }
181              
182             sub _download_error {
183 22     22   38 my $tx = shift;
184              
185 22 100       69 return $tx unless my $err = $tx->error;
186 6 100 100     52 return undef if $err->{message} eq 'Download complete' || $err->{message} eq 'Download incomplete';
187 3 100       235 croak "$err->{code} response: $err->{message}" if $err->{code};
188 2         475 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