1 | package Mojo::File;
|
---|
2 | use Mojo::Base -strict;
|
---|
3 | use overload
|
---|
4 | '@{}' => sub { shift->to_array },
|
---|
5 | bool => sub {1},
|
---|
6 | '""' => sub { ${$_[0]} },
|
---|
7 | fallback => 1;
|
---|
8 |
|
---|
9 | use Carp 'croak';
|
---|
10 | use Cwd 'getcwd';
|
---|
11 | use Exporter 'import';
|
---|
12 | use File::Basename ();
|
---|
13 | use File::Copy qw(copy move);
|
---|
14 | use File::Find 'find';
|
---|
15 | use File::Path ();
|
---|
16 | use File::Spec::Functions
|
---|
17 | qw(abs2rel canonpath catfile file_name_is_absolute rel2abs splitdir);
|
---|
18 | use File::Temp ();
|
---|
19 | use IO::File ();
|
---|
20 | use Mojo::Collection;
|
---|
21 |
|
---|
22 | our @EXPORT_OK = ('path', 'tempdir', 'tempfile');
|
---|
23 |
|
---|
24 | sub basename { File::Basename::basename ${shift()}, @_ }
|
---|
25 |
|
---|
26 | sub child { $_[0]->new(@_) }
|
---|
27 |
|
---|
28 | sub copy_to {
|
---|
29 | my ($self, $to) = @_;
|
---|
30 | copy($$self, $to) or croak qq{Can't copy file "$$self" to "$to": $!};
|
---|
31 | return $self->new(-d $to ? ($to, File::Basename::basename $self) : $to);
|
---|
32 | }
|
---|
33 |
|
---|
34 | sub dirname { $_[0]->new(scalar File::Basename::dirname ${$_[0]}) }
|
---|
35 |
|
---|
36 | sub is_abs { file_name_is_absolute ${shift()} }
|
---|
37 |
|
---|
38 | sub list {
|
---|
39 | my ($self, $options) = (shift, shift // {});
|
---|
40 |
|
---|
41 | return Mojo::Collection->new unless -d $$self;
|
---|
42 | opendir(my $dir, $$self) or croak qq{Can't open directory "$$self": $!};
|
---|
43 | my @files = grep { $_ ne '.' && $_ ne '..' } readdir $dir;
|
---|
44 | @files = grep { !/^\./ } @files unless $options->{hidden};
|
---|
45 | @files = map { catfile $$self, $_ } @files;
|
---|
46 | @files = grep { !-d } @files unless $options->{dir};
|
---|
47 |
|
---|
48 | return Mojo::Collection->new(map { $self->new($_) } sort @files);
|
---|
49 | }
|
---|
50 |
|
---|
51 | sub list_tree {
|
---|
52 | my ($self, $options) = (shift, shift // {});
|
---|
53 |
|
---|
54 | # This may break in the future, but is worth it for performance
|
---|
55 | local $File::Find::skip_pattern = qr/^\./ unless $options->{hidden};
|
---|
56 |
|
---|
57 | # The File::Find documentation lies, this is needed for CIFS
|
---|
58 | local $File::Find::dont_use_nlink = 1 if $options->{dont_use_nlink};
|
---|
59 |
|
---|
60 | my %all;
|
---|
61 | my $wanted = {wanted => sub { $all{$File::Find::name}++ }, no_chdir => 1};
|
---|
62 | $wanted->{postprocess} = sub { delete $all{$File::Find::dir} }
|
---|
63 | unless $options->{dir};
|
---|
64 | find $wanted, $$self if -d $$self;
|
---|
65 | delete $all{$$self};
|
---|
66 |
|
---|
67 | return Mojo::Collection->new(map { $self->new(canonpath $_) } sort keys %all);
|
---|
68 | }
|
---|
69 |
|
---|
70 | sub make_path {
|
---|
71 | my $self = shift;
|
---|
72 | File::Path::make_path $$self, @_;
|
---|
73 | return $self;
|
---|
74 | }
|
---|
75 |
|
---|
76 | sub move_to {
|
---|
77 | my ($self, $to) = @_;
|
---|
78 | move($$self, $to) or croak qq{Can't move file "$$self" to "$to": $!};
|
---|
79 | return $self->new(-d $to ? ($to, File::Basename::basename $self) : $to);
|
---|
80 | }
|
---|
81 |
|
---|
82 | sub new {
|
---|
83 | my $class = shift;
|
---|
84 | my $value = @_ == 1 ? $_[0] : @_ > 1 ? catfile @_ : canonpath getcwd;
|
---|
85 | return bless \$value, ref $class || $class;
|
---|
86 | }
|
---|
87 |
|
---|
88 | sub open {
|
---|
89 | my $self = shift;
|
---|
90 | my $handle = IO::File->new;
|
---|
91 | $handle->open($$self, @_) or croak qq{Can't open file "$$self": $!};
|
---|
92 | return $handle;
|
---|
93 | }
|
---|
94 |
|
---|
95 | sub path { __PACKAGE__->new(@_) }
|
---|
96 |
|
---|
97 | sub realpath { $_[0]->new(Cwd::realpath ${$_[0]}) }
|
---|
98 |
|
---|
99 | sub remove_tree {
|
---|
100 | my $self = shift;
|
---|
101 | File::Path::remove_tree $$self, @_;
|
---|
102 | return $self;
|
---|
103 | }
|
---|
104 |
|
---|
105 | sub sibling {
|
---|
106 | my $self = shift;
|
---|
107 | return $self->new(scalar File::Basename::dirname($self), @_);
|
---|
108 | }
|
---|
109 |
|
---|
110 | sub slurp {
|
---|
111 | my $self = shift;
|
---|
112 |
|
---|
113 | CORE::open my $file, '<', $$self or croak qq{Can't open file "$$self": $!};
|
---|
114 | my $ret = my $content = '';
|
---|
115 | while ($ret = $file->sysread(my $buffer, 131072, 0)) { $content .= $buffer }
|
---|
116 | croak qq{Can't read from file "$$self": $!} unless defined $ret;
|
---|
117 |
|
---|
118 | return $content;
|
---|
119 | }
|
---|
120 |
|
---|
121 | sub spurt {
|
---|
122 | my ($self, $content) = (shift, join '', @_);
|
---|
123 | CORE::open my $file, '>', $$self or croak qq{Can't open file "$$self": $!};
|
---|
124 | ($file->syswrite($content) // -1) == length $content
|
---|
125 | or croak qq{Can't write to file "$$self": $!};
|
---|
126 | return $self;
|
---|
127 | }
|
---|
128 |
|
---|
129 | sub tap { shift->Mojo::Base::tap(@_) }
|
---|
130 |
|
---|
131 | sub tempdir { __PACKAGE__->new(File::Temp->newdir(@_)) }
|
---|
132 |
|
---|
133 | sub tempfile { __PACKAGE__->new(File::Temp->new(@_)) }
|
---|
134 |
|
---|
135 | sub to_abs { $_[0]->new(rel2abs ${$_[0]}) }
|
---|
136 |
|
---|
137 | sub to_array { [splitdir ${shift()}] }
|
---|
138 |
|
---|
139 | sub to_rel { $_[0]->new(abs2rel(${$_[0]}, $_[1])) }
|
---|
140 |
|
---|
141 | sub to_string {"${$_[0]}"}
|
---|
142 |
|
---|
143 | sub with_roles { shift->Mojo::Base::with_roles(@_) }
|
---|
144 |
|
---|
145 | 1;
|
---|
146 |
|
---|
147 | =encoding utf8
|
---|
148 |
|
---|
149 | =head1 NAME
|
---|
150 |
|
---|
151 | Mojo::File - File system paths
|
---|
152 |
|
---|
153 | =head1 SYNOPSIS
|
---|
154 |
|
---|
155 | use Mojo::File;
|
---|
156 |
|
---|
157 | # Portably deal with file system paths
|
---|
158 | my $path = Mojo::File->new('/home/sri/.vimrc');
|
---|
159 | say $path->slurp;
|
---|
160 | say $path->dirname;
|
---|
161 | say $path->basename;
|
---|
162 | say $path->sibling('.bashrc');
|
---|
163 |
|
---|
164 | # Use the alternative constructor
|
---|
165 | use Mojo::File 'path';
|
---|
166 | my $path = path('/tmp/foo/bar')->make_path;
|
---|
167 | $path->child('test.txt')->spurt('Hello Mojo!');
|
---|
168 |
|
---|
169 | =head1 DESCRIPTION
|
---|
170 |
|
---|
171 | L<Mojo::File> is a scalar-based container for file system paths that provides a
|
---|
172 | friendly API for dealing with different operating systems.
|
---|
173 |
|
---|
174 | # Access scalar directly to manipulate path
|
---|
175 | my $path = Mojo::File->new('/home/sri/test');
|
---|
176 | $$path .= '.txt';
|
---|
177 |
|
---|
178 | =head1 FUNCTIONS
|
---|
179 |
|
---|
180 | L<Mojo::File> implements the following functions, which can be imported
|
---|
181 | individually.
|
---|
182 |
|
---|
183 | =head2 path
|
---|
184 |
|
---|
185 | my $path = path;
|
---|
186 | my $path = path('/home/sri/.vimrc');
|
---|
187 | my $path = path('/home', 'sri', '.vimrc');
|
---|
188 | my $path = path(File::Temp->newdir);
|
---|
189 |
|
---|
190 | Construct a new scalar-based L<Mojo::File> object, defaults to using the current
|
---|
191 | working directory.
|
---|
192 |
|
---|
193 | # "foo/bar/baz.txt" (on UNIX)
|
---|
194 | path('foo', 'bar', 'baz.txt');
|
---|
195 |
|
---|
196 | =head2 tempdir
|
---|
197 |
|
---|
198 | my $path = tempdir;
|
---|
199 | my $path = tempdir('tempXXXXX');
|
---|
200 |
|
---|
201 | Construct a new scalar-based L<Mojo::File> object for a temporary directory with
|
---|
202 | L<File::Temp>.
|
---|
203 |
|
---|
204 | # Longer version
|
---|
205 | my $path = path(File::Temp->newdir('tempXXXXX'));
|
---|
206 |
|
---|
207 | =head2 tempfile
|
---|
208 |
|
---|
209 | my $path = tempfile;
|
---|
210 | my $path = tempfile(DIR => '/tmp');
|
---|
211 |
|
---|
212 | Construct a new scalar-based L<Mojo::File> object for a temporary file with
|
---|
213 | L<File::Temp>.
|
---|
214 |
|
---|
215 | # Longer version
|
---|
216 | my $path = path(File::Temp->new(DIR => '/tmp'));
|
---|
217 |
|
---|
218 | =head1 METHODS
|
---|
219 |
|
---|
220 | L<Mojo::File> implements the following methods.
|
---|
221 |
|
---|
222 | =head2 basename
|
---|
223 |
|
---|
224 | my $name = $path->basename;
|
---|
225 | my $name = $path->basename('.txt');
|
---|
226 |
|
---|
227 | Return the last level of the path with L<File::Basename>.
|
---|
228 |
|
---|
229 | # ".vimrc" (on UNIX)
|
---|
230 | path('/home/sri/.vimrc')->basename;
|
---|
231 |
|
---|
232 | # "test" (on UNIX)
|
---|
233 | path('/home/sri/test.txt')->basename('.txt');
|
---|
234 |
|
---|
235 | =head2 child
|
---|
236 |
|
---|
237 | my $child = $path->child('.vimrc');
|
---|
238 |
|
---|
239 | Return a new L<Mojo::File> object relative to the path.
|
---|
240 |
|
---|
241 | # "/home/sri/.vimrc" (on UNIX)
|
---|
242 | path('/home')->child('sri', '.vimrc');
|
---|
243 |
|
---|
244 | =head2 copy_to
|
---|
245 |
|
---|
246 | my $destination = $path->copy_to('/home/sri');
|
---|
247 | my $destination = $path->copy_to('/home/sri/.vimrc.backup');
|
---|
248 |
|
---|
249 | Copy file with L<File::Copy> and return the destination as a L<Mojo::File>
|
---|
250 | object.
|
---|
251 |
|
---|
252 | =head2 dirname
|
---|
253 |
|
---|
254 | my $name = $path->dirname;
|
---|
255 |
|
---|
256 | Return all but the last level of the path with L<File::Basename> as a
|
---|
257 | L<Mojo::File> object.
|
---|
258 |
|
---|
259 | # "/home/sri" (on UNIX)
|
---|
260 | path('/home/sri/.vimrc')->dirname;
|
---|
261 |
|
---|
262 | =head2 is_abs
|
---|
263 |
|
---|
264 | my $bool = $path->is_abs;
|
---|
265 |
|
---|
266 | Check if the path is absolute.
|
---|
267 |
|
---|
268 | # True (on UNIX)
|
---|
269 | path('/home/sri/.vimrc')->is_abs;
|
---|
270 |
|
---|
271 | # False (on UNIX)
|
---|
272 | path('.vimrc')->is_abs;
|
---|
273 |
|
---|
274 | =head2 list
|
---|
275 |
|
---|
276 | my $collection = $path->list;
|
---|
277 | my $collection = $path->list({hidden => 1});
|
---|
278 |
|
---|
279 | List all files in the directory and return a L<Mojo::Collection> object
|
---|
280 | containing the results as L<Mojo::File> objects. The list does not include C<.>
|
---|
281 | and C<..>.
|
---|
282 |
|
---|
283 | # List files
|
---|
284 | say for path('/home/sri/myapp')->list->each;
|
---|
285 |
|
---|
286 | These options are currently available:
|
---|
287 |
|
---|
288 | =over 2
|
---|
289 |
|
---|
290 | =item dir
|
---|
291 |
|
---|
292 | dir => 1
|
---|
293 |
|
---|
294 | Include directories.
|
---|
295 |
|
---|
296 | =item hidden
|
---|
297 |
|
---|
298 | hidden => 1
|
---|
299 |
|
---|
300 | Include hidden files.
|
---|
301 |
|
---|
302 | =back
|
---|
303 |
|
---|
304 | =head2 list_tree
|
---|
305 |
|
---|
306 | my $collection = $path->list_tree;
|
---|
307 | my $collection = $path->list_tree({hidden => 1});
|
---|
308 |
|
---|
309 | List all files recursively in the directory and return a L<Mojo::Collection>
|
---|
310 | object containing the results as L<Mojo::File> objects. The list does not
|
---|
311 | include C<.> and C<..>.
|
---|
312 |
|
---|
313 | # List all templates
|
---|
314 | say for path('/home/sri/myapp/templates')->list_tree->each;
|
---|
315 |
|
---|
316 | These options are currently available:
|
---|
317 |
|
---|
318 | =over 2
|
---|
319 |
|
---|
320 | =item dir
|
---|
321 |
|
---|
322 | dir => 1
|
---|
323 |
|
---|
324 | Include directories.
|
---|
325 |
|
---|
326 | =item dont_use_nlink
|
---|
327 |
|
---|
328 | dont_use_nlink => 1
|
---|
329 |
|
---|
330 | Force L<File::Find> to always stat directories.
|
---|
331 |
|
---|
332 | =item hidden
|
---|
333 |
|
---|
334 | hidden => 1
|
---|
335 |
|
---|
336 | Include hidden files and directories.
|
---|
337 |
|
---|
338 | =back
|
---|
339 |
|
---|
340 | =head2 make_path
|
---|
341 |
|
---|
342 | $path = $path->make_path;
|
---|
343 | $path = $path->make_path({mode => 0711});
|
---|
344 |
|
---|
345 | Create the directories if they don't already exist, any additional arguments are
|
---|
346 | passed through to L<File::Path>.
|
---|
347 |
|
---|
348 | =head2 move_to
|
---|
349 |
|
---|
350 | my $destination = $path->move_to('/home/sri');
|
---|
351 | my $destination = $path->move_to('/home/sri/.vimrc.backup');
|
---|
352 |
|
---|
353 | Move file with L<File::Copy> and return the destination as a L<Mojo::File>
|
---|
354 | object.
|
---|
355 |
|
---|
356 | =head2 new
|
---|
357 |
|
---|
358 | my $path = Mojo::File->new;
|
---|
359 | my $path = Mojo::File->new('/home/sri/.vimrc');
|
---|
360 | my $path = Mojo::File->new('/home', 'sri', '.vimrc');
|
---|
361 | my $path = Mojo::File->new(File::Temp->new);
|
---|
362 | my $path = Mojo::File->new(File::Temp->newdir);
|
---|
363 |
|
---|
364 | Construct a new L<Mojo::File> object, defaults to using the current working
|
---|
365 | directory.
|
---|
366 |
|
---|
367 | # "foo/bar/baz.txt" (on UNIX)
|
---|
368 | Mojo::File->new('foo', 'bar', 'baz.txt');
|
---|
369 |
|
---|
370 | =head2 open
|
---|
371 |
|
---|
372 | my $handle = $path->open('+<');
|
---|
373 | my $handle = $path->open('r+');
|
---|
374 | my $handle = $path->open(O_RDWR);
|
---|
375 | my $handle = $path->open('<:encoding(UTF-8)');
|
---|
376 |
|
---|
377 | Open file with L<IO::File>.
|
---|
378 |
|
---|
379 | # Combine "fcntl.h" constants
|
---|
380 | use Fcntl qw(O_CREAT O_EXCL O_RDWR);
|
---|
381 | my $handle = path('/home/sri/test.pl')->open(O_RDWR | O_CREAT | O_EXCL);
|
---|
382 |
|
---|
383 | =head2 realpath
|
---|
384 |
|
---|
385 | my $realpath = $path->realpath;
|
---|
386 |
|
---|
387 | Resolve the path with L<Cwd> and return the result as a L<Mojo::File> object.
|
---|
388 |
|
---|
389 | =head2 remove_tree
|
---|
390 |
|
---|
391 | $path = $path->remove_tree;
|
---|
392 | $path = $path->remove_tree({keep_root => 1});
|
---|
393 |
|
---|
394 | Delete this directory and any files and subdirectories it may contain, any
|
---|
395 | additional arguments are passed through to L<File::Path>.
|
---|
396 |
|
---|
397 | =head2 sibling
|
---|
398 |
|
---|
399 | my $sibling = $path->sibling('.vimrc');
|
---|
400 |
|
---|
401 | Return a new L<Mojo::File> object relative to the directory part of the path.
|
---|
402 |
|
---|
403 | # "/home/sri/.vimrc" (on UNIX)
|
---|
404 | path('/home/sri/.bashrc')->sibling('.vimrc');
|
---|
405 |
|
---|
406 | # "/home/sri/.ssh/known_hosts" (on UNIX)
|
---|
407 | path('/home/sri/.bashrc')->sibling('.ssh', 'known_hosts');
|
---|
408 |
|
---|
409 | =head2 slurp
|
---|
410 |
|
---|
411 | my $bytes = $path->slurp;
|
---|
412 |
|
---|
413 | Read all data at once from the file.
|
---|
414 |
|
---|
415 | =head2 spurt
|
---|
416 |
|
---|
417 | $path = $path->spurt($bytes);
|
---|
418 | $path = $path->spurt(@chunks_of_bytes);
|
---|
419 |
|
---|
420 | Write all data at once to the file.
|
---|
421 |
|
---|
422 | =head2 tap
|
---|
423 |
|
---|
424 | $path = $path->tap(sub {...});
|
---|
425 |
|
---|
426 | Alias for L<Mojo::Base/"tap">.
|
---|
427 |
|
---|
428 | =head2 to_abs
|
---|
429 |
|
---|
430 | my $absolute = $path->to_abs;
|
---|
431 |
|
---|
432 | Return absolute path as a L<Mojo::File> object, the path does not need to exist
|
---|
433 | on the file system.
|
---|
434 |
|
---|
435 | =head2 to_array
|
---|
436 |
|
---|
437 | my $parts = $path->to_array;
|
---|
438 |
|
---|
439 | Split the path on directory separators.
|
---|
440 |
|
---|
441 | # "home:sri:.vimrc" (on UNIX)
|
---|
442 | join ':', @{path('/home/sri/.vimrc')->to_array};
|
---|
443 |
|
---|
444 | =head2 to_rel
|
---|
445 |
|
---|
446 | my $relative = $path->to_rel('/some/base/path');
|
---|
447 |
|
---|
448 | Return a relative path from the original path to the destination path as a
|
---|
449 | L<Mojo::File> object.
|
---|
450 |
|
---|
451 | # "sri/.vimrc" (on UNIX)
|
---|
452 | path('/home/sri/.vimrc')->to_rel('/home');
|
---|
453 |
|
---|
454 | =head2 to_string
|
---|
455 |
|
---|
456 | my $str = $path->to_string;
|
---|
457 |
|
---|
458 | Stringify the path.
|
---|
459 |
|
---|
460 | =head2 with_roles
|
---|
461 |
|
---|
462 | my $new_class = Mojo::File->with_roles('Mojo::File::Role::One');
|
---|
463 | my $new_class = Mojo::File->with_roles('+One', '+Two');
|
---|
464 | $path = $path->with_roles('+One', '+Two');
|
---|
465 |
|
---|
466 | Alias for L<Mojo::Base/"with_roles">.
|
---|
467 |
|
---|
468 | =head1 OPERATORS
|
---|
469 |
|
---|
470 | L<Mojo::File> overloads the following operators.
|
---|
471 |
|
---|
472 | =head2 array
|
---|
473 |
|
---|
474 | my @parts = @$path;
|
---|
475 |
|
---|
476 | Alias for L</"to_array">.
|
---|
477 |
|
---|
478 | =head2 bool
|
---|
479 |
|
---|
480 | my $bool = !!$path;
|
---|
481 |
|
---|
482 | Always true.
|
---|
483 |
|
---|
484 | =head2 stringify
|
---|
485 |
|
---|
486 | my $str = "$path";
|
---|
487 |
|
---|
488 | Alias for L</"to_string">.
|
---|
489 |
|
---|
490 | =head1 SEE ALSO
|
---|
491 |
|
---|
492 | L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
|
---|
493 |
|
---|
494 | =cut
|
---|