views:

114

answers:

4

I have a number of different sites that I download data from and massage into other formats (using Perl) for use at work, that are all run from one Perl script kinda like so:

#! /usr/bin/perl
use strict;

use My::Package1;
use My::Package2;

my $p1 = My::Package1->new;
$p1->download;

my $p2 = My::Package2->new;
$p2->download;

and so on and so forth. At the moment each My::Package is its own package; it doesn't inherit from a base package or anything. I am planning to re-write them using Moose and I was hoping that rather than having to edit the Perl script that runs the download each time a new package is added, there might be a way of finding packages that inherit from a base package, and then in a loop instantiate each and do the downloading, kinda like so:

#! /usr/bin/perl
use strict;

for my $pname (packages_that_inherit_from("My::Package")) {
    my $package = $pname->new;
    $package->download;
}

Is it, or something ilke it, possible?

TIA

+2  A: 

What you are asking for wont be possible because none of the packages you are looking to use will be loaded yet. Why not place all of the packages in a common directory, and then have your script open that directory, and for each file, require it, and then instantiate your objects.

Eric Strom
+2  A: 

There's no way to do this based on inheritance because the parent class doesn't even know if it has descendants, never mind how many it has or what their names are.

However, if you follow the common convention of using hierarchal namespaces and naming the descendants as Parent::Foo, Parent::Bar, etc., you can approximate this by using Module::Pluggable to load up everything under the Parent namespace:

use Module::Pluggable require => 1, search_path => ['Parent'];
my @descendants = plugins();

Since this is based on the namespaces, though, it will pull in Parent::Helper::ThatIsNotAChild, while missing Child::NotUnder::Parent::Namespace, so it's not entirely perfect.

Dave Sherohman
+5  A: 

Using Moose's Class::MOP underpinning you can find the subclasses assigned to each class (at that point in time).

From Class::MOP::Class docs:

$metaclass->subclasses This returns a list of all subclasses for this class, even indirect subclasses.

$metaclass->direct_subclasses This returns a list of immediate subclasses for this class, which does not include indirect subclasses.

So for example if we build these classes:

{
    package Root;
    use Moose;
    use namespace::clean -except => 'meta';

    sub baz      { say 'Some root thingy' }
    sub download { say "downloading from " . __PACKAGE__ }
}

{
    package NodeA;
    use Moose;
    extends 'Root';
    use namespace::clean -except => 'meta';
    sub download { say "downloading from " . __PACKAGE__ }
}

{
    package NodeA1;
    use Moose;
    extends 'NodeA';
    use namespace::clean -except => 'meta';
    sub download { say "downloading from " . __PACKAGE__ }
}

Then using your example as a basis we can do this:

for my $pname ( Root->new->meta->direct_subclasses ) {
    my $package = $pname->new;
    $package->download;
}

# =>  "downloading from NodeA"

So above runs NodeA->download. Changing above to meta->subclasses would also run the NodeA1->download.

/I3az/

draegtun
+3  A: 

Although you say you are moving to Moose, a non-Moose way is to put all the derived packages in the known subdirectory based on the base package name. You then load all of the modules

For instance, if your base package is Local::Downloader, all the derived packages start with Local::Downloader::Plugin or something similar. You then look for all modules in your @INC that much .../Local/Downloader/Plugin/.... Although it's not too hard to do yourself, something like Module::PluginFinder can do it for you too.

brian d foy