2011-06-29 63 views
2

我正在跟進關於perl web服務的此question。我設法從一個主程序加載和執行模塊。每個模塊是這樣的:如何動態加載模塊並在perl中執行方法

#!/usr/bin/perl 
package NiMbox::perlet::skeleton; 

use strict; 
use warnings; 

require Exporter; 
our @ISA = qw(Exporter); 
our @EXPORT_OK = qw(%DEFINITION main secondary); 

our %DEFINITION; 
$DEFINITION{'main'} = { 
    summary => 'skeleton main', 
    description => 'long skeleton main description', 
    args => { 'box' => {}, 'other' => {} } 
}; 
$DEFINITION{'secondary'} = { 
    summary => 'skeleton secondary', 
    description => 'long skeleton secondary description' 
}; 

sub main { 
    print "main...\n"; 
} 

sub secondary { 
    print "secondary...\n" 
} 

1; 

這些模塊的調用就可以這樣做:

use NiMbox::perlet::skeleton; 

my %DEFINITION = %NiMbox::perlet::skeleton::DEFINITION; 
foreach my $s (keys %DEFINITION) { 
    print "calling sub '$s'\n"; 
    NiMbox::perlet::skeleton->$s(); 
} 

我怎麼才能在一個方式擺脫NiMbox::perlet:skeleton直接調用我可以做一些看起來像這樣(不工作,但說明了什麼,我需要做的):

my $perlet = 'skeleton'; 

use NiMbox::perlet::$perlet; 

my %DEFINITION = %NiMbox::perlet::$perlet::DEFINITION; 
foreach my $s (keys %DEFINITION) { 
    print "calling sub '$s'\n"; 
    NiMbox::perlet::$perlet->$s(); 
} 

由於我非常接近我寧願看到缺什麼在這個例子中,而不是使用另一個庫。有任何想法嗎?

回答

3

我相信你要找的是什麼Exporter或它的許多後續模塊。我看到你已經在你的模塊中使用它,但你沒有使用它來得到%DEFINITION。你會做到這一點,像這樣:

use NiMbox::perlet::skeleton qw(%DEFINITION); 

foreach my $s (keys %DEFINITION) { 
    print "calling sub '$s'\n"; 
    NiMbox::perlet::skeleton->$s(); 
} 

這別名%NiMbox::perlet::skeleton::DEFINITION%DEFINITION,節省了一堆的打字。

爲了能夠使用%DEFINITION的變量定義,您可以使用「符號引用」來按名稱引用變量......但這些變量充滿了危險。另外,導出全局變量意味着您只能在給定的命名空間中一次只有一個。我們可以做得更好。

我建議改爲將%DEFINITION散列值更改爲definition()類方法,該方法返回對%DEFINITION的引用。你可以返回一個散列,但是這個引用避免了浪費時間複製。

package NiMbox::perlet::skeleton; 

use strict; 
use warnings; 

my %DEFINITION = ...; 

sub definition { 
    return \%DEFINITION; 
} 

現在您可以調用該方法並獲取散列引用。

use NiMbox::perlet::skeleton; 

my $definition = NiMbox::perlet::skeleton->definition; 

foreach my $s (keys %$definition) { 
    print "calling sub '$s'\n"; 
    NiMbox::perlet::skeleton->$s(); 
} 

動態地做它,唯一的技巧是加載類。你可以eval "require $class" or die [email protected]但有安全隱患。 UNIVERSAL::requireModule::Load可以爲您更好地處理。

use Module::Load; 

my $class = 'NiMbox::perlet::skeleton'; 
load $class; 

my $definition = $class->definition; 

foreach my $s (keys %$definition) { 
    print "calling sub '$s'\n"; 
    $class->$s(); 
} 
+0

感謝這麼好的解釋。我唯一需要擺脫的是最後一個'NiMbox :: perlet :: skeleton'。通過這種方式,我可以使模塊的名稱成爲一個變量,並且可能會調用它,像'docall skeleton main',它將調用'skeleton'包中的'main'子項。 – rmarimon

+0

明白了。只需使用'$ class - > $ s()'。 – rmarimon

+1

@rmarimon是的,這是我的一個疏忽。 – Schwern

3

如果你想在類名的動態,你可以做這樣的事情:

my $class = 'NiMbox::perlet::' . $perlet; 
my $class_file = $class; 
$class_file =~ s{::}{/}; 
$class_file .= '.pm'; 

require $class_file; 
$class->import; 

(甚至更好,使用Module::Load@Schwern suggests

獲取%DEFINITION類是有點棘手因爲它會涉及符號參考。更好的方法是提供返回它的類方法,例如

package NiMbox::perlet::skeleton; 
... 
sub definition { 
    my %definition; 
    $definition{main} = { summary => 'skeleton main', ... }; 
    return %definition; 
} 

然後,你可以這樣做:

my %DEFINITION = $class->definition; 
foreach my $s(keys %DEFINITION) { 
    print "calling sub '$s'\n"; 
    $class->$s; 
}