Как я могу динамически включать модули Perl без использования eval? - PullRequest
26 голосов
/ 16 декабря 2009

Мне нужно динамически включить модуль Perl, но, если возможно, хотелось бы держаться подальше от eval из-за стандартов рабочего кодирования. Это работает:

$module = "My::module";
eval("use $module;");

Но мне нужен способ сделать это без eval, если это возможно. Все поиски в Google приводят к методу eval, но никаким другим способом.

Возможно ли это сделать без eval?

Ответы [ 6 ]

48 голосов
/ 16 декабря 2009

Используйте require для загрузки модулей во время выполнения. Часто бывает полезно заключить это в блок (не в строку) eval, если модуль не может быть загружен.

eval {
    require My::Module;
    My::Module->import();
    1;
} or do {
   my $error = $@;
   # Module load failed. You could recover, try loading
   # an alternate module, die with $error...
   # whatever's appropriate
};

Причина синтаксиса eval {...} or do {...} и создания копии $@ заключается в том, что $@ - это глобальная переменная, которую можно задавать множеством разных вещей. Вы хотите получить это значение как можно более атомарно, чтобы избежать состояния гонки, когда что-то еще установило другое значение.

Если вы не знаете имя модуля до времени выполнения, вам придется выполнить перевод между именем модуля (My :: Module) и именем файла (My / Module.pm) вручную:

my $module = 'My::Module';

eval {
    (my $file = $module) =~ s|::|/|g;
    require $file . '.pm';
    $module->import();
    1;
} or do {
    my $error = $@;
    # ...
};
16 голосов
/ 01 ноября 2012

Как насчет использования основного модуля Module :: Load

С вашим примером:

use Module::Load;
my $module = "My::module";
load $module;

«Module :: Load - требования времени выполнения как для модулей, так и для файлов»

«загрузка избавляет от необходимости знать, пытаетесь ли вы потребовать файл или модуль.»

Если это не удастся, он умрет с чем-то вроде "Не удается найти xxx в @INC (@INC содержит: ..." .

10 голосов
/ 16 декабря 2009

Ну, всегда есть require как в

require 'My/Module.pm';
My::Module->import();

Обратите внимание, что вы теряете любые эффекты, которые вы могли получить от import, вызываемого во время компиляции, а не во время выполнения.

Edit: компромисс между этим и eval способом: eval позволяет вам использовать обычный синтаксис модуля и дает вам более явную ошибку, если имя модуля неверно (в отличие от просто не найден). OTOH, eval-путь (потенциально) более подвержен произвольному внедрению кода.

4 голосов
/ 16 декабря 2009

Нет, без eval невозможно, так как require() требуется имя модуля голого слова, как описано в perldoc -f require . Однако использование eval не является злым, поскольку не допускает внедрения произвольного кода (конечно, при условии, что вы контролируете содержимое файла, который вы require используете). РЕДАКТИРОВАТЬ: Код исправлен ниже, но я оставляю первую версию для полноты.

Я использую Я использовал этот маленький сахарный модуль для динамических нагрузок во время выполнения:

package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter's import() method directly
our @EXPORT_OK = qw(requireClass);

# Usage: requireClass(moduleName);
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports);
sub requireClass
{
    my ($class) = @_;
    eval "require $class" or do { die "Ack, can't load $class: $@" };
}

1;

PS. Я смотрю на это определение (я написал это довольно давно), и я размышляю над тем, чтобы добавить это: $class->export_to_level(1, undef, @imports); ... это должно работать, но не тестируется.

РЕДАКТИРОВАТЬ : версия 2 сейчас, намного лучше без Eval (спасибо YSTH)::)

package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter's import() method directly
our @EXPORT_OK = qw(requireClass);

# Usage: requireClass(moduleName);
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports);
sub requireClass
{
    my ($class) = @_;

    (my $file = $class) =~ s|::|/|g;
    $file .= '.pm';
    require $file;  # will die if there was an error
}

1;
1 голос
/ 23 июня 2011

Class :: MOP в CPAN имеет метод load_class для этого: http://metacpan.org/pod/Class::MOP

0 голосов
/ 07 ноября 2012

Мне нравится делать такие вещи, как ..

require Win32::Console::ANSI if ( $^O eq "MSWin32" );

...