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

I want to create generic objects if the package does not already exists. This is to keep me from creating a bunch of nearly identical modules that all do the same thing. The only thing that really changes is the package name. So I wrote this code, which I thought would work but I am not so sure.

################################################# sub _create_new_package_for { my $self = shift; my $package = _get_package_name( shift ); eval "use $package"; ## no critic return $package if not $EVAL_ERROR; my $eval_package =<<"END"; package ${package}; use strict; use warnings; use English qw( -no_match_vars ); use base 'Generic::Tree'; use base 'Generic::Container'; 1; END eval $eval_package; ## no critic croak $EVAL_ERROR if $EVAL_ERROR; return $package; }

The issue I see here is that the new 'package' does not seemed to be stored. So I can use it once, but if I want to 'use' a generic package that has already been created, it will not allow itself to be identified with a 'use' statement. So it will always eval the package code again. To me that did not seem correct. Example:

$self->_create_new_package_for( 'Testing::Package' ); $self->_create_new_package_for( 'Testing::Package' );

From the above code, I would assume, that the first time I initialize this namespace. So the second time I execute the subroutine it would be 'usable'. It is not, so what is it that I am missing and how can I fix it?

Replies are listed 'Best First'.
Re: Dynamically creat Packages
by kyle (Abbot) on Nov 04, 2008 at 17:49 UTC

    You may be able to get what you want by messing with %INC:

    my $path = $package; $path =~ s{::}{/}g; $path .= '.pm'; $INC{$path} = '(eval)';

    There may be something better to use than '(eval)'. It's just the first thing I thought of. When you then use the package again, it sees the entry in %INC and thinks it's already been loaded.

      I understand the first part, but I don't get what the '(eval)' is supposed to be.

        It's just a true value. For other packages, %INC contains the full path to the file that provided it. Since it's not coming out of a file here, I just put '(eval)' like what caller says when it also would otherwise name something specific.

Re: Dynamically creat Packages
by JadeNB (Chaplain) on Nov 04, 2008 at 21:00 UTC
    It seems that kyle understood what you meant, so I'm sure that I must be missing something, but—what does it mean to not be ‘usable’? I tried running your snippet and, once I defined sub _get_package_name { return $_[0] }, it didn't complain for me. Do you get a complaint, or is it only later that anything bad happens?

    UPDATE: Whoops, I didn't use English;, so that $EVAL_ERROR was false while $@ was not.

      Add this line, after the eval "use $package" statement. (Note: You did add a use strict and use English to your script right?).

      warn "use error: " . $EVAL_ERROR . "\n";

      It should complain twice about not being able to find the package. Once for each call. If you add a third call it would complain a third time, etc... Each eval statement is kinda slow. If there are a large enough transactions it can slow the system significantly.

      What kyle was suggesting is spoofing the load in the %INC variable so I only have it complain once and eval once for a given package. Does that help?

Re: Dynamically create Packages
by chromatic (Archbishop) on Nov 05, 2008 at 02:11 UTC
    This is to keep me from creating a bunch of nearly identical modules that all do the same thing.

    That sounds to me like an application of the Factory pattern.

Re: Dynamically create Packages
by JadeNB (Chaplain) on Nov 09, 2008 at 21:50 UTC
    I just stumbled today on Ricardo Signes's Package::Generator. Does this do anything like what you want?