Let Paths Be Paths Again
The de facto standard way of constructing portable filesystem paths in Perl is through the use of File::Spec's catfile and catdir functions. Example:
my $path = File::Spec->catfile('dir', 'subdir', 'file.txt');
This method, or a similar one involving Path::Class, is the most recommended approach and has been adopted by application development frameworks like Dancer (which has a wrapper method for it, named path
) and Catalyst (with its path_to
method).
The slight problem that I see with this method is that it makes code a bit more complicated, and thus a bit less readable. Paths become lists of parameters and no longer look like paths.
I wrote a simple module that tries to address this by allowing you to write paths the traditional way -- as strings, using a directory separator of your choice (/
being the default), while the catfile
stuff happens behind the scenes. You can just say:
my $path = path 'dir/subdir/file.txt';
What it does is it splits the path string on each occurrence of the forward slash and feeds the resulting list of path components to File::Spec->catfile
, which reassembles them using the appropriate OS-specific directory separator, and constructs the OS-specific path that you want.
The module is up on Github, and should also be available on CPAN shortly.
I'm pretty sure this exists already, although for the life of me I can't recall a specific module name.
I know that I've personally written it as a utility function within some larger module four or five times.
(The one from File::PathList looks closest)
Using Path::Class is just as readable IMHO.
I'm genuinely curious: which currently-used systems don't use Unix-style paths? VMS? Windows seems to accept them in most contexts, and I don't think anyone uses Mac OS 9 anymore. It would be great to finally stop having to code defensively around paths (the runner-up bane of programmers' existence, after "\n" vs. "\r\n" vs. "\r").
I like the concept, but I worry a little about the way the separator is stored. If two modules both use File::PlainPath, and both use a different seperator, will they interfere with each other over the course of execution?
But what about absolute paths? What about drive letters? This doesn't take into account a lot of things that File::Spec does…
I'm with educated_foo on this one. 99,99% of the time, I've never bothered to use File::Spec and hardcode "/". IIRC, I've never had a CPAN Testers report where my modules fail because of path separator problem.
Majority convention becomes de facto standard all the time.
@targ: It's definitely a personal thing, and I'm sure many people are just fine with constructing paths with File::Spec or Path::Class, like you are. But for me, it is slightly less readable -- when I look at a path written as a simple string, I just see a path, while a catfile() construct forces me to think that I'm actually looking at a path. Sort of like the difference between looking at an arithmetic expression and figuring out the result, and just looking at a number.
@educated_foo: It is true that most of the time you can get away with just using Unix paths. However, I don't think it's actually guaranteed to work in every situation -- that's why using File::Spec or Path::Class is still recommended.
Moreover, there are cases when you do need to build a system-specific path -- one example that I can think of is when you want to output a path to the user, e.g.:
If you want your program to be user-friendly, you should display the path in a format that the user is familiar with.
@Leon Timmermans: That's correct, this module doesn't touch any of that and is mostly useful for constructing relative paths. It wasn't my intention to replace File::Spec entirely, just to provide an alternative, simpler solution for a common use case.
As a side note, I think if you're writing code that is supposed to be portable, you don't often need to work with absolute paths and deal with things like drive letters. In most cases, you need a single location that is your point of reference, such as the home directory of the user running your program, or the directory where your application is installed, and then you use relative paths to work your way from there. This is where this module might make things a tiny bit simpler, that's all.
I ask because my main reasons for using Path::Class or File::Spec would be fear, uncertainty, and doubt. Where, and how often, will blindly using a Unix path cause trouble?
BTW, I'm not too worried about showing paths to users, since if they're not on Unix, they probably won't understand them. I have never, while using a Mac, had to deal with the colon-separated native paths.
"Where, and how often, will blindly using a Unix path cause trouble?"
Windows. it works *most* of the time. except sometimes it doesn't. and it probably won't be clear why.
(i speak from horrible experience here, having lost a couple of hours last month debugging a problem where some paths in a configuration file were Unix-style and worked just fine, and others were Unix-style and failed far down the line with obtuse error messages. comedy!)