Path::Tiny

TL;DR

Path::Tiny is very useful.

When I started learning Perl a bit more seriously in 2005, it was clear that copy-and-paste wouldn’t work. This is true of all languages, of course, but there were so many things that you might copy and paste (e.g. from books) that up to that time I always got my problems solved with it.

Path handling

One of the things that had to change, of course, was path handling. If you have a directory name in variable $dir, and you want file ever.txt inside its sub-directory what, it’s easy to do this:

my $filepath = $dir . '/what/ever.txt';

Except, for example, that it’s not really general, e.g. in Windows.

So, I learned that the canonical way to do path handling in Perl was using File::Spec (and, in case, File::Spec::Functions):

use File::Spec::Functions qw< splitpath splitdir catdir catpath >;
my ($volume, $directories) = splitpath($dir, 'this-is-a-directory');
my @dirs = splitdir($directories);
$directories = catdir(@dirs, 'what');
my $filepath = catpath($volume, $directories, 'whatever.txt');

This is very general - works on all systems - but wow it’s hard! Directories and files take two separate routes (*dir vs *path functions) and you have to track them all. Without considering that you have to carry around a lot of variables for all the different parts.

Path::Tiny makes path handling so much easier:

use Path::Tiny; # imports `path` by default
my $filepath = path($dir)->child('what', 'ever.txt')->stringify;

Much more readable!

Also, the last call to stringify is most of the times not needed - whenever a string is needed, a Path::Tiny object transforms itself into one using that method automatically. Neat!

Of course, other methods are very readable:

my $filepath    = path($dir)->child('what', 'ever.txt');
my $basename    = $filepath->basename; # this is a string
my $parent_dir  = $filepath->parent;
my $dirname     = $filepath->parent->stringify; # ...
my $absolute    = $filepath->absolute;
my $relative    = $filepath->relative('/tmp');
my @all_in_dir  = $parent->children;
my $in_same_dir = $filepath->sibling('another.txt');
my $resolved    = $filepath->realpath;

The last call is equivalent to Cwd’s realpath, which does a check on the filesystem - so make sure the file exists!

Reading and writing

Another thing that you might want to do is reading and writing files.

Many times you want to iterate line by line. Well, in this case use the good ol’ system 😄:

use autodie;
open my $fh, '<', $filepath;
while (<$fh>) { ... }
close $fh;

If, on the other hand, you want to get the whole file, you might be tempted to use File::Slurp, right? Path::Tiny has you covered:

my $whole_file = $filepath->slurp;

Make sure to take a look at slurp_raw and slurp_utf8!

On the other hand, what if you want to save data in a file? Again… Path::Tiny has you covered:

$filepath->spew($contents_to_be_saved);

Again… look also at spew_raw and spew_utf8, as well as append, append_raw, and append_utf8!

Temporary stuff

Sometimes you need a temporary file. Sometimes a temporary directory. Those sometimes, you can use File::Temp. Or…

my $tempfile = Path::Tiny->tempfile;
my $tempdir  = Path::Tiny->tempdir;

They have options… but you get the idea. What’s interesting is that these options have been put to sensible defaults, like e.g. getting rid of temporary files when they are not needed any more. Whatever!

Going on

We’ve come about halfway through the module… there are other functions, like for creating directories, getting rid of stuff, manipulating permissions, copying stuff… most things you want to do with files.

If I had to find the one thing I would change is function copy. This is how you call it:

$foo->copy($bar);

Wait… is $foo copied to $bar, or is the other way around? I think that this would be much better:

$foo->copy_to($bar);

Enough nitpicking anyway… go read the documentation!


Comments? Octodon, , GitHub, Reddit, or drop me a line!