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

Damn it's tough searching the web with terms like "perl", "dynamic", "use", etc...

Anyway, I'd like to know if it's possible to do the following:
$dynamic_configuration = 'CONF-A'; use $dynamic_configuration;
Then my PERL program would have the variables and methods contained in CONF-A.pm at least this is what I would like to happen...rather than the compile errors.

...BUT...that doesn't seem to work, so is there a way to accomplish this?

Here are some ideas I've thought of already:
if ($A) { use CONF-A; } elsif ($B) { use CONF-B; } elsif ($C) { use CONF-C; }
I'd rather not have to change all my programs to add CONF-D, E, and F.

The other possibility is that there would be a CONF.pm file containing:
%conf = (CONF-A => 'foo', etc..., CONF-B => 'bar', etc..., CONF-C => 'baz', etc...);
The problem with that is that the CONF.pm file can get very large and I would prefer to keep such a large configuration out of memory and not have such a monster to edit.

Would it be possible to use "require" instead of "use"? I'm not entirely sure how that would work, I thought maybe I could do this:
$x = 'A'; eval "require CONF-$x"; import ????? qw(var1 var2...);
But the ????? part trips me up.

...ack...

Anyone have any pointers or solutions to this?

THANKS!

Tosh

Replies are listed 'Best First'.
Re: Are dynamic 'use' statements possible?
by suaveant (Parson) on Jul 01, 2002 at 20:25 UTC
    It is very possible... here is a snippet of code I use to load a library which mimics Net::FTP, and if that is not found tries to load Net::FTP directly...
    BEGIN { $^W = 0; eval "use Net::Gnift qw(Net::FTP);"; if($@) { eval "use Net::FTP;"; if($@) { print qq`Content-Type: text/html\n\n<HTML><BODY>You do not + have Net: :FTP installed. You need it to use Web-FTP. You can find it on <A HREF +="http://s earch.cpan.org/">CPAN</A></BODY></HTML>`; die "Net::FTP not found in \@INC: @INC"; } } }
    Of course, that is in a begin block, if you want to do it at run time I believe the same thing is in effect, run an eval with the use in a string, check $@ for errors...

    In that case you can even build strings to do a use on... you could probably also pull it off somehow with require and import, but I'm not sure how...

                    - Ant
                    - Some of my best work - (1 2 3)

Re: Are dynamic 'use' statements possible?
by Juerd (Abbot) on Jul 01, 2002 at 20:33 UTC

    use Foo::Bar qw(foo bar baz);
    is equal to
    BEGIN { require 'Foo/Bar.pm'; Foo::Bar->import(qw(foo bar baz)); }
    This is documented in perlfunc, item use.

    Another example:

    BEGIN { my ($module, @imports) = qw(Foo::Bar foo bar baz); (my $file = "$module.pm") =~ s[::][/]g; require $file; $module->import(@imports); }
    Update - fixed typo. Thank you, Matts.

    - Yes, I reinvent wheels.
    - Spam: Visit eurotraQ.
    

      Typo:

      (my $file = "$module.pm") =~ s[::][/]g;

      Note the "=~", not "=". The worst thing about this is it will compile it just fine with it as an equals sign {1}, and simply do the s/// on the $_ variable, and then assign it's return value to $file. Not fun trying to locate that sort of bug.

      {1} Yes, even with use strict and warnings on.

      But wouldn't the problem with this be that $module is only a STRING which would prevent the import() call, no?

      I think Aristotle has the solution below...
Re: Are dynamic 'use' statements possible?
by robobunny (Friar) on Jul 01, 2002 at 20:31 UTC
    for checking at runtime, i typically do this:
    if(eval "require Term::ReadKey") { import Term::ReadKey; }
Re: Are dynamic 'use' statements possible?
by Abigail-II (Bishop) on Jul 02, 2002 at 09:34 UTC
    I would certainly not do the require in an eval, and then not inspect $@. Because in your code fragment, what's going to happen if CONF-$x contains code that doesn't compile? The require would die, but you trap it without dealing with it. I'd use something like this:
    my $x = "A"; require "CONF-$x.pm"; # Note the .pm! "CONF-$x" -> import if "CONF-$x" -> can ("import");

    Abigail

Re: Are dynamic 'use' statements possible?
by ehdonhon (Curate) on Jul 01, 2002 at 22:12 UTC

    The following code may be found in Term::ReadLine:

    my ($which) = exists $ENV{PERL_RL} ? split /\s+/, $ENV{PERL_RL} : unde +f; if ($which) { if ($which =~ /\bgnu\b/i){ eval "use Term::ReadLine::Gnu;"; } elsif ($which =~ /\bperl\b/i) { eval "use Term::ReadLine::Perl;"; } else { eval "use Term::ReadLine::$which;"; } } elsif (defined $which and $which ne '') { # Defined but false # Do nothing fancy } else { eval "use Term::ReadLine::Gnu; 1" or eval "use Term::ReadLine::Perl; + 1"; }

    I think this is similar to what you are looking for.

Re: Are dynamic 'use' statements possible?
by bronto (Priest) on Jul 01, 2002 at 20:36 UTC

    Well, since evaluation of use happens at compile time, you should try something with eval to make it work. But it is an approach that I would use to load third parties' modules on the fly, not mine

    Since you described a situation where you are writing your own configuration, I suggest to read perlmod and the Exporter module's documentation. There you should find how to write modules that export variables in your program's namespace. And, since you write them, you can make them work with a simple require, which is evaluated at runtime

    disclaimer: I wouldn't do the job that way, but you look like searching for the simplest solution, not the best one :-)

    Ciao!
    --bronto

    # Another Perl edition of a song:
    # The End, by The Beatles
    END {
      $you->take($love) eq $you->made($love) ;
    }

Re: Are dynamic 'use' statements possible?
by tomhukins (Curate) on Jul 01, 2002 at 22:34 UTC
    In addition to the advice already given, you might find the discussions I referred to in Re: Plug-in mechanism implemented in Perl useful if you're interested in pulling in code at run-time according to certain criteria.
Re: Are dynamic 'use' statements possible?
by Aristotle (Chancellor) on Jul 02, 2002 at 01:34 UTC
    The following snippet avoids all the manual module->filename munging. (An interesting property is the nesting of several compile- and execution phases..)
    BEGIN { my $m = "File::Spec::Functions"; eval "use $m;" } print catfile qw(a b c);
    ____________
    Makeshifts last the longest.
      This seems to be exactly the solution I'm after, however I wonder about the consequences in a mod_perl context...

      Say I have a module called DO_STUFF.pm, and DO_STUFF has the following code in it:
      package DO_STUFF.pm; BEGIN { use strict; use Exporter; use vars qw(@ISA @EXPORT); my $conf = determine_conf_module_name(); eval "use $conf;"; } do_stuff....etc...
      Now I load DO_STUFF.pm into memory for mod_perl and at loading time I would assume that $conf is determined and that module is now loaded into memory space for use by DO_STUFF.

      But what happens when I am using DO_STUFF in a context that changes what determine_conf_module_name() returns, i.e. a new $conf, how would DO_STUFF.pm (already loaded into memory) know to use a different module?

      Would it be better to execute
      my $conf = determine_conf_module_name(); eval "use $conf;";
      during instantiation, like this:
      package DO_STUFF.pm; use strict; use Exporter; use vars qw(@ISA @EXPORT); ... sub new { my $conf = determine_conf_module_name(); eval "use $conf;"; ... }
      Or is the BEGIN{} block run only during instantiation of the object so I'm fine...?

      oooOOOOooo....my head is all twisty now...

      THANKS ALL!!!

      Tosh

        Oh la la.

        To answer your questions, yes, BEGIN {} only execute once when the module is compiled, and yes, you can use eval "use $module;"; in your new(). However (!!), how is your configuration module set up? Do you import variables into your own namespace from it? Or do the configuration modules share one namespace? In that case, loading a configuration module would affect all instances of your class.. and you're looking at a major mess.

        Is there any reason you can't simply use an honest to god configuration file with something like Config::Inifiles, or at least my $config = do "config_in_anonymoushash.pl";?

        Makeshifts last the longest.