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

I have a situation where I'm working on a module loader, that will be passed names of other Perl modules, or .pl packages, and optional release version numbers, that may or may not be in a standard Perl library path. The module's import method will look at each module name passed to it in it's LIST, and look through a series of known non-standard directory paths for that versioned module file. So the interface is something like:
# This is the location where the loader module will be # picked up. use lib qw( /net/public/tools/perl/lib ); use PmLoader qw( MyFunnyModule :1.5 someoldpackage.pl Some::Hierarchical::Module :2.1 );
The PmLoader will parse the module name, and a version number (if it exists) for that module. Otherwise, it will query our system to see if there is a "current" version for this modulename, and use it. Now these funny modules may be located in paths down from /net/public/tools/perl/lib that may contain the version number in the path, or the module name as a parent directory, in a known set of subdirectories. (These are the result of many years of haphazard development and installation methods.) For example:
/net/public/tools/perl/lib/MyFunnyModule/<version>/MyFunnyModule.pm /net/public/tools/perl/lib/MyFunnyModule/MyFunnyModule.pm,<version> /net/public/tools/perl/lib/pl-libs/someoldpackage.pl
But basically, after the PmLoader.pm module has found each of the correct modules (or .pl files), and pushed that path on @INC, I then need to load the module. So here's the rub: I need to do this:
BEGIN { require $module; }
Perl really doesn't like that variable name however in the BEGIN require block!! First of all, the variable $module set in the PmLoader sub import routine, isn't visible to the BEGIN{} block. And secondly, even if it was, I don't think it like seeing a variable there. What's the solution to loading module names that exist in variables?? -Cadphile

Replies are listed 'Best First'.
Re: use/require $MODULE (a variable)
by Zaxo (Archbishop) on Mar 16, 2002 at 02:54 UTC

    In BEGIN{ require $module; }, you need to replace '::' by '/' since the automatic replacement magic only occurs for barewords. See require for details. For import, the replacement is unwanted, since the package name is its argument.

    As for getting things defined before they're needed, you just have to make sure that happens. You'll wind up with a bigger BEGIN block.

    The use keyword is unusable for this, it truly doesn't accept a variable as a module name.

    After Compline,
    Zaxo

      I needed to test if a pile of modules were installed on my system, My solution (after reading this and looking elsewhere) was to use backtick and have perl write a series of scripts that would fail if the module wasn't installed.
      #!/usr/bin/perl @modnames = qw / CGI Test::Simple Foo::Bomb Test::More / ; print "Testing for Modules: @modnames\n\n" ; open STDERR, '>/dev/null' ; foreach $module ( @modnames ) { open OTHERSELF, '>REAL.pl' ; print OTHERSELF qq/use $module;\n print "succeeded $module\n" / ; # With SRDERR directed to devnull a value needs # to be returned to STDOUT, the print provides # a value that can be used in determining success. close OTHERSELF ; print "Trying $module\n" ; $result = `perl REAL.pl` ; if ( $result =~ m/succeeded/i ) { print "Successfully Loaded $module \n" } else { print "\n>>>> Failed to Load $module \n\n" } }

        Why don't you simply use eval?

        The friendly version using try-catch alias eval BLOCK needs a litte bit more typing than the quick-and-dirty eval STRING version, because require only converts barewords to filenames, but not variables.

        eval STRING restarts the Perl parser for each tested module. If the module is already loaded, that's more expensive than needed. If not, the Perl parser must be started anyway to load the module.

        #!/usr/bin/perl use strict; use warnings; # "Friendly" version using eval BLOCK foreach my $mod (qw(CGI Test::Simple Foo::Bomb Test::More)) { (my $fn="$mod.pm")=~s|::|/|g; # Foo::Bar::Baz => Foo/Bar/Baz.pm if (eval { require $fn; 1; }) { print "Module $mod loaded ok\n"; } else { print "Could not load $mod. Error Message: $@\n"; } }
        Returns:
        Module CGI loaded ok Module Test::Simple loaded ok Could not load Foo::Bomb. Error Message: Can't locate Foo/Bomb.pm in @ +INC (@INC contains: c:/strawberry/perl/lib c:/strawberry/perl/site/li +b .) at foo.pl line 6. Module Test::More loaded ok
        #!/usr/bin/perl use strict; use warnings; # "Quick-and-dirty" version using eval BLOCK foreach my $mod (qw(CGI Test::Simple Foo::Bomb Test::More)) { if (eval "require $mod; 1;") { print "Module $mod loaded ok\n"; } else { print "Could not load $mod. Error Message: $@\n"; } }
        Returns:
        Module CGI loaded ok Module Test::Simple loaded ok Could not load Foo::Bomb. Error Message: Can't locate Foo/Bomb.pm in @ +INC (@INC contains: c:/strawberry/perl/lib c:/strawberry/perl/site/li +b .) at (eval 5) line 1. Module Test::More loaded ok

        Note that there is a little difference in the error message.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: use/require $MODULE (a variable)
by lestrrat (Deacon) on Mar 16, 2002 at 03:52 UTC

    Why do you need to require() in a separate BEGIN block? the statement

    use PmLoader qw{ ...args... }

    is equivalent of

    BEGIN { require PmLoader; PmLoader->import( qw{ ...args... } ); }

    so you're already in a BEGIN block. Why not just require() everything at the end of import()?

    As to using require() with variable names.. you need to do either of these:

    require BAREWORD; require $filename; ## or require 'filename'

    So what happens when you want to do the equivalent of

    require CLASS::NAME;

    but the "CLASS::NAME" portion is in a variable? well, eval(), of course, but you need to use eval STRING, not eval BLOCK:

    my $module = "CLASS::NAME"; eval "require $module"; if( $@ ) { die $@; }

    If you do eval{ require $module }, then require would try to load a file named "CLASS::NAME", which most likely don't exist

    HTH

      Thanks all for your input. I have the require working OK now, although I have two questions:

      1). The two forms of require are:

      require MODULE; require "MODULE.pm";
      The first form (according to the book) translates '::' to '/' (on Unix systems), and also treats indirect object notation as methods, whereas the second form requires the '/' path separator and treats indirect object notation as basic function calls. So I really want the first form, so I need to call this with
      eval "require $module";
      Is this right?

      2). I realize though that I need to perform some trickery with the symbol tables, I think. What I want is my module PmLoader.pm to take it's LIST input, and then find and load each of THOSE modules into the main package. As I have it now, it's loading these into the PmLoader package, but not into main. So, as was pointed out, when I say

      use Pmloader qw( ...args ...);
      it's the same as saying
      BEGIN { require PmLoader; PmLoader->import( qw( ...args...)); }
      But then in PmLoader's import method, if I only call
      eval "require $module";
      I'm missing the import. But the import needs to be done to main, not to package PmLoader. Can anyone give me a hint how to proceed here?
      From the scriptorium annex,
      -cadphile

        Look into Exporter's export_to_level function. Somehting like this:

        # in your PmLoader module... sub import { ## do whatever you need to do, and then eval " require $module; $module->export_to_level(2); "; ## obivously, don't forget to check for errors }

        Update: On a second thought, not all modules have a export_to_level function, so it's really better to separate out those two statements into two eval() calls...

        eval "require $module"; ## check for errors... eval "$module->export_to_level(2)"; ## this one can/may fail, so may be just warn? ## or I guess you could check by doing ## ## $module->can( 'export_to_level' ); ##
        Just to restate what I just posted, I'm able to get through the eval "require $module"; without trouble, and @INC, and %INC are being appropriately updated.

        But!, if one of the loaded Modules uses Exporter to export its symbols into the caller package, the problem is that the caller package is not main! I.e. the Modules are not exporting their symbols into main. Hence, if I have a module that exports a subroutine symbol into main, if I

        use Module;
        directly, I can call the subroutine without a qualified package designator. But when my loader loads the Module, I can only call the subroutine with the fully qualified package name.

        Why isn't the Exporter working in this case to export the Module's symbols into main?

        thanks,
        -cadphile

Re: use/require $MODULE (a variable)
by erikharrison (Deacon) on Mar 16, 2002 at 03:03 UTC

    You can most definately use a variable - however, Perl will require that you append a file extension if you do.

    $foo = "CGI"; require $foo; #this will bork $foo = "CGI.pm"; require $foo; #this won't

    You just need to make sure that the variable is visible and assigned an appropriate value when used. To do this, wrap the BEGIN block around the sub AND around the sub - other wise Perl won't see the declaration or the use.

    BEGIN { sub which_finds_modules { #stuff goes here } @modules = which_finds_modules; foreach $mod(@modules) { require $mod; #make sure it has the extension! } }
    Cheers,
    Erik