http://www.perlmonks.org?node_id=692982

l2kashe has asked for the wisdom of the Perl Monks concerning the following question:

I have run into a situation with dynamically load available modules at runtime via eval. There is magic afoot I don't fully appreciate, so I am hoping someone can point to the right doc/direction on what is going on.

I have a fairly large codebase which needs to be deployed in a variety of situations/environments. Given the nature of the code, it can have an excessive number of module requirements to cover all the scenarios it can be leveraged for. Instead of doing some type of preinspection of the available modules and then reconfiguring the codebase on the fly or trying to download modules from CPAN, I wanted to wait until runtime and let user configurable data drive what modules should be loaded.

There are 2 main usage patterns, simply use()ing a module, and use()ing a module with a list of keywords to import into the application (IE. use Time::HiRes; vs use Time::HiRes qw(gettimeofday tv_interval). So a simple hash gets the trick done in terms of handling the data requirements The issue comes in with using eval causing keywords to not be imported into the namespace cleanly as far as I can tell.

After a number of posts about the syntax of the code not being valid I have replaced the code with the actual method being used. This is within a package which uses strict. I have added a third example which a contrived object using this information

#!/path/to/perl # This snippet works as expected use strict; use Time::HiRes qw(gettimeofday tv_interval); my $now = [gettimeofday]; sleep 2; # The below produces "Elapsed: 2.01233" print "Elapsed: ", tv_interval($now, [gettimeofday]), "\n";
#!/path/to/perl # This snippet *DOES NOT* work as expected use strict; my $toLoad = { 'CGI' => '1', 'Time::HiRes' => 'gettimeofday tv_interval', }; while ( my($module, $args) = each %$toLoad) { my $str = "use $module"; $str .= 'qw(' . $args . ')' if $args ne '1'; eval $str; # also tried eval {}, eval "", etc... } # END while each %$toLoad my $now = [gettimeofday]; sleep 2; # The below produces Elapsed: 0, print "Elapsed: ", tv_interval($now, [gettimeofday]), "\n";

Changing the line now = line to [gettimeofday()] it works, I assume by hinting to the runtime environment that this is in fact a routine and not a string.

So what is different within the eval expression that isn't within the standard use EXPR line?

#!/path/to/perl use strict; my $obj = {}; # For this example bless($obj, 'main'); # get some data via config and then my $status = $obj->loadPerlModule($configData); # Prints no warnings or errors under strict.. foreach my $module ( keys %$status ) { print "$module status: $status->{$module}\n"; } # This fails.. my $now = [gettimeofday]; sleep 2; # The below produces Elapsed: 0, print "Elapsed: ", tv_interval($now, [gettimeofday]), "\n"; # This succeeds my $newNow = [gettimeofday()]; sleep 2; # The below produces Elapsed: 2.02313, print "Elapsed: ", tv_interval($now, [gettimeofday()]), "\n"; # This is the actual method that loads the module. sub loadPerlModule { my $self = shift; my $modules = $self->asHash(@_); my $return = {}; foreach my $module ( keys %$modules ) { if ( $modules->{ $module } ne '1' ) { eval "use $module " . 'qw(' . $modules->{$module} . ');'; } else { eval "use $module"; } $return->{ $module } = ( $@ ) ? "Failed to use module: $@" : "Module loaded"; $@ = undef; } # END foreach my $module return $return; } # END sub loadPerlModule

use perl;
Everyone should spend some time pounding nails with their forehead for a while before they graduate to complicated stuff like hammers. - Dominus