views:

65

answers:

2

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!

+4  A: 
mobrule
@mobrule - 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?
Russell C.
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
@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 C.
@Russell: don't use global variables at all. Use an OO design, and store your state values in whatever class is appropriate.
Ether
A: 

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.

Chas. Owens
@Chas - 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;'?
Russell C.
@Chas - 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.
@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`.
Chas. Owens
You could also say `our $foo; *foo = \$M::foo` to create the alias in the submodules.
Chas. Owens