Как я могу динамически включать модули Perl без использования eval?
Мне нужно динамически включать модуль Perl, но, если возможно, хотелось бы избегать eval из-за стандартов рабочего кодирования. Это работает:
$module = "My::module";
eval("use $module;");
Но мне нужен способ сделать это без eval
, если это возможно. Все поисковые запросы Google приводят к методу eval
, но ни в коем случае.
Можно ли это сделать без eval
?
Ответы
Ответ 1
Используйте require
для загрузки модулей во время выполнения. Часто рекомендуется обернуть это в блок (не строку) eval
, если модуль не может быть загружен.
eval {
require My::Module;
My::Module->import();
1;
} or do {
my $error = [email protected];
# Module load failed. You could recover, try loading
# an alternate module, die with $error...
# whatever appropriate
};
Причина синтаксиса eval {...} or do {...}
и создание копии [email protected]
заключается в том, что [email protected]
- это глобальная переменная, которая может быть задана многими разными способами. Вы хотите получить значение как можно более атомарным, чтобы избежать условия гонки, когда что-то еще установило его на другое значение.
Если вы не знаете имя модуля до выполнения, вам придется вручную выполнить перевод между именем модуля (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 = [email protected];
# ...
};
Ответ 2
Как насчет использования основного модуля Модуль:: Загрузка
В вашем примере:
use Module::Load;
my $module = "My::module";
load $module;
"Модуль:: Загрузка - время выполнения как модулей, так и файлов"
"устраняет необходимость знать, пытаетесь ли вы требовать либо файл, либо модуль."
Если он терпит неудачу, он умрет с чем-то вроде "Невозможно найти xxx в @INC (@INC содержит:...".
Ответ 3
Ну, всегда require
, как в
require 'My/Module.pm';
My::Module->import();
Обратите внимание, что вы теряете любые эффекты, которые вы, возможно, получили от import
, вызываемых во время компиляции, а не время выполнения.
Изменить: компромиссы между этим и eval образом: eval позволяет использовать стандартный синтаксис модуля и дает более явную ошибку, если имя модуля недействительно (в отличие от просто не найденного). OTOH, метод eval (потенциально) более подвержен произвольной инъекции кода.
Ответ 4
Нет, это невозможно без eval
, так как require()
требуется имя модуля bareword, как описано в perldoc -f require. Однако это не злое использование eval, так как оно не позволяет вводить произвольный код (при условии, что вы, конечно, контролируете содержимое файла, который вы require
ing).
EDIT: Код изменен ниже, но я оставляю первую версию для полноты.
Я использую Я использовал этот маленький модуль сахара для выполнения динамических нагрузок во время выполнения:
package MyApp::Util::RequireClass;
use strict;
use warnings;
use Exporter 'import'; # gives you Exporter 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: [email protected]" };
}
1;
PS. Я смотрю на это определение (я написал это довольно давно), и я размышляю над добавлением этого:
$class->export_to_level(1, undef, @imports);
... он должен работать, но не проверен.
EDIT: версия 2 теперь гораздо приятнее без eval (спасибо ysth)::)
package MyApp::Util::RequireClass;
use strict;
use warnings;
use Exporter 'import'; # gives you Exporter 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;
Ответ 5
Класс:: MOP на CPAN имеет для этого метод load_class:
http://metacpan.org/pod/Class::MOP
Ответ 6
Мне нравится делать такие вещи, как..
require Win32::Console::ANSI if ( $^O eq "MSWin32" );