views:

527

answers:

3

I'm trying to make it easier to follow some Perl Best Practices by creating a Constants module that exports several of the scalars used throughout the book. One in particular, $EMPTY_STRING, I can use in just about every Perl script I write. What I'd like is to automatically export these scalars so I can use them without defining them explicitly in each script.

#!perl
package Example::Constants;

use Exporter qw( import );
use Readonly;

Readonly my $EMPTY_STRING => q{};
our @EXPORT = qw( $EMPTY_STRING );

An example usage:

#!perl
use Example::Constants;
print $EMPTY_STRING . 'foo' . $EMPTY_STRING;

Using the above code produces an error:
Global symbol "$EMPTY_STRING" requires explicit package name

If I change the Readonly declaration to:

Readonly our $EMPTY_STRING => q{}; # 'our' instead of 'my'

The error becomes:
Attempt to reassign a readonly scalar

Is this just not possible with mod_perl?

+1  A: 

You had 4 problems:

  1. You weren't including the strict and warnings pragmas
  2. It is better to include exporter through the base pragma (since it sets @ISA for you)
  3. Only package variables (i.e. our variables) can be exported
  4. Modules must end with a true value

Here is the corrected module.

package Example::Constants;

use strict;
use warnings;
use base 'Exporter';
use Readonly;

Readonly our $EMPTY_STRING => q{};
our @EXPORT = qw( $EMPTY_STRING );

1;

Hmm, I missed the bit about attempting to assign to a readonly, it sounds like the module is getting loaded more than once. I believe mod_perl has a mechanism for loading modules separate from the scripts themselves. This loading happens only once, so you should be using it.

Chas. Owens
I'm already doing 1, 3 and 4, I just left them out of the example for brevity. Also, use Exporter qw( import ) is the preferred method, not use base qw( Exporter ). Adding the module to PerlRequire still produces the error, one for each http process started.
cowgod
What about with PerlModule?
Chas. Owens
Also, did you remove "use Example::Constants;" from your script?
Chas. Owens
A: 

I don't have a mod_perl instance handy to test with, so I can't test these suggestions. I hope they pan out.

Try using Scalar::Util::readonly to check if the variable has already been marked read only.

#!perl
package Example::Constants;

use Exporter qw( import );
use Readonly;
use Scalar::Util qw(readonly);

our $EMPTY_STRING;
our @EXPORT = qw( $EMPTY_STRING );

if ( !readonly( $EMPTY_STRING ) ) {
    Readonly $EMPTY_STRING => q{};
}

You could also try use vars:

#!perl
package Example::Constants;

use Exporter qw( import );
use Readonly;
use vars qw( $EMPTY_STRING );

Readonly $EMPTY_STRING => q{};
our @EXPORT = qw( $EMPTY_STRING );

You could also use a typeglob constant:

#!perl
package Example::Constants;

use Exporter qw( import );
use Readonly;

our $EMPTY_STRING;
*EMPTY_STRING = \q{};
our @EXPORT = qw( $EMPTY_STRING );

Using a typeglob constant seems perfect, since the big limitation of the technique (it requires a package global) is not an issue here.

daotoad
+2  A: 

Hi, I'm the author of the Readonly module. The next version of Readonly will provide support for mod_perl, specifically because of this problem.

I know this doesn't solve your problem now, but... well, I'm working on it :-)

-- Eric

wow, you signed up just to say this? I am impressed.
Ape-inago
Awesome! Thank you so much. If it matters, I am also using the Readonly::XS module.
cowgod
Well Eric, it's been a year. Any progress with this? :)
cowgod