4

我有许多不同的站点,我从这些站点下载数据并将其转换为其他格式(使用 Perl)以供工作使用,它们都是从一个 Perl 脚本运行的,有点像这样:

#! /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;

等等等等。目前每个My::Package都是自己的包;它不会从基本包或任何东西继承。我计划使用重新编写它们Moose,我希望不必编辑每次添加新包时运行下载的 Perl 脚本,可能有一种方法可以找到从基本包继承的包,并且然后在循环中实例化每个并进行下载,有点像这样:

#! /usr/bin/perl
use strict;

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

它,或者类似的东西,可能吗?

TIA

4

4 回答 4

6

使用Moose'Class::MOP基础,您可以找到分配给每个类的子类(在那个时间点)。

来自Class::MOP::Class文档:

$metaclass->subclasses 这将返回这个类的所有子类的列表,甚至是间接子类。

$metaclass->direct_subclasses 这将返回该类的直接子类列表,其中不包括间接子类。

例如,如果我们构建这些类:

{
    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__ }
}

然后以您的示例为基础,我们可以这样做:

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

# =>  "downloading from NodeA"

所以上面运行NodeA->download。将上面更改为meta->subclasses也将运行NodeA1->download.

/I3az/

于 2010-01-08T10:25:56.360 回答
3

尽管您说要迁移到Moose,但一种非Moose方法是将所有派生的包放在基于基本包名称的已知子目录中。然后加载所有模块

例如,如果您的基础包是Local::Downloader,则所有派生包都以Local::Downloader::Plugin或类似的开头。然后,您在@INCthat much中查找所有模块.../Local/Downloader/Plugin/...。虽然自己做起来并不难,但像Module::PluginFinder这样的东西也可以为你做。

于 2010-01-08T11:09:31.017 回答
2

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.

于 2010-01-08T03:39:53.233 回答
1

基于继承没有办法做到这一点,因为父类甚至不知道它是否有后代,更不用说它有多少或它们的名字是什么。

但是,如果您遵循使用分层命名空间并将后代命名为 、 等的通用约定,则Parent::Foo可以通过使用Module::Pluggable来加载命名空间下的所有内容Parent::Bar来近似这一点:Parent

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

但是,由于这是基于名称空间的,它会拉入Parent::Helper::ThatIsNotAChild,而缺少Child::NotUnder::Parent::Namespace,因此它并不完全完美。

于 2010-01-08T10:19:16.313 回答