如何在Perl中找到从包中继承的所有包?

时间:2010-01-08 03:18:08

标签: perl inheritance package moose

我有许多不同的网站,我从中下载数据并按摩到其他格式(使用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 个答案:

答案 0 :(得分:6)

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

来自Class::MOP::Class docs:

  

<强> $ metaclass-&GT;亚类   这将返回此类的所有子类的列表,甚至是间接子类。

     

<强> $ metaclass-&GT; 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 /

答案 1 :(得分:3)

虽然您说您要转移到Moose,但非Moose方法是根据基本包名称将所有派生包放入已知子目录中。然后加载所有模块

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

答案 2 :(得分:2)

您要求的是不可能的,因为您要使用的所有软件包都不会被加载。为什么不将所有软件包放在一个公共目录中,然后让脚本打开该目录,对于每个文件,需要它,然后实例化对象。

答案 3 :(得分:1)

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

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

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

由于这是基于命名空间的,因此它会引入Parent::Helper::ThatIsNotAChild,同时缺少Child::NotUnder::Parent::Namespace,因此它并不完全完美。