Reputation: 25445
I'm building a Perl application that relies on Moose. One task the Moose object needs to accomplish is to use File::Find to populate an attribute with a list of files. I'm having some trouble figuring out how to use find's \&wanted
code reference in a way that will let me maintain access to a $self
version of the Moose object.
So far, I have this:
#!/usr/bin/perl
package MyMoose;
use Modern::Perl;
use Moose;
use File::Find;
use FindBin qw($Bin);
### Attribute to hold the file list
has "file_list" => (
is => 'rw',
isa => 'ArrayRef',
default => sub {[]}
);
### Method to populate the file list
sub update_file_list {
my $self = shift;
find(\&wanted, $Bin);
}
### File::Find wanted sub
sub wanted {
### This won't work, but shows what I'd like to do
# my $self = shift;
# ### Do some filtering
# push @{$self->file_list}, $File::Find::name;
}
1;
######################################################################
### Main package to test the object.
package main;
use Data::Dumper;
run_main() unless caller();
sub run_main {
my $m = MyMoose->new();
$m->update_file_list();
print Dumper $m->file_list;
}
It runs, but obviously doesn't assemble a file list. That's the part I'm trying to figure out.
What's the proper way to use File::Find so that it will let you have access to the Moose object during processing?
Upvotes: 3
Views: 444
Reputation: 25445
After some trial an error, I also got the script to work by replacing the original version of 'update_file_list' with this:
sub update_file_list {
my $self = shift;
find( sub { wanted($self); }, $Bin );
}
That seems to work as well.
Upvotes: 0
Reputation: 50338
As bvr notes, the subroutine reference passed to find
doesn't need to be a named package method — a lexical closure will work just fine. Thus, you can do this:
sub update_file_list {
my $self = shift;
my $wanted = sub {
### Do some filtering
push @{$self->file_list}, $File::Find::name;
};
find($wanted, $Bin);
}
The lexical variable $self
declared in the outer function scope will be visible in the inner function.
In particular, every time the update_file_list
method is called, a new $self
and a new $wanted
will be created (and bound together by the inner reference to $self
), so that it's perfectly safe to call the method several times on different objects, even recursively if you want.
Upvotes: 0
Reputation: 9697
The problem is that you don't have access to $self
within wanted
sub. You can use inline closure and default
or builder
to build the list.
Edit: updated code per updated question
has "file_list" => (
is => 'rw',
isa => 'ArrayRef',
default => sub {
my $self = shift;
return $self->_get_file_list();
},
);
sub update_file_list {
my $self = shift;
$self->file_list($self->_get_file_list());
}
sub _get_file_list {
my @files;
find(sub { push @files, $File::Find::name }, $Bin);
return \@files;
}
_get_file_list
method returns arrayref of files found. It is used both in default
and update_file_list
method to populate the attribute.
Upvotes: 2