Как динамически загружать модули и выполнять методы в Perl - PullRequest
2 голосов
/ 29 июня 2011

Я отвечаю на этот вопрос о веб-сервисах 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();
}

Поскольку я очень близок, я бы лучше увиделотсутствует в этом примере, а не использовать другую библиотеку.Есть идеи?

Ответы [ 2 ]

3 голосов
/ 29 июня 2011

Я полагаю, что вы ищете Экспортер или его многочисленные последующие модули.Я вижу, что вы уже используете его в своем модуле, но вы не используете его для получения %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 $@, но это имеет значение для безопасности. UNIVERSAL :: требуется или 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();
}
3 голосов
/ 29 июня 2011

Если вы хотите сделать имя класса динамическим, вы можете сделать что-то вроде этого:

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

require $class_file;
$class->import;

(Или даже лучше, используйте Module :: Load , как @ Шверн предлагает .

Получить класс %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;
}
...