How can I find all the packages that inherit from a package in Perl?
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
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/
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.
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.
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.
精彩评论