Jak mogę dynamicznie dołączać Moduły Perla bez użycia eval?

Muszę dynamicznie dołączyć moduł Perla, ale jeśli to możliwe, chciałbym trzymać się z dala od evalu ze względu na Robocze standardy kodowania. To działa:

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

Ale potrzebuję sposobu, aby to zrobić bez eval jeśli to możliwe. Wszystkie wyszukiwania google prowadzą do metody eval, ale żadne w inny sposób.

Czy można to zrobić bez eval?

Author: brian d foy, 2009-12-16

6 answers

Użycie require aby załadować Moduły w czasie wykonywania. Często dobrym pomysłem jest zawinięcie tego w blok (Nie łańcuch) eval na wypadek, gdyby moduł nie mógł zostać załadowany.

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
};

Powodem składni eval {...} or do {...} i tworzenia kopii $@ jest to, że $@ jest zmienną globalną, która może być ustawiona przez wiele różnych rzeczy. Chcesz pobrać wartość tak atomicznie, jak to możliwe, aby uniknąć stanu wyścigu, w którym coś innego ustawiło ją na inną wartość.

Jeśli nie znasz nazwy modułu do czasu uruchomienia będziesz musiał ręcznie tłumaczyć nazwę modułu (My::Module) i nazwę pliku (My/Module.pm):

my $module = 'My::Module';

eval {
    (my $file = $module) =~ s|::|/|g;
    require $file . '.pm';
    $module->import();
    1;
} or do {
    my $error = $@;
    # ...
};
 44
Author: Michael Carman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-16 20:58:26

Może skorzystasz z modułu podstawowego Module:: Load

Z twoim przykładem:

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

"Module:: Load-Runtime require of both modules and files"

"load eliminuje potrzebę wiedzieć, czy próbujesz wymagać pliku lub modułu."

Jeśli się nie powiedzie, umrze z czymś podobnym " nie można zlokalizować xxx w @INC (@INC zawiera: ...".

 16
Author: DavidG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-01 14:46:52

Cóż, zawsze jest require jak w

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

Zauważ, że tracisz wszelkie efekty, które możesz uzyskać z wywołania import w czasie kompilacji zamiast trybu runtime.

Edit: kompromis pomiędzy tą A eval jest następujący: eval pozwala na użycie zwykłej składni modułu i daje bardziej wyraźny błąd, jeśli nazwa modułu jest nieprawidłowa (w przeciwieństwie do zwykłego Nie odnalezionego). OTOH, sposób ewaluacji jest (potencjalnie) bardziej uzależniony od arbitralnego wtrysku kodu.

 10
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-16 20:15:35

nie, nie jest możliwe Bez eval, ponieważ require() wymaga nazwy modułu bareword, jak opisano w perldoc-f require. Jednak nie jest to złe użycie eval, ponieważ nie pozwala na wstrzyknięcie dowolnego kodu(zakładając, że masz kontrolę nad zawartością pliku, którym jesteś require ing, oczywiście).

EDIT: Kod poprawiony poniżej, ale zostawiam pierwszą wersję dla kompletności.

używam używałam tego małego cukru moduł do wykonywania obciążeń dynamicznych w czasie pracy:

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. Wpatruję się w tę definicję (pisałem ją już jakiś czas temu) i zastanawiam się nad dodaniem tej: $class->export_to_level(1, undef, @imports);... to powinno działać, ale nie jest testowane.

EDIT : Wersja 2 teraz, dużo ładniejsza bez ewaluacji (dzięki 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;
 4
Author: Ether,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-16 21:20:10

Class:: MOP na CPAN ma do tego metodę load_class: http://metacpan.org/pod/Class::MOP

 1
Author: Terrence Brannon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-05 20:55:13

Lubię robić takie rzeczy..

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

 0
Author: ShoeLace,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-06 23:31:59