0

I have a Perl module (Module.pm) that initializes a number of variables, some of which I'd like to import ($VAR2, $VAR3) into additional submodules that it might load during execution.

The way I'm currently setting up Module.pm is as follows:

package Module;

use warnings;
use strict;

use vars qw($SUBMODULES $VAR1 $VAR2 $VAR3);

require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw($VAR2 $VAR3);

sub new {
    my ($package) = @_;
    my $self = {};
    bless ($self, $package);
    return $self;
}

sub SubModules1 {
    my $self = shift;
    if($SUBMODULES->{'1'}) { return $SUBMODULES->{'1'}; }

    # Load & cache submodule
    require Module::SubModule1;
    $SUBMODULES->{'1'} = Module::SubModule1->new(@_);    
    return $SUBMODULES->{'1'};
}

sub SubModules2 {
    my $self = shift;
    if($SUBMODULES->{'2'}) { return $SUBMODULES->{'2'}; }

    # Load & cache submodule
    require Module::SubModule2;
    $SUBMODULES->{'2'} = Module::SubModule2->new(@_);    
    return $SUBMODULES->{'2'};
}

Each submodule is structured as follows:

package Module::SubModule1;

use warnings;
use strict;
use Carp;

use vars qw();

sub new {
    my ($package) = @_;
    my $self = {};
    bless ($self, $package);
    return $self;
}

I want to be able to import the $VAR2 and $VAR3 variables into each of the submodules without having to reference them as $Module::VAR2 and $Module::VAR3. I noticed that the calling script is able to access both the variables that I have exported in Module.pm in the desired fashion but SubModule1.pm and SubModule2.pm still have to reference the variables as being from Module.pm.

I tried updating each submodule as follows which unfortunately didn't work I was hoping:

package Module::SubModule1;

use warnings;
use strict;
use Carp;

use vars qw($VAR2 $VAR3);

sub new {
    my ($package) = @_;
    my $self = {};
    bless ($self, $package);
    $VAR2 = $Module::VAR2;
    $VAR3 = $Module::VAR3;
    return $self;
}

Please let me know how I can successfully export $VAR2 and $VAR3 from Module.pm into each Submodule. Thanks in advance for your help!

3 Answers 3

5

In your submodules, are you forgetting to say

use Module;

? Calling use Module from another package (say Module::Submodule9) will try to run the Module::import method. Since you don't have that method, it will call the Exporter::import method, and that is where the magic that exports Module's variables into the Module::Submodule9 namespace will happen.


In your program there is only one Module namespace and only one instance of the (global) variable $Module::VAR2. Exporting creates aliases to this variable in other namespaces, so the same variable can be accessed in different ways. Try this in a separate script:

package Whatever;
use Module;
use strict;
use vars qw($VAR2);

$Module::VAR2 = 5;
print $Whatever::VAR2;    # should be 5.
$VAR2 = 14;               # same as $Whatever::VAR2 = 14
print $Module::VAR2;      # should be 14
Sign up to request clarification or add additional context in comments.

4 Comments

rule - I thought that would create a multiple instance situation since I only want 1 instance of Module.pm running and sharing it's GLOBAL variables across SubModule1.pm and SubModule2.pm so basically if a function in SubModule1.pm modifies $VAR2 then that modification would be accessible across Module.pm and the other submodules. Would adding 'use Module;' as you've suggested in the submodules create this desired effect? If not, how else could it be accomplished?
Yes, it would have the desired effect, but if your desired effect is to base behavior off of globals in one package that are arbitrarily changed by another, you would do well to seriously rethink your design.
@friedo - I'd love some advice on ways we could consider changing the design since I know it's not ideal to use or change GLOBALS. The reason I want these variables to be global is because Module.pm is acting an our controller and sets up a bunch of GLOBAL variables like the URL of our domain, the location of the data directory, etc. Since Module.pm is then loading whatever submodules it needs to render the appropriate page, I'd like all submodules to be able to reference, use, and in a couple special circumstances modify these global variables. Is there another more ideal way to set this up?
@Russell: don't use global variables at all. Use an OO design, and store your state values in whatever class is appropriate.
0

Well there is the easy way:

In M.pm:

package M;

use strict;
use warnings;

#our is better than "use vars" for creating package variables
#it creates an alias to $M::foo named $foo in the current lexical scope 
our $foo = 5;

sub inM { print "$foo\n" }

1;

In M/S.pm

package M;

#creates an alias to $M::foo that will last for the entire scope,
#in this case the entire file
our $foo;

package M::S;

use strict;
use warnings;

sub inMS { print "$foo\n" }

1;

In the script:

#!/usr/bin/perl

use strict;
use warnings;

use M;
use M::S;

M::inM();
M::S::inMS();

But I would advise against this. Global variables are not a good practice, and sharing global variables between modules is even worse.

4 Comments

The reason I have Module.pm and the submodules setup as I do is so that the calling script doesn't have to explicitly ask to use it's required submodules. Is there another way to setup this up that would have the same effect but would only require the calling script to 'use Module;'?
To address your last comment the reason I want these variables to be global is because Module.pm is acting an our controller and sets up a bunch of GLOBAL variables like the URL of our domain, the location of the data directory, etc. Since Module.pm is then loading whatever submodules it needs to render the appropriate page, I'd like all submodules to be able to reference, use, and in a couple special circumstance modify these global variables.
@Russell C. It will still work. Where the use/require occurs doesn't really matter. What matters is that $foo is an alias to $M::foo.
You could also say our $foo; *foo = \$M::foo to create the alias in the submodules.
0

This is from "Exporter(3pm)" under "Good Practices":

Exporting variables is not a good idea. They can change under the hood, provoking horrible effects at-a-distance, that are too hard to track and to fix. Trust me: they are not worth it.
To provide the capability to set/get class-wide settings, it is best instead to provide accessors as subroutines or class methods instead.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.