I would assume that the error actually comes from your second eval. It is actually the same as:
eval $module::run . "()";
interpolating the $run variable from the module:: package. You should write this as
eval "${module}::run()";
| [reply] [d/l] [select] |
| [reply] |
eval "$module\::run()";
| [reply] [d/l] |
A while ago I wrote a plugin factory where it returns a new object based on a name passed. Here is the core of the routine:
$module =~ s/\.(pm|PM)$//;
my $mainmodule = PLUGIN_BASE . $module . ".pm";
require $mainmodule;
import $mainmodule;
return $module->new();
Where $module was passed in and PLUGIN_BASE is just the dir where I keep the modules.
I then did this in the main program
eval{
my $obj = $facory->get_instance('thingy');
$obj->run();
$obj->closedown();
};
if($@)
{
print $@;
}
On a different note, if anyone spots something wrong with this then please do let me know. This is something that I got working and then left well alone ;)
HTH
SP | [reply] [d/l] [select] |
Perhaps you should rethink your approach, and go for require instead of use, and use an OO approach, even though you have no objects, by calling the subs as class methods. That way, you can get rid of the eval, and even have it pass strict without problem.
One problem when calling require with a variable scalar, is that the format must be different: the magic conversion you get when writing require Foo::Bar; no longer works: you have to convert 'Foo::Bar' to 'Foo/Bar.pm' yourself. So this should, basically, work:
use strict;
our $module = 'Foo::Bar';
my $file = join('/', split /::/, $module) . '.pm';
require $file;
$module->run();
Note that any class method ("run") you call this way, will get one extra argument prepended, in front of any of your own: the class name, the string that's currently in $module.
p.s. For reasons of sanity, I personally would prefer an extra root in front of the actual module names, for example 'MyDrivers' (bad example :-), so you actually load and run MyDrivers::Foo::Bar, if the user supplies the string 'Foo::Bar'. That way, you can make sure you can only load modules that are especially prepared to be used this way.
p.p.s. People using older perls than 5.6.0 should replace the 'our' with another appropriate solution (use vars or my), or simply not use strict :-). This has nothing to do with the functionality of the code I described here. I would think that should still work.
| [reply] [d/l] [select] |
Just thought I'd add that, as far as I know, 'use' happens at compile time only, so you have to 'require' modules at runtime. Also, for OO modules, is there any need to do an import?
| [reply] |
Well, that depends on what the module wants to do, doesn't?
I sometimes let OO modules export constants, or a subroutine
that returns an object. And there are other things you
might want to do in an import.
Abigail
| [reply] |
I'm sure others are already pointing this out, but the second eval line uses double quotes, so an interpolation is being done before eval "" even sees it.
My preferred form is:
if (defined $module)
{
# Detaint
$module =~ m/^(My_Safe_Namespace(?:\:\:\w+)*)$/
or die "Unsafe module name: $module\n";
$module = $1;
eval "use $1;";
die if $@;
# Call 'run' as a method to avoid eval-string
$module->run();
}
Eval-string is generally a bad idea for security reasons, assuming you're relying on data external to the program. This is just a way of minimising its use.
| [reply] [d/l] |
I can't answer your question (perhaps jand is right). Just as a side note you might try require in place of use - and you would not need the eval. | [reply] |
| [reply] |