Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Re^10: How to completely destroy class attributes with Test::Most?

by jcb (Parson)
on Aug 28, 2019 at 01:33 UTC ( [id://11105154]=note: print w/replies, xml ) Need Help??


in reply to Re^9: How to completely destroy class attributes with Test::Most?
in thread How to completely destroy class attributes with Test::Most?

It might work better to have a "stack" of iterators, but the more I think about this, the more I think that your original API with a single iterator inside the object and the option to retrieve an explicit list of files in a category will probably be better for long-term maintenance.

DOH! Better still, make iterators a separate class File::Collector::Iterator with AUTOLOAD providing both get_TYPE_files and scan_TYPE_files methods, with the latter returning an iterator object. This allows moving the _selected_file instance variable into that iterator instead of making it part of the collection, which does not really make logical sense.

Here is a rough draft: (in File::Collector)

sub AUTOLOAD { our $AUTOLOAD; my $s = shift; $AUTOLOAD =~ /.*::(get|scan)(_\w+)_files*$/ or croak "No such method: $AUTOLOAD"; my ($mode, $type) = ($1, $2); # Get the files and return appropriate file(s) based on method calle +d return @{$s->{$type.'_files'}} if ($mode eq 'get'); return new ref($s).'::Iterator' (@{$s->{$type.'_files'}}) if ($mode +eq 'scan'); }

(in File::Collector::Iterator)

sub new { my $class = shift; bless [@_], $class; } sub next_file { my $self = shift; shift @$self; return $self->[0]; } sub selected_file { (shift)->[0] }

This is a low-overhead proof-of-concept that simply uses an array as the iterator and uses the first element of the array as the "selected file" variable.

This would require classes that build on File::Collector to also supply subclasses for File::Collector::Iterator, but that is probably exactly what you want: delete_file could be a method on the ::Iterator for the subclass that produces the "bad header files" category.

What do you think?

Replies are listed 'Best First'.
Re^11: How to completely destroy class attributes with Test::Most?
by nysus (Parson) on Aug 28, 2019 at 13:31 UTC

    I noodled around with this. So, the best I can tell, this makes iterating over files a two step process. First I have to create the iterator and then I have to loop over it? Something like this:

    my $iterator = $fc->scan_blah_files; while ($iterator->next_file) { my $file = $iterator->selected_file; print $file . "\n"; } }

    I rewrote it a bit to keep the get/next terminology:

    sub AUTOLOAD { our $AUTOLOAD; my $s = shift; $AUTOLOAD =~ /.*::get(_next)*(_\w+)*_files*$/ or croak "No such method: $AUTOLOAD"; my ($next, $type) = ($1, $2); if ($next) { # create an iterat +or my $class = ref($s) . '::Iterator'; if (!$type) { return $class->new($s->get_files); # iterator for all + files } else { return $class->new(@{$s->{$type.'_files'}}); # iterator for fil +e subset } } else { return @{$s->{$type.'_files'}}; # return file subs +et } }

    $PM = "Perl Monk's";
    $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
    $nysus = $PM . ' ' . $MCF;
    Click here if you love Perl Monks

Re^11: How to completely destroy class attributes with Test::Most?
by nysus (Parson) on Aug 28, 2019 at 19:21 UTC

    Ok, I spent a lot more time on this. I got it back to a one step process while using your idea to create iterator objects AND can do multiple iterators now. Woot!

    sub AUTOLOAD { our $AUTOLOAD; my $s = shift; $AUTOLOAD =~ /.*::get(_next)*(_\w+)_files*$/ or croak "No such method: $AUTOLOAD"; my ($next, $type) = ($1, $2); # Handle edge case when get_next_file() method is called if (!$next && $type eq '_next') { $next = 1; $type = ''; } # return the requested list of files if (!$next) { my $attr = "${type}_files"; my @files = @{$s->{$attr}}; return @files; } # get a single file from an iterator if ($s->{"_iterator_$type"}) { my $file = $s->{"_iterator_$type"}->next_file; $s->{_last_iterator_file} = $file; undef $s->{"_iterator_$type"} if (!$file); return $file; } else { my @files = $type ? @{$s->{"${type}_files"}} : $s->get_files; return '' if !@files; my $class = ref($s) . '::Iterator'; $s->{"_iterator_$type"} = $class->new(@files); $s->{_last_iterator_file} = $s->{"_iterator_$type"}->selected_file +; return $s->{"_iterator_$type"}->selected_file; } } sub selected_file { my $s = shift; return $s->{_last_iterator_file}; }

    Now I can do nested loops:

    while ($fp->get_next_file) { print $fp->selected_file . "\n"; while ($fp->get_next_nonparseable_file) { print $fp->selected_file . "\n"; } }
    Thanks so much for the pointers and getting me to think hard about this. Much appreciated!

    $PM = "Perl Monk's";
    $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
    $nysus = $PM . ' ' . $MCF;
    Click here if you love Perl Monks

      Building on your previous suggestion, now I'm thinking I could do something like this:

      $AUTOLOAD =~ /.*::(get|bundle)(_next)*(_\w+)_files*$/

      The bundle mode would also create an object that's just a list of files except you would perform operations on. So instead of:

      while ($s->get_next_blah_file) { $s->delete($s->selected_file); }
      You would just do:
      my $bundle = $s->bundle_blah_files; $bundle->delete;

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        The key motivation for independent iterator objects is that they allow the program to have multiple iterators even on the same category by removing the need for the File::Collector object to track the iterators.

        These "bundles" look a lot like independent iterators, and you could easily introduce an ->all method that returns a special object like so:

        (in File::Collector::Iterator)

        sub all { my $self = shift; bless \ $self, 'File::Collector::Iterator::All'; } { package File::Collector::Iterator::All; sub AUTOLOAD { our $AUTOLOAD; my $self = shift; $$self->$AUTOLOAD(@_) while ($$self->next_file); } }

        This allows subclasses of File::Collector::Iterator to define their own "bundle actions" invokable as $iterator->all->$action. For example, a subclass that adds a delete action could contain simply:

        sub delete { unlink (shift)->selected_file }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11105154]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2024-04-19 07:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found