Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Seeking the right way to override global variables

by contra-sh (Acolyte)
on Sep 26, 2014 at 13:37 UTC ( #1102131=perlquestion: print w/replies, xml ) Need Help??

contra-sh has asked for the wisdom of the Perl Monks concerning the following question:

Hello,

In order to test my perl scripts, I want to override global variables (defined with the our keyword in a separated module).

Basically I have a working solution, but I don't really know if there's a better way to do that. And I need some clarifications.

To explain my issue, I reduced the code to 3 files :

- consts.pm which declare globals variables and set their content.

- logging.pm which will use consts.pm and use the global vars

- run-unit-tests.pl which overrides the content of these variables

My current solution is like this :

consts.pm
use Exporter; our @ISA = 'Exporter'; our @EXPORT = qw($VAR1 $VAR2); our $VAR1="var1"; our $VAR2="var2"; 1;
logging.pm
use consts; our ($VAR1, $VAR2); print "Import...\n"; print "VAR1 : [$VAR1]\n"; print "VAR2 : [$VAR2]\n"; sub logging_func() { print "Running function...\n"; print "VAR1 : [$VAR1]\n"; print "VAR2 : [$VAR2]\n"; } 1;
run-unit-tests.pl
use consts; our ($VAR1, $VAR2); local $VAR1 = "override1"; local $VAR2 = "override2"; use logging; logging_func();

The produced output is like this :

Import... VAR1 : [var1] VAR2 : [var2] Running function... VAR1 : [override1] VAR2 : [override2]

Note that I use "local" because seems like this is the good option to override global vars (I'm reading the camel book). Seems like the override only works when I call the function. That's weird because I modified the value before including the logging module.

Other topic, could you please tell me when and why I should use (or not use) the Exporter core module. Because removing it seems to have no impact at all...

Next 3 code snippets produce the same output without teh use of Exporter code module.

consts.pm
our $VAR1="var1"; our $VAR2="var2"; 1;
logging.pm
use consts; our ($VAR1, $VAR2); print "Import...\n"; print "VAR1 : [$VAR1]\n"; print "VAR2 : [$VAR2]\n"; sub logging_func() { print "Running function...\n"; print "VAR1 : [$VAR1]\n"; print "VAR2 : [$VAR2]\n"; } 1;
use consts; our ($VAR1, $VAR2); #print "VAR1 : [$VAR1]\n"; #print "VAR2 : [$VAR2]\n"; local $VAR1 = "override1"; local $VAR2 = "override2"; use logging; logging_func();

When I run perl run-unit-tests.pl I get this output:

Import... VAR1 : [var1] VAR2 : [var2] Running function... VAR1 : [override1] VAR2 : [override2]

I thought that using a package name was a possible solution but it isn't... As you can see on the next code snippets :

consts.pm
use Exporter; package consts; our $VAR1="var1"; our $VAR2="var2"; 1;
logging.pm
use consts; print "Import...\n"; print "VAR1 : [$consts::VAR1]\n"; print "VAR2 : [$consts::VAR2]\n"; sub logging_func() { print "Running function...\n"; print "VAR1 : [$consts::VAR1]\n"; print "VAR2 : [$consts::VAR2]\n"; } 1;
run-unit-tests.pl
use consts; $consts::VAR1 = "override1"; $consts::VAR2 = "override2"; # OR #local $consts::VAR1 = "override1"; #local $consts::VAR2 = "override2"; use logging; logging_func();

This produce the same output :

Import... VAR1 : [var1] VAR2 : [var2] Running function... VAR1 : [override1] VAR2 : [override2]

Note that using "local" keyword during overriding step does not change anything.

If I use $main without specifying the package name, I have the exact same behavior (not a surprise of course).

So to summarize :

- Is it a better solution than mines ?

- Could you please explain this behavior ?

- Should I see any difference if I use require instead of use?

- Why do I have no impact when I stop using Exporter module ? I can't remember why I needed Exporter (maybe because I have "use strict" and "use warnings" in the original scripts...)

I don't really want to use non core modules (I read about PadWalker somewhere). At the moment, I don't really want to use packages if possible (nothing against it, but if there's a solution with packages, there's probably another one without packages).

Thank you a lot by advance.

And... I'm pretty new to perl, and I would say... perl rocks!

Regards.

Thibault

Replies are listed 'Best First'.
Re: Seeking the right way to override global variables
by Athanasius (Archbishop) on Sep 26, 2014 at 13:59 UTC

    Hello contra-sh, and welcome to the Monastery!

    I’ll just address one part of your question:

    Seems like the override only works when I call the function. That's weird because I modified the value before including the logging module.

    No you didn’t! use logging; is the same as BEGIN { require Module; }, which means that the code in “logging.pm” is loaded and run before the rest of the script begins running (because anything in a BEGIN block is run at compile time). Change that line in “run-unit-tests.pl” to require logging; and the output is:

    23:48 >perl 1030_SoPW.pl Import... VAR1 : [override1] VAR2 : [override2] Running function... VAR1 : [override1] VAR2 : [override2] 23:48 >
    - Should I see any difference if I use require instead of use?

    Yes! (as just explained).

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Thanks a lot. That's cristal clear right now :)
Re: Seeking the right way to override global variables
by LanX (Sage) on Sep 26, 2014 at 14:00 UTC
    Already the first question is almost tl;dr plz consider different posts next time.

    You should use local only within the target scope, but your applying it before use, which is problematic.

    Module code outside subs is executed, that's why the effect of local is negated again.

    Cheers Rolf

    (addicted to the Perl Programming Language and ☆☆☆☆ :)

      Yes but the target scope is not aware that we are in "unit test mode"...

      Thank you for the answer.

Re: Seeking the right way to override global variables
by Anonymous Monk on Sep 26, 2014 at 14:01 UTC
    It is usually a good idea to put "global variables" into a Common module of some kind, perhaps which exposes an object that simply serves as the repository of those values. When you do it this way, it's always obvious that you're referring to a widely-used variable ... and it's therefore always obvious when and where it's being used throughout a potentially very-large application. Be sure to use strict; use warnings; everywhere.
      When you say :

      "It is usually a good idea to put "global variables" into a Common module of some kind, perhaps which exposes an object that simply serves as the repository of those values."

      Do you mean explicit package + object oriented?

      I already use a separate perl module and I'm thinking using explicit package name in addition. But I don't want to put object oriented programming with the rest of the code, which is not.

      But I fully agree with you that explicit package name is a better practice (even for small projects like mine, it doesn't hurt).

      And yes I already put use strict and use warnings everywhere :D

      Thank you for your answer.
        Then you could use a pseudo-object, for example storing your global variables in a lexical hashref in the const module, and have in that same module a get and a set functions to access the hash that you export. Something like this:
        use strict; use warnings; use Exporter; our @ISA = 'Exporter'; our @EXPORT = qw(get set); my $private_hash_ref = {VAR1 => "one", VAR2 => "two"}; sub get { my $key = shift; return $private_hash_ref->{$key};} sub set { my ($key, $value) = @_; $private_hash_ref->{$key} = $value;}
        And then in your main code or other module, you just use the set and get functions.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1102131]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (1)
As of 2022-07-06 00:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?