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

I have package Foo.

Package Foo has sub-packages Foo::Bar and Foo::Baz.

When I do

my $foo = Foo->new qw( Foo::Bar Foo::Baz );
I load those modules:
my @submodules = @_; for ( @submodules ) { eval "require $_" || die "$!"; }
as part of new {}.

But I can't figure out how to abbreviate the submodule names to just "Bar" and "Baz" and still have it work.

For instance I would like to do

my $foo = Foo->new qw( Bar Baz );
and have new {} do this:
for ( @submodules ) { eval "require Foo::$_" || die "$!"; }
or similar. But when I do it that way, although it doesn't die, the submodule's methods don't seem to be available.

This works:

package Foo; sub new { my $class = shift; my @submodules = @_; for ( @submodules ) { eval "require $_" || die "$@"; } bless { submodules => \@submodules }, $class; } 1;

package Foo::Bar; sub details { return { name => 'Simon', job => 'doctor', }; } 1;

#!/usr/bin/perl use Foo; my $foo = Foo->new qw(Foo::Bar Foo::Baz ); for ( @{ $foo->{'submodules'} } ) { print $_->details->{'name'} . " is a " . $_->details->{'job'} . $/; } # prints: Simon is a doctor


This doesn't work:

package Foo; sub new { my $class = shift; my @submodules = @_; for ( @submodules ) { eval "require Foo::$_" || die "$@"; } bless { submodules => \@submodules }, $class; } 1;

package Foo::Bar; sub details { return { name => 'Simon', job => 'doctor', }; } 1;

#!/usr/bin/perl use Foo; my $foo = Foo->new qw( Bar ); for ( @{ $foo->{'submodules'} } ) { print $_->details->{'name'} . " is a " . $_->details->{'job'} . $/; } # prints: Can't locate object method "details" via package "Bar" # (perhaps you forgot to load "Bar"?)


($_='kkvvttuu bbooppuuiiffss qqffssmm iibbddllffss')
=~y~b-v~a-z~s; print

Replies are listed 'Best First'.
Re: Loading Modules At Run-Time With Interpolation
by Tanktalus (Canon) on Aug 12, 2005 at 04:23 UTC

    With your updates, I see the problem. You're using the package names without the Foo:: prefix. You're probably best off Foo-izing your submodules during the load and then from there on in, you don't need to worry about it:

    package Foo; sub new { my $class = shift; my @submodules = map { "Foo::$_" } @_; for ( @submodules ) { eval "require $_" || die "$@"; } bless { submodules => \@submodules }, $class; } 1;
    Hope that helps,

      I AM using them with the Foo prefix, in the section under "This doesn't work".


      ($_='kkvvttuu bbooppuuiiffss qqffssmm iibbddllffss')
      =~y~b-v~a-z~s; print
      ...but, having said that, your method works! Thanks!

      So my question is now -- how come "require String::$varable" doesn't work but if you do that map on the array to add the String:: part first, it does?



      ($_='kkvvttuu bbooppuuiiffss qqffssmm iibbddllffss')
      =~y~b-v~a-z~s; print

        You are misdiagnosing the problem. The problem isn't the require. It's where you try to use the details function. For example, both of these will work:

        my $p = 'Foo::Bar'; eval "require $p"; print $p->details->{name}; # ... and ... my $p = 'Bar'; eval "require Foo::$p"; print "Foo::$p"->details->{name};
        By merging Foo:: directly into your submodules list, we don't need to constantly recreate the full package name each time - it's already created.

Re: Loading Modules At Run-Time With Interpolation
by Tanktalus (Canon) on Aug 12, 2005 at 01:56 UTC

    Your function looks correct to me. Is it something you are just postulating here, or did you try it and get some sort of error message or condition?

    (my $pm = $module) =~ s.::./.g; $pm .= '.pm'; eval { require $pm }

    Shouldn't matter if $module is hardcoded or built up from pieces. I actually use this type of idea already in some of my code where the base package is settable in one location, making specifying sub-packages much easier, as well as changing the root-level package.

Re: Loading Modules At Run-Time With Interpolation
by tlm (Prior) on Aug 12, 2005 at 03:23 UTC

    Backslash the first colon within double quotes. Also, after eval, check for $@, not $!. E.g.:

    eval "require Foo\::$_" || die $@;

    Update: On closer inspection, the lack of backslash is not a problem (I was thinking of pitfalls like "$foo::$bar"; but do check for $@ after eval!). FWIW, your code works fine for me.

    # Foo/Quux.pm package Foo::Quux; print 'Loading ', __PACKAGE__, "\n";

    use strict; use warnings; for ( 'Quux' ) { eval "require Foo::$_" || die $@; } __END__ Loading Foo::Quux

    the lowliest monk

      FWIW, your code works fine for me.

      Sorry, I should have been more specific. It doesn't die when I do it, but it doesn't work as I expect it to. I'll add it my example code to the original post.



      ($_='kkvvttuu bbooppuuiiffss qqffssmm iibbddllffss')
      =~y~b-v~a-z~s; print