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

时间:2023-01-13 15:51:13

I'll just start out by saying I am not at all experienced with creating Perl modules so I'm sorry if I'm way off here.

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

Let's say I am creating a few modules:

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

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

Since I don't know what they are called, I am calling the a.pm and b.pm modules "sub modules" since they are related to the bar.pm module, but could still be somewhat independent.

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

So one of my Perl scripts could use foo::bar::a, another script could use foo::bar::b, and maybe I have another script that needs to use functions from both "a" and "b". Instead of saying this:

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

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

I want to do something like this:

我想做这样的事情:

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

In my mind, that would give my script access to everything in bar.pm, a.pm, and b.pm.

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

I tested something like this, and I was obviously wrong.

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

Is something like this possible? I suppose I could have bar.pm use a.pm and b.pm, and then have "wrapper" functions that pass the call onto the "sub modules" but it seems like there would be an easier way.

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

6 个解决方案

#1


Look at my Test::Data module for an example about doing that. Even though you can make it happen, I've never been terribly fond of the result. You might want to consider a Plugin or Mixin approach instead. There are some modules on CPAN that can help with this.

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

Here's the custom import that I wrote for Test::Data:

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

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);
        }

    }

#2


Yes, you can do that. It will probably involve writing a custom 'sub import' in foo::bar that interprets incoming arguments the way you want.

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

Probably you're using Exporter right now, and it's its lack of support for your syntax that's the issue. You'll find that there's nothing particularly special about the module syntax Exporter implements; it's just a common convention. You'll likely want to look at how it does business to get insight into how you'll want to, though.

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

#3


If you don't know what a module is called, why are you including it? You shouldn't need to include it. Only include a module in the (calling) module that needs it, and nowhere else.

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

That is: if you are using it, then "use" it. If you don't use it, don't "use" it.

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

#4


Yes, but you have to rig your own import sub:

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

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


Also try looking at Class::MixinFactory

另请尝试查看Class :: MixinFactory

#6


I have looked for the solution similar to the recent one. I know -- too old thread but I'd like to comment the answer (Feb 12 '09 at 17:55) by brian d foy but sadly I don't have enough reputation to accomplish this. That's why I add my comment as new response.

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

His answer has helped me to solve the issue similar to the recent one. But it requires some modification if is used with use lib.

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

I have a bunch of modules that look like A::B::*. Those should be loaded to scripts by the general module A::B. All those modules are within their files under the same directory as the loading script. Using the mechanism suggested by brian d foy we can get many subroutine redefined errors. To avoid all of them, I believe, I found better solution, better than no warnings 'redefine'. Now we are free to use use lib, no warnings 'redefine' or shift @INC, ... in the main script.

我有一堆看起来像A :: B :: *的模块。这些应该由通用模块A :: B加载到脚本中。所有这些模块都在其文件中,与加载脚本位于同一目录下。使用brian d foy建议的机制,我们可以获得许多子程序重新定义的错误。为了避免所有这些,我相信,我找到了更好的解决方案,比没有警告'重新定义'更好。现在我们可以在主脚本中*使用lib,没有警告'重新定义'或转换@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" ) };
    }

#1


Look at my Test::Data module for an example about doing that. Even though you can make it happen, I've never been terribly fond of the result. You might want to consider a Plugin or Mixin approach instead. There are some modules on CPAN that can help with this.

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

Here's the custom import that I wrote for Test::Data:

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

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);
        }

    }

#2


Yes, you can do that. It will probably involve writing a custom 'sub import' in foo::bar that interprets incoming arguments the way you want.

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

Probably you're using Exporter right now, and it's its lack of support for your syntax that's the issue. You'll find that there's nothing particularly special about the module syntax Exporter implements; it's just a common convention. You'll likely want to look at how it does business to get insight into how you'll want to, though.

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

#3


If you don't know what a module is called, why are you including it? You shouldn't need to include it. Only include a module in the (calling) module that needs it, and nowhere else.

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

That is: if you are using it, then "use" it. If you don't use it, don't "use" it.

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

#4


Yes, but you have to rig your own import sub:

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

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


Also try looking at Class::MixinFactory

另请尝试查看Class :: MixinFactory

#6


I have looked for the solution similar to the recent one. I know -- too old thread but I'd like to comment the answer (Feb 12 '09 at 17:55) by brian d foy but sadly I don't have enough reputation to accomplish this. That's why I add my comment as new response.

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

His answer has helped me to solve the issue similar to the recent one. But it requires some modification if is used with use lib.

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

I have a bunch of modules that look like A::B::*. Those should be loaded to scripts by the general module A::B. All those modules are within their files under the same directory as the loading script. Using the mechanism suggested by brian d foy we can get many subroutine redefined errors. To avoid all of them, I believe, I found better solution, better than no warnings 'redefine'. Now we are free to use use lib, no warnings 'redefine' or shift @INC, ... in the main script.

我有一堆看起来像A :: B :: *的模块。这些应该由通用模块A :: B加载到脚本中。所有这些模块都在其文件中,与加载脚本位于同一目录下。使用brian d foy建议的机制,我们可以获得许多子程序重新定义的错误。为了避免所有这些,我相信,我找到了更好的解决方案,比没有警告'重新定义'更好。现在我们可以在主脚本中*使用lib,没有警告'重新定义'或转换@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" ) };
    }