Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Dynamically wrapping multiple modules

by BigLug (Chaplain)
on Apr 19, 2004 at 08:11 UTC ( [id://346222]=perlquestion: print w/replies, xml ) Need Help??

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

I have a module that takes a list of country codes in its constructor. Each of these codes could (should) be another module that just contains a hash (%MyModule::AU::Hash, %MyModule::FR::Hash etc). I have several questions:

1. Is there a 'best' or simple way to load all the modules? I figure an evaled require in a loop. However I don't want to eval user input. Can anyone suggest a better way to do it?

foreach my $cc (@country_code) { eval( "require MyModule::$cc" ); croak("Unable to locate MyModule::$cc") if $@; }
2. Once they're loaded, how should I access them? At several points in my code I want to use the hash. One thought was to use eval to import the hash at load time, however I'm still sending user input to eval!:
foreach my $cc (@country_code) { eval( "require MyModule::$cc" ); croak("Unable to locate MyModule::$cc") if $@; push( @{$self->{data}}, { eval( "\%MyModule::$cc::Hash" ) } ); }
However I'd prefer not to import it all if I can. I'd prefer to store a reference to the hash in my object or even to grab the data as needed. But I don't want to fill my module with eval()s Thoughts and comments welcome.

Update: I can't validate the user input with known values. However I can validate it using a regex: croak if $cc !~ /^[a-z]{2}$/i

"Get real! This is a discussion group, not a helpdesk. You post something, we discuss its implications. If the discussion happens to answer a question you've asked, that's incidental." -- nobull@mail.com in clpm

Replies are listed 'Best First'.
Re: Dynamically wrapping multiple modules
by kvale (Monsignor) on Apr 19, 2004 at 08:56 UTC
    eval is ok if you can verify the user input. I would recommend creating a hash of allowed input, i.e., country codes:
    my %code = ( au => 'AU', fr => 'FR' ); foreach my $cc (@country_code) { croak("Country code $cc is not available") unless exists $code{$cc} +; eval( "require MyModule::$code{$cc}" ); croak("Unable to locate MyModule::$code{$cc}") if $@; }

    -Mark

      I should point out that I don't have the full country list. I'm not too keen on keeping it up to date with the full ISO country list ! However I guess I can validate it with a regex: croak if $cc!~/^[a-z]{2}$/i
      "Get real! This is a discussion group, not a helpdesk. You post something, we discuss its implications. If the discussion happens to answer a question you've asked, that's incidental." -- nobull@mail.com in clpm
Re: Dynamically wrapping multiple modules
by broquaint (Abbot) on Apr 19, 2004 at 17:56 UTC
    The likes of Module::Locate would seem appropriate here
    use Module::Locate 'locate'; require $_ for locate map "MyModule::$_", @country_code;
    Then to access the symbol tables
    @{ $self->{data} } = map \%{ $MyModule::{"$_\::"}{Hash} }, @country_code;
    Both code examples neatly avoid eval STRING and comply with strictures.
    HTH

    _________
    broquaint

Re: Dynamically wrapping multiple modules
by ysth (Canon) on Apr 19, 2004 at 15:48 UTC
    Assuming you don't have control of the modules themselves, this is a valid case for symbolic refs:
    $ref_to_desired_hash = do { no strict "refs"; \%{"MyModule::$cc::Hash" +} };
    or the symbol table lookup equivalent:
    $ref_to_desired_hash = \%{$MyModule::{$cc."::"}{Hash}};
    If you do have control of the modules, have each one export the hash to a single name, and just eval "use MyModule::$cc".
Re: Dynamically wrapping multiple modules
by Ryszard (Priest) on Apr 20, 2004 at 08:40 UTC
    I rolled my own, integrated with Net::Server::PreFork.
    sub post_configure_hook { my $self = shift; $self->logObj( Logger->new(name => 'log/gaa') ); $self->xml( new XML::Simple(xmldecl => 1,forcearray => 1) ); # Go find all the modules find ({ no_chdir => 1, wanted => sub { $self->load_api_modules($File::Find::name) +}, follow =>1 }, 'api'); } # Load and create instances of each module in the search path sub load_api_modules{ my $self = shift; my $filename = shift; if ($filename =~ /\.pm$/) { my @file = split(/\//, $filename); require "$filename"; my @name = split(/\./, $file[$#file]); $self->{ "_$file[1]_$file[2]" } = $name[0]->new; } }
    In my case, the input and the module name are the same. I grab an XML stream, determine the module to use, then call it via self.

    It works well in this case, as it goes and dynamically finds any modules in the path, then loads them, so you dont need to know (explicitily) what is available.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://346222]
Approved by davido
Front-paged by davido
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (5)
As of 2024-04-25 08:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found