Subject: | chomp-ing slurp-ed input |
In a recent Perl Module Authors thread there was discussion of how this could be better expressed:
my @mp3s = grep { -f } @ARGV, ($m3u?(map{chomp;$_}`cat "$m3u"`):());
I came up with this example of using Path::Class:
push @ARGV, (file $m3u)->slurp if $m3u;
chomp @ARGV;
my @mp3s = grep { -f } @ARGV;
That's still a little awkward. The awkwardness is Perl's built-in chomp function not returning the chomped text, so can't be chained (at least not without using a map block, as in the one-liner above).
I considered:
Perhaps Path::Class::File should steal IO::All's chomp method? (Or
perhaps that would start to bloat what is currently a very clean and
elegant suite of modules -- and if we keep doing things like that we'd
just end up turning Path::Class into IO::All, thereby proving that Ingy
was right in the first place.)
But I've since come up with a real-world situation where I wanted this: I'm trying to get the current CVS repository directory from a working copy; that involves constructing a path from the contents of CVS/Root and CVS/Repository:
my $cvs_dir = dir map { my $f = (file 'CVS', $_)->slurp; chomp $f; $f }
qw<Root Repository>;
Ug! I don't like that. This seems much cleaner:
my $cvs_dir = dir map { (file 'CVS', $_)->slurp(1) } qw<Root Repository>;
Attached is a patch that makes that work, with tests and docs. Having a true value flag that chomping is desired is a little obfuscatory, but I don't think it's too bad, and I didn't like any of the other interfaces I tried:
* $file->chomp->slurp, as per IO::All, doesn't make as much sense here as
there because IO::All has a number of file-reading methods that are
all affected by chomp. In Path::Class::File slurp is the only such
method, so we've effectively made chomp->slurp the only circumstance
in which it is meaningful to call the chomp method.
(Also I kept wanting to write it as $file->slurp->chomp instead.)
* That led me on to thinking that since the chomp method is only of use
when slurping, why not make it slurp as well, and rename the method
so you have something like $file->chomp_and_slurp. But that seemed
quite long for a method name, and having lots of similarly named
methods just doing slightly different things always grated on me in
PHP.
* So really it's just an option on the slurp method. That suggested
$file->slurp(chomped => 1), or perhaps $file->slurp(chomp => 1). I
don't really object to that in general (though the Perl 6 version of
$file->slurp(:chomped) is much nicer), but there aren't lots of
Path::Class methods with named arguments at the moment that this has
to fit in with, and it isn't immediately apparent that there'll be
more options needed on slurp.
* Which made me decide that I find $file->slurp(1) to be the nicest. If
it turns out that slurp does need further options in the future then
it isn't too much hassle to switch to having named options then and
just keep (1) as a special case for backwards compatibility.
Obviously it's your call, and I don't much mind having a different
interface!
For reference, here's the original thread:
http://groups.google.co.uk/groups?threadm=slrnd6uabh.ohq.Smylers@stripey.com
Cheers.
Smylers
diff -ur -r Path-Class-0.10/lib/Path/Class/File.pm Path-Class-dev/lib/Path/Class/File.pm
--- Path-Class-0.10/lib/Path/Class/File.pm Wed Apr 6 23:05:08 2005
+++ Path-Class-dev/lib/Path/Class/File.pm Wed May 11 17:22:45 2005
@@ -62,10 +62,11 @@
sub openw { $_[0]->open('w') or die "Can't write $_[0]: $!" }
sub slurp {
- my $self = shift;
+ my ($self, $chomp) = @_;
my $fh = $self->open() or die "Can't open $self: $!";
- local $/ unless wantarray;
- return <$fh>;
+ my @content = do { local $/ unless wantarray; <$fh> };
+ chomp @content if $chomp;
+ return wantarray ? @content : $content[0];
}
1;
@@ -243,7 +244,10 @@
In a scalar context, returns the contents of C<$file> in a string. In
a list context, returns the lines of C<$file> (according to how C<$/>
is set) as a list. If the file can't be read, this method will throw
-an exception.
+an exception. The output will be C<chomp>ed if you pass a true value to
+C<slurp()>:
+
+ my @staff = $file->slurp(1);
=item $st = $file->stat()
diff -ur -r Path-Class-0.10/t/03-filesystem.t Path-Class-dev/t/03-filesystem.t
--- Path-Class-0.10/t/03-filesystem.t Wed Apr 6 23:05:08 2005
+++ Path-Class-dev/t/03-filesystem.t Wed May 11 17:23:31 2005
@@ -3,7 +3,7 @@
use Test;
use Path::Class;
-plan tests => 42;
+plan tests => 46;
ok 1;
my $file = file('t', 'testfile');
@@ -104,10 +104,18 @@
my $content = $file->slurp;
ok $content, "Line1\nLine2\n";
+ $content = $file->slurp(1);
+ ok $content, "Line1\nLine2";
+
my @content = $file->slurp;
ok @content, 2;
ok $content[0], "Line1\n";
ok $content[1], "Line2\n";
+
+ @content = $file->slurp(1);
+ ok @content, 2;
+ ok $content[0], "Line1";
+ ok $content[1], "Line2";
unlink $file;
ok -e $file, undef;