The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Path::Class::Tiny - a Path::Tiny wrapper for Path::Class compatibility

VERSION

This document describes version 0.06 of Path::Class::Tiny.

SYNOPSIS

use Path::Class::Tiny;

# creating Path::Class::Tiny objects
$dir1 = path("/tmp");
$dir2 = dir("/home");
$foo = path("foo.txt");
$foo = file("bar.txt");

$subdir = $dir->child("foo");
$bar = $subdir->child("bar.txt");

# stringifies as cleaned up path
$file = path("./foo.txt");
print $file; # "foo.txt"

# reading files
$guts = $file->slurp;
@lines = $file->slurp;

# writing files
$bar->spew( $data );
$bar->spew( @data );

# comparing files
if ( $foo->ef($bar) ) { ... }

# reading directories
for ( $dir->children ) { ... }

DESCRIPTION

What do you do if you started out (Perl) life using Path::Class, but then later on you switched to Path::Tiny? Well, one thing you could do is relearn a bunch of things and go change a lot of existing code. Or, another thing would be to use Path::Class::Tiny instead.

Path::Class::Tiny is a thin(ish) wrapper around Path::Tiny that (mostly) restores the Path::Class interface. Where the two don't conflict, you can do it either way. Where they do conflict, you use the Path::Class way. Except where Path::Class is totally weird, in which case you use the Path::Tiny way.

Some examples:

Creating file/dir/path objects

Path::Class likes you to make either a file object or a dir object. Path::Tiny says that's silly and you should just make a path object. Path::Class::Tiny says you can use any of the 3 words you like; all the objects will be the same underneath.

my $a = file('foo', 'bar');
my $b = dir('foo', 'bar');
my $c = path('foo', 'bar');
say "true" if $a eq $b;         # yep
say "true" if $b eq $c;         # also yep

Going up or down the tree

Again, both styles work.

my $d = dir("foo");
my $up = $d->dir;               # this works
$up = $d->parent;               # so does this
$up = $d->dir->parent;          # sure, why not?
my $down = $d->child('bar');    # Path::Tiny style
my $down = $d->subdir('bar');   # Path::Class style

Slurping files

This mostly works like Path::Class, in that the return value is context-sensitive, and options are sent as a hash and not as a hashref.

my $data = $file->slurp;                        # one big string
my @data = $file->slurp;                        # one element per line
my @data = $file->slurp(chomp => 1);            # chomp every line
my @data = $file->slurp(iomode => '<:crlf');    # Path::Class style; works
my @data = $file->slurp(binmode => ':crlf');    # vaguely Path::Tiny style; also works
my @data = $file->slurp({binmode => ':crlf'});  # this one doesn't work
my $data = $file->slurp(chomp => 1);            # neither does this one, because it's weird

DETAILS

This module is still undergoing active development. While the general UI is somewhat constrained by the design goals, specific choices may, and almost certainly will, change. I think this module can be useful to you, but for right now I would only use it for personal scripts.

A Path::Class::Tiny isa Path::Tiny, but not isa Path::Class::Entity. At least not currently.

Path::Class::Tiny is not entirely a drop-in replacement for Path::Class, and most likely never will be. In particular, I have no interest in implementing any of the "foreign" methods. However, it should work for most common cases, and, if it doesn't, patches are welcome.

Performance of Path::Class::Tiny should be comparable to Path::Tiny. Again, if it's not, please let me know.

PATH::CLASS STYLE METHODS

cleanup

Redirects to "canonpath" in Path::Tiny.

components

Basically just like components from Path::Class::Dir, which means that it accepts offset and length arguments (which Path::Class::File doesn't). Another nice difference: calling components from Path::Class::File in scalar context doesn't do anything useful, whereas Path::Class::Tiny always returns the number of components, which is (hopefully) what you expect.

The only real difference between Path::Class::Tiny's components and Path::Class::Dir's components is that you don't get the volume returned in the Path::Class::Dir version. In this version, the volume (if any) will just be part of the first component in the list.

dir_list

Just an alias for "components", so it also works on files (Path::Class::File doesn't have a dir_list method). This means the basename is always the last entity in the list, even for files. Basically this is just here for compatibility's sake, and you probably shouldn't use it for new code, because the name doesn't really sound like what it does.

next

Uses "iterator" in Path::Tiny (with its default value of no recursion) to implement the interface of next from Path::Class::Dir. The primary difference this engenders is that the Path::Class::Dir version will return . and .., whereas this version will not. I also don't guarantee this version is re-entrant.

rmtree

Just an alias to "remove_tree" in Path::Tiny.

subdir

Just an alias to "child" in Path::Tiny.

touch

Basically just calls "touch" in Path::Tiny, which is better than "touch" in Path::Class::File in a couple of ways:

  • It returns the path object, which is useful for chaining.

  • It takes an argument, so you can set the time to something other than "now."

However, Path::Class::Tiny::touch is even better than that! It adds another cool feature:

  • If the argument is an object, and that object has an epoch method, it will call it and pass the result on to Path::Tiny::touch.

The practical result is that your argument to touch can be an integer (number of epoch seconds), or any of the most popular datetime objects: DateTime, Time::Piece, Time::Moment, Date::Easy, and possibly others as well.

Potential Incompatibility: The only way these additional features could be incompatible with existing Path::Class code is if it were relying on the return value from touch, which in Path::Class is either the return from open or the return from utime (so theoretically it's true if the touch was successful and false otherwise). Path::Tiny (and thus Path::Class::Tiny) will instead throw an exception if the touch was unsuccessful and return the chained object.

copy_to

Just an alias to "copy" in Path::Tiny.

move_to

There are two big differences between "move" in Path::Tiny and "move_to" in Path::Class::File:

  • On failure, Path::Class::File::move_to returns undef, while Path::Tiny::move throws an exception.

  • On success, Path::Tiny::move just returns true. Path::Class::File::move_to, on the other hand, returns the path object for chaining, which has been modified to have the new name.

Path::Class::Tiny splits the difference by throwing an exception on error, and returning the modified $self on success. That means this method is a mutator! Consequently, use of this method means your objects are not immutable. No doubt many people will object to this behavior. However, after some internal debate, it was decided to retain this aspect of Path::Class's interface for the following reasons:

  • It keeps from breaking existing Path::Class code that you're trying to convert over to Path::Class::Tiny. While we do implement some breaking changes, most of them feel a lot less likely to be encountered in real code than this one.

  • The real-world thing that the object represents--that is, the file on disk--is itself being mutated. If the object is not changed to reflect the new reality, then any stray copies of it lying around now reference a file that doesn't exist. So it seems just about as likely to fix a problem as to cause one.

  • If you don't like the mutability, just call "move" instead.

PATH::TINY STYLE METHODS

Since a Path::Class::Tiny object isa Path::Tiny object, the vast majority of Path::Tiny methods just work the same way they always have. Notable methods (with exceptions or just clarifications) are listed below.

move

Unchanged from Path::Tiny, which means it's quite different from "move_to". In particular, move does not mutate the object, which means that code like this:

my $file = path($whatever);
$file->move($new_name);
say $file->basename;

does not give you the basename of the file-as-it-is, but rather the basename of the file-as-it-was, which could be considered less useful. But at least it doesn't mutate the object, so it's got that going for it. If you actually want the object to be mutated, try "move_to" instead.

tempfile

Basically works just like "tempfile" in Path::Tiny, except it:

  • is exported whether you like it or not.

  • can't be called as a class method, only a global function.

  • returns a Path::Class::Tiny instead of a Path::Tiny (obviously).

Other than that, it retains all the coolness of Path::Tiny::tempfile, including the normalization of arguments, the handling of template as either named or positional argument, and the addition of TMPDIR => 1 as a default (which you can override).

tempdir

Everything discussed re "tempfile" applies to "tempdir" as well. Don't forget that if you want to keep a tempfile, you have to pass UNLINK => 0, whereas if you want to keep a tempdir, you have to pass CLEANUP => 0. That's not our fault (nor Path::Tiny's neither): talk to File::Temp about it.

NEW METHODS

dirname

Since dirname in Path::Tiny is stupid, and dirname in Path::Class doesn't even exist, dirname in Path::Class::Tiny is just an alias for parent, on the grounds that that's almost certainly what you wanted anyway.

ef

Are you tired of trying to remember which method (or combination of methods) you have to call to verify that two files are actually the same file, where one path might be relative and the other absolute, or one might be a symlink to the other, or one might be a completely different path but one directory somewhere in the middle is really a symlink to a directory in the middle of the other path so they wind up being the same path, really? Yeah, me too. In bash, this is super easy:

if [[ $file1 -ef $file2 ]]

Well, why shouldn't it be easy in Perl too? Okay, now it is:

my $file1 = path($whatever);
if ( $file1->ef($file2) )

While $file1 must obviously be a Path::Class::Tiny, $file2 can be another Path::Class::Tiny object, or a Path::Class::Entity, or a Path::Tiny, or just a bare string. Most anything should work, really. Do note that both files must actually exist in the filesystem though. It's also okay for both to be exactly the same object:

if ( $file1->ef($file1) )   # always true

mtime

This is mostly just a shortcut for going through stat, but it has the added benefit of producing a Date::Easy::Datetime object. Thus:

my $file = path($whatever);
$file->mtime == $file->stat->mtime        # true, but maybe not for the reason you thought
$file->mtime->epoch == $file->stat->mtime # true, and more reflective of reality
$file->mtime->isa('Date::Easy::Datetime') # true, which can be handy:

say $file->mtime->as('-Ymd')    # day portion of mtime, in YYYY-mm-dd format
say "file is from the future!"  # this one will work, but only if you have
    if $file->mtime > now;      # previously done `use Date::Easy` (to get `now`)

Note that Date::Easy::Datetime is loaded on demand, so:

  • It is not necessary for you to load it ahead of time.

  • However, as the example above mentions, you don't get all the exports you would if you use Date::Easy, so you may wish to do that anyway.

  • If Date::Easy is not installed, you get a runtime error when you call mtime.

SUPPORT

Perldoc

You can find documentation for this module with the perldoc command.

perldoc Path::Class::Tiny

Bugs / Feature Requests

This module is on GitHub. Feel free to fork and submit patches. Please note that I develop via TDD (Test-Driven Development), so a patch that includes a failing test is much more likely to get accepted (or at least likely to get accepted more quickly).

If you just want to report a problem or suggest a feature, that's okay too. You can create an issue on GitHub here: https://github.com/barefootcoder/path-class-tiny/issues.

Source Code

none https://github.com/barefootcoder/path-class-tiny

git clone https://github.com/barefootcoder/path-class-tiny.git

AUTHOR

Buddy Burden <barefootcoder@gmail.com>

COPYRIGHT AND LICENSE

This software is Copyright (c) 2021 by Buddy Burden.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)