tags:

views:

530

answers:

3

Hi,

I need to dynamically include a Perl module, but if possible would like to stay away from eval due to work coding standards. This works:

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

But I need a way to do it without eval if possible. All google searches lead to the eval method, but none in any other way.

Is it possible to do it without eval?

+11  A: 

Use require to load modules at runtime. It often a good idea to wrap this in a block (not string) eval in case the module can't be loaded.

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

The reason for the eval {...} or do {...} syntax and making a copy of $@ is because $@ is a global variable that can be set by many different things. You want to grab the value as atomically as possible to avoid a race condition where something else has set it to a different value.

If you don't know the name of the module until runtime you'll have to do the translation between module name (My::Module) and file name (My/Module.pm) manually:

my $module = 'My::Module';

eval {
    (my $file = $module) =~ s|::|/|g;
    require $file . '.pm';
    $module->import();
    1;
} or do {
    my $error = $@;
    # ...
};
Michael Carman
Hmm. This is good because you wrap the require in an eval, but your require isn't particularly dynamic. If one could merge answers, Dan's and your's would be my top candidates.
innaM
While his require wasn't dynamic, I was easily able to make it so (as Picard would say).Thanks for the answer!
This is not very threadsafe, e.g. see http://www.perlfoundation.org/perl5/index.cgi?exception_handling
Ether
@Ether: The standard advice is to make checking `$@` the very first thing you do, but you're right that there's a race condition. I'll update the answer.
Michael Carman
@Ether: You're right to say it's not safe, (before the edit) but it's not related to threads.
tsee
`s@'|::@/@g` -- for hysterical raisins, `'` and `::` are interchangeable as package delimiters.
ephemient
Anyone who uses `'` as a package delimiter outside of a JAPH deserves to have their code break.
Michael Carman
http://search.cpan.org/perldoc?Acme::Don't ;)
ephemient
+4  A: 

Well, there's always require as in

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

Note that you lose whatever effects you may have gotten from the import being called at compile time instead of runtime.

Edit: The tradeoffs between this and the eval way are: eval lets you use the normal module syntax and gives you a more explicit error if the module name is invalid (as opposed to merely not found). OTOH, the eval way is (potentially) more subject to arbitrary code injection.

Dan
Thanks for the answer. I voted you up since I can't select both answers :).
+2  A: 

No, it's not possible to without eval, as require() needs the bareword module name, as described at perldoc -f require. However, it's not an evil use of eval, as it doesn't allow injection of arbitrary code (assuming you have control over the contents of the file you are requireing, of course).

EDIT: Code amended below, but I'm leaving the first version up for completeness.

I use I used to use this little sugar module to do dynamic loads at runtime:

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. I'm staring at this definition (I wrote it quite a while ago) and I'm pondering adding this: $class->export_to_level(1, undef, @imports);... it should work, but is not tested.

EDIT: version 2 now, much nicer without an eval (thanks 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;
Ether
"require() needs the bareword module name" - no, it doesn't; just s#::#/#g; and append '.pm' to get a pathname.
ysth
@ysth: wow, you're right... the docs are very vague about this but it does indeed work. Well that's splendid, now I can revise my module to eliminate that evil eval :)
Ether