如何在Perl脚本中包含所有/部分“子模块”?

时间:2009-02-12 17:28:20

标签: perl perl-module

我刚才开始说我对创建Perl模块没有任何经验,所以如果我离开这里我很抱歉。

假设我正在创建一些模块:

foo::bar
foo::bar::a
foo::bar::b

由于我不知道它们叫什么,我称之为a.pm和b.pm模块“子模块”,因为它们与bar.pm模块有关,但仍然可能有点独立。

所以我的一个Perl脚本可以使用foo :: bar :: a,另一个脚本可以使用foo :: bar :: b,也许我有另一个脚本需要使用“a”和“b”中的函数”。而不是这样说:

use foo::bar;
use foo::bar::a qw(one two);
use foo::bar::b;

我想做这样的事情:

use foo::bar qw(:a :b);

在我看来,这会让我的脚本可以访问bar.pm,a.pm和b.pm中的所有内容。

我测试了这样的东西,我显然是错的。

这样的事情可能吗?我想我可以使用bar.pm使用a.pm和b.pm,然后使用“包装”函数将调用传递给“子模块”,但似乎会有更简单的方法。

6 个答案:

答案 0 :(得分:5)

查看我的Test::Data模块,了解有关这样做的示例。即使你能实现它,我也从未如此喜欢这个结果。您可能想要考虑插件或Mixin方法。 CPAN上有一些模块可以帮助解决这个问题。

这是我为Test :: Data编写的自定义import

sub import 
    {
    my $self   = shift;
    my $caller = caller;

    foreach my $package ( @_ )
        {
        my $full_package = "Test::Data::$package";
        eval "require $full_package; 1";
        if( $@ )
            {
            carp "Could not require Test::Data::$package: $@";
            }

        $full_package->export($caller);
        }

    }

答案 1 :(得分:2)

是的,你可以这样做。它可能涉及在foo :: bar中编写一个自定义的“子导入”,它以你想要的方式解释传入的参数。

可能你现在正在使用Exporter,而且它缺乏对你的语法的支持。您会发现Exporter实现的模块语法没有什么特别之处;这只是一个共同的惯例。但是,您可能希望了解如何开展业务以深入了解您的需求。

答案 2 :(得分:1)

如果您不知道模块的名称,为什么要包含它?你不应该包含它。只在(调用)模块中包含一个需要它的模块,而不是其他任何模块。

即:如果您正在使用它,那么“使用”它。如果您不使用它,请不要“使用”它。

答案 3 :(得分:0)

另请尝试查看Class::MixinFactory

答案 4 :(得分:0)

是的,但您必须装备自己的导入子:

use strict;
use warnings;

package ab;
use base qw<Exporter>;
our @EXPORT_OK;
our %EXPORT_TAGS;
BEGIN { 
    @EXPORT_OK   = qw<>;
    %EXPORT_TAGS = ( a => 1, b => 1, all => \@EXPORT_OK );
}

sub setup_part { 
    #use Smart::Comments;
    my $code = shift;
    my $mini_path = "foo/bar/$code.pm";
    return if exists $INC{$mini_path};
    require $mini_path; 
    my $arr_ref 
        = do { no strict 'refs';
            \@{Symbol::qualify( 'EXPORT_OK', $code )};
        };
    $code->import( @$arr_ref );
    push @EXPORT_OK, @$arr_ref;
    $EXPORT_TAGS{$code} = [ @$arr_ref ];
    return;
}

sub import { 
    my ( $package_name, @imports ) = @_;
    my %import_hash = map { $_ => 1 } @imports;
    if ( exists $import_hash{':all'} ) { 
        @import_hash{qw<:a :b>} = ( 1, 1 );
    }
    foreach my $import ( grep { exists $import_hash{$_} } qw<:a :b> ) { 
        setup_part( substr( $import, 1 ));
    }
    goto &{Exporter->can( 'import' )};
}

1;

答案 5 :(得分:0)

我已经找到了类似于最近的解决方案。我知道 - 太老了,但我想通过 brian d foy 评论答案( 2月12日&09; 09,15:55 )但是遗憾的是,我没有足够的声誉来实现这一目标。这就是我将评论添加为新回复的原因。

他的回答帮助我解决了类似于最近的问题。但如果与use lib一起使用,则需要进行一些修改。

我有一堆看起来像A::B::*的模块。那些应该由通用模块A::B加载到脚本中。所有这些模块都在其文件中,与加载脚本位于同一目录下。使用 brian d foy 建议的机制,我们可以获得许多子例程重新定义的错误。我相信,为了避免所有这些,我找到了更好的解决方案,优于no warnings 'redefine'。现在,我们可以在主脚本中自由使用use libno warnings 'redefine'shift @INC, ...


    sub import {
        @TAGS = ( @_ );
        my $me = shift @TAGS;

        ( my $pm = $me ) =~ s|::|/|g;
        $pm .= ".pm";

        ( $dir = $INC{$pm} ) =~ s/\.pm$//;
        foreach ( glob "$dir/*.pm" ) {
            /(\w+)\.pm$/;
            my $module = "${me}::$1";

            eval "use $module qw(:all)"; # You are free to use any items in the exporting list
            die "$me: Error while loading $module from $_: $@\n" if $@;
        }

        # Fill in @EXPORT_OK and %EXPORT_TAGS manually from each A::B::*::EXPORT_OK
        # ...

        goto &{ Exporter->can( "import" ) };
    }