Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

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

by jcb (Parson)
on Aug 29, 2019 at 23:07 UTC ( [id://11105272]=note: print w/replies, xml ) Need Help??


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

Ideally, the iterator should need no connection to its parent other than shared data, and I see no reason that File::Collector should be a singleton object, which means that it needs to be an instance variable if it is used at all.

We can avoid that by transferring more data into the newly-constructed iterator. I now see that your application attaches some additional attributes to each file, where I had previously supposed that you care only about the file name. The solution is simple: add a name key to each item in $collector->{files} and change selected_file to:

sub selected_file { (shift)->[0]->{name} }

(also change the return value of next)

When constructing an iterator, pass a list of the relevant entries in ->{files} (which are hashrefs, so the per-category lists should be aliases into the master file set) to new. This even allows another bit of AUTOLOAD magic to provide selected_file_KEY getters that read $self->[0]->{KEY} on an iterator. The explicit selected_file method is therefore an alias for selected_file_name with this approach.

Replies are listed 'Best First'.
Re^18: How to completely destroy class attributes with Test::Most?
by nysus (Parson) on Aug 29, 2019 at 23:22 UTC

    Ok, but what about the case of a child class of Iterator needing to reach over into another child class with an object attached to one of the attributes? How can my child iterator objects get access to attributes of other child classes if not through File::Collector? Let's say, for example, a File::Collector::Parser::Iterator object needs to get some data from attributes in a File::Collector::HeaderAnalyzer object.

    I suppose I could just pass the File::Collector::HeaderAnalyzer objects to the File::Collector::Parser::Iterator object as a bundle. I'll have to play with this some more and see what I come up with. This is hard!

    Thanks!

    $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

      Every iterator subclass has a corresponding subclass of File::Collector that sorts files into additional categories. I had expected that each subclass that adds attributes ends up adding them to the same hashref as is indexed by $collector->{files}, so that if App::whatever::Collector::HeaderAnalyzer adds an is_valid_header attribute, that attribute appears in $collector->{files}{$filename}{is_valid_header}. Since the iterator now has the entire ->{files}{$filename} hash in its array, the AUTOLOAD magic that I suggested would provide a ->selected_file_is_valid_header method on the iterator object with no actual code in the iterator subclass.

        Nice, I got the File::Collector subclass down to one sub and just a few lines of code now:

        package File::Collector::DateClassifier ; use strict; use warnings; use Log::Log4perl::Shortcuts qw(:all); use File::Collector::DateClassifier::Iterator; use parent qw ( File::Collector ); sub _classify_files { my ($s, $files_added) = @_; $s->add_iterators( qw ( some_files other_files ) ); foreach my $file (sort @$files_added) { $s->add_to_iterator('some_files', $file); } } return 1;

        This feels fun, now. I feel like I can call myself an intermediate Perl programmer now. Only took 20 years. :)

        Thanks again for all your time advising me on this. I learned a lot. I'll post this up to CPAN when I think it's CPAN worthy. I got it on a git for now: File::Collector

        $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

        Phew, ok, I think I really got it down good now. I did some more refinement of how things work, too. Here's what I got:

        package File::Collector::DateAnalyzer ; use strict; use warnings; use Log::Log4perl::Shortcuts qw(:all); use File::Collector::DateAnalyzer::Iterator; use parent qw ( File::Collector ); sub add_resources { my $s = shift; $s->SUPER::add_resources(@_); $s->{files}{some_files} = File::Collector::DateAnalyzer::Ite +rator->new(); $s->{files}{other_files} = File::Collector::DateAnalyzer::Ite +rator->new(); $s->_test_blah; } sub _test_blah { my $s = shift; foreach my $file ($s->get_files) { $s->{files}{some_files}->add_file($s->{files}{all}{$file}); } } return 1; package File::Collector::DateAnalyzer::Iterator ; use strict; use warnings; use parent qw (File::Collector::Iterator); use Log::Log4perl::Shortcuts qw(:all); sub print_blah_names { my $s = shift; print $s->next->{short_path} . "\n\n"; } 1; package File::Collector::Iterator ; use strict; use warnings; use Carp; use Log::Log4perl::Shortcuts qw(:all); sub new { my $class = shift; bless [@_], $class; } sub next { my $s = shift; my $last = shift @$s; push @$s, $last; return $last; } sub add_file { my $s = shift; pop @$s; push @$s, shift; push @$s, ''; } sub print_short_names { my $s = shift; print $s->next->{short_path} . "\n"; } sub selected_file { my $s = shift; $s->[0]; } sub do { my $self = shift; bless \$self, 'File::Collector::Iterator::All'; } sub DESTROY { my $s = shift; $s->next while ($s->selected_file); } { package File::Collector::Iterator::All; use Log::Log4perl::Shortcuts qw(:all); sub AUTOLOAD { our $AUTOLOAD; my $self = shift; my @method = split /::/, $AUTOLOAD; my $method = pop @method; $$self->$method(@_) while ($$self->selected_file); $$self->next(@_); } }

        A few notes:

        Iterator is now a circular queue, terminated with an empty string.

        Attributes of File::Collector subclasses are now iterators themselves. This let me get rid of the AUTOLOAD in File::Collector.

        I restrucutred File::Collector->{files} as you suggested.

        This simple test works:

        { my $da; # 1 lives_ok { $da = File::Collector::DateAnalyzer->new('t/test_data/many +_files'); } 'creates DateAnalyzer object'; $da->{files}{some_files}->do->print_blah_names; $da->{files}{some_files}->do->print_short_names; }

        I will improve upon the do method calls so I don't have the ugly hashes in there and will use a simple AUTOLOAD so I can do $da->some_files->do->print_blah_names

        How does it look to you?

        $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

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (8)
As of 2024-04-19 15:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found