jjl / you-dont-know-perl

You Don't Know Perl - The Book
7 stars 0 forks source link

Dealing with Files #9

Open kentfredric opened 11 years ago

kentfredric commented 11 years ago

Path::Tiny

( Which is an improved reductionist lightweight alternative to Path::Class, does everything Path::Class does, does it better, )

Path::Iterator::Rule

( Which is more or less a hugely improved reductionist take on File::Find::Rule )

File::Next

ack-grep

kentfredric commented 11 years ago

I personally think File::Next should be missed, its surely one of the fastest, but the API is a little wonky and hard to cognitively memoize.

And I can't seem to imagine anything you could do with it that wouldn't be easier to do with either Path::Tiny or Path::Iterator::Rule

ie:


use Path::Tiny qw(path);
my $iterator = path('/some/path')->iterator({ recurse => 1 });
while( my $item = $iterator->() ) { 
  # . and .. automatically skipped for you
  # walks entire /some/path and each item is a Path::Tiny object 
}

And for more complex things theres Path::Iterator::Rule, which shares the same familiar API style


use Path::Iterator::Rule;
my $rule = Path::Iterator::Rule->new->file;
my $iterator = $rule->iter("/some/path");
while( my $item = $iterator->() ) { 
  # . and .. automatically skipped for you
  # walks entire /some/path and each item is a string
  # also, as per rule above, only files are returned to user
}
jjl commented 11 years ago

File::Next looked promising with files(), dirs() and everything(). Then I read it has loads more stuff and started facepalming. Path::Tiny one should be modified with next unless $item->is_file;, partially because the documentation doesn't indicate it skips directories (I'm not saying you're wrong, I haven't read the code), but because we can replace the $item->is_file with next unless is_wanted_item($item) and then it becomes very much more reusable in the context of doing ever more impressive things.

It might be great to team it with IO::All, which is damn cool. Python has now gotten much cleaner for dealing with files as well, but IO::All still definitely has the edge.

with open(filename) as fh:
    for line in fh:
        func(line)

This then takes care of closure of the file the moment the block has been left. What's quite nifty is that this doesn't just apply to files, but the syntax extends to quite of lot of things. I wonder if we could extended the ideas of IO::All onwards in a similar manner.

jjl commented 11 years ago

Sorry, didn't mean to hit close. I've been doing that too much during the daytime recently.

osfameron commented 11 years ago

re with-open-file, we can already do:

for my $fh (io 'foo.dot') {
    for my $line ($fh->getlines) {
    print $line;
    }
}

Or we could hack up a 'with' keyword using Keywords::API or similar that does same as for, but in scalar context.

davorg commented 11 years ago

IO::All scares the crap out of me. I place it firmly in the "too magic to be used in production" category. I really wouldn't be introducing it to beginners.

jjl commented 11 years ago

Are we really targeting beginners though? Perhaps people who don't know too much perl, but generally I think we're targeted at people who know how to code to at least some standard.

jjl commented 11 years ago

What about an IO::Most wrapper around IO::All that removes a bit of the crack (and things that aren't useful to most people anyway) ?

jjl commented 11 years ago

osf': isn't that equivalent to:

{
  my $fh = io "foo.bar";
  ...
}
osfameron commented 11 years ago

jjl sure, that's pretty much all a with statement is, isn't it? Create scope, bind a variable within that scope.

Using a for keyword just has the benefit that it can do the aliasing to $_ if you prefer, and it's more explicitly setting a variable then the bare block (using a 'with' keyword would be even clearer).

kentfredric commented 11 years ago

I don't use IO::All, its just far too "magical" for me.

my $fh = path("/some/path")->openr; 

Is adequate, explicit, and unambiguous.

And for linewise,

for my $line ( path("/some/path")->lines ) {

}

And if you want the lines chomped

for my $line ( path("/some/path")->lines({ chomp => 1 }) ) {

}

And if you want utf8

for my $line ( path("/some/path")->lines_utf8({ chomp => 1 }) ) {

}

Just more "natural" to me than most the IO::All magic.

kentfredric commented 11 years ago

"partially because the documentation doesn't indicate it skips directories "

And yes, Path::Tiny's recursive iterator tool traveses both files and directories, returning both.

As does Path::Iterator::Rule unless you tell it to reduce results to files by specifying the ->file rule.