2011-06-29 3 views
2

Я слежу за этим question о веб-сервисах perl. Мне удалось загрузить и загрузить модули из основной программы. Каждый из модулей что-то вроде этого:Как динамически загружать модули и выполнять методы в 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; 
} 

Теперь вы можете вызвать этот метод и получить хэш-код ref.

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::require или Module::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 :: скелет».Таким образом, я могу сделать имя модуля переменной и потенциально назвать это как 'docall skeleton main', который будет вызывать' main' sub в пакете 'skeleton'. – 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; 
} 
Смежные вопросы