tags:

views:

135

answers:

5

Hi Guys,

Suppose the module AAA::BBB::CCC located in ~/modules/AAA/BBB/CCC.pm, and "~/modules" is in @INC, so why the following code doesn't work and lead to compile error?

$class = "AAA::BBB" ;
$type = "CCC";
require $class . '::' . $type ;

I try to use require AAA::BBB::CCC instead, it works . If I do need dynamically require a module by combining strings together rather than hardcode the module name directly, how should I do ?

thanks

+4  A: 

From perldoc -f require:

But if you try this:

$class = 'Foo::Bar';
require $class; # $class is not a bareword
#or
require "Foo::Bar"; # not a bareword because of the ""

The require function will look for the "Foo::Bar" file in the @INC array and will complain about not finding "Foo::Bar" there. In this case you can do:

eval "require $class";

Therefore, you can try this instead:

$class = "AAA::BBB";
$type = "CCC";
eval qq{ require "${class}::${type}" };
Andrew Barnett
+1 for eval usage. This is how I do it.
Xetius
-1 for eval usage on string.
depesz
@depesz There is no need to downvote this answer. This is how the documentation for `require` tells you to provide a non-bareword argument to `require`.
Sinan Ünür
@Sinan I'm definitely not agreeing with docs on this one. I hope it's still allowed :) The problem I see with it is that it shows valid usage for something that is generally a bad idea. And since this particular case doesn't actually **require** string-based-eval, I think it's much better not to justify it by "docs told me to do it this way".
depesz
@depesz Can you explain why you think this is a bad idea? Security? Performance?Also, I have no problem with the down vote, except: too often (the 80 in 80/20) the questions can easily be answered with four letters: RTFM. So it's surprising to get down voted for Q(uoting)TFM.
Andrew Barnett
+6  A: 

My approach:

my $class = "AAA::BBB";
my $type = "CCC";

my $full_class_name = $class . '::' . $type;
( my $file_name = $full_class_name . '.pm' ) =~ s{::}{/}g;

require $file_name;

$full_class_name->test();

Using eval, if you're only doing "die $@ if $@" doesn't make much sense - without eval it will work just as fine.

This additional step of making file_name, and requiring $file_name instead of class makes it not required to use string based eval, which tends to be rather slow.

Of course you still can use eval if you want to provide some kind of fallback:

eval { require $file_name };
if ( my $error = $@ ) {
    ... do something ...
}

But note, that this is block-based eval, and not a string-based one.

depesz
See the part that starts with "But if you try this: ... " in http://perldoc.perl.org/functions/require.html
Sinan Ünür
@Sinan: not sure how's that relevant. My code doesn't fall in this pitfall. And the fact that they show usage of string-based eval - well - it's not my fault ;-P
depesz
+6  A: 

If I may restate your symptoms, I assume you mean this fails like so:

require "AAA::BBB" . "::" . "CCC"; # built from $class and $type;
# => Can't locate AAA::BBB::CCC in @INC (@INC contains ...

because it cannot find a file named AAA::BBB::CCC. However, this succeeds:

require AAA::BBB::CCC;  # note, this is _not_ quoted

because perl searches for a subdirectory-nested module named, in part, AAA/BBB/CCC.pm.

This is how require works. From the documentation for require EXPR:

If EXPR is a bareword, the require assumes a ".pm" extension and replaces "::" with "/" in the filename for you, to make it easy to load standard modules.

So, you must generally either convert your constructed module name to a pathname fragment (s!::!/!g appending ".pm"), or string eval your constructed module name, as others have suggested.

pilcrow
+1  A: 

suppose the module AAA::BBB::CCC.pm located in ~/modules/AAA/BBB/CCC.pm

There is no such thing as AAA::BBB::CCC.pm. There is only AAA/BBB/CCC.pm and is AAA::BBB::CCC.

Don't confuse package names and module files.

innaM
@Manni : It's my fault, I've changed "AAA::BBB::CCC.pm" to "AAA::BBB::CCC". thanks a lot.
Haiyuan Zhang
A: 

I have this kludgy utility class defined in my current project:

package Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter's import() method directly
our @EXPORT_OK = qw(requireClass);

sub requireClass
{
    my $class = shift;
    eval "require $class" or do { die "Ack, can't load $class: $@" };
}

1;
Ether