leriksen has asked for the wisdom of the Perl Monks concerning the following question:
My question is 'what parts of your code can you assume have already been parsed when you are writing a BEGIN block ?'
I ask because I spent quite some time on the weekend struggling with a package global, set in a BEGIN block, getting clobbered at run time for reasons that are still a little unclear.
Background : I am refactoring some modules, one of which pulls in a configuration file at compile time. That is, clients of this module use it thus
... use Config qw(getConfig); ... my $foo = getConfig('bar'); ...
In Config.pm, the code was originally like this
package Config; my $initialised = 0; sub _initialise { return if $initialised; ... # read config file $initialised++; } _initialise(); sub getConfig{...} ... 1;
This worked, but I changed the $initialised from a my to an our so that there was a way to reload a config file if required (I've changed this to a Reload() method now)
And because I wanted to make clearer that the call to _initialise() was a deliberate decision, I put it in a BEGIN {} block - to me it stands out a lot clearer that the bare statement in the original.
So we have
package Config; our $initialised = 0; sub _initialise { ... # as before } BEGIN { _initialise(); } ... 1;
Now because I like writing correct code, I wrapped all this in a test harness ala Test::More, and took advantage of the fact that $initialised was a package global to write a test like
... use_ok('Config'); is($Config::initialised, 1); # initialisation completed OK
And guess what, I got
t/Config....Name "Config::var" used only once: possible typo at t/Conf +ig.t line 9. # Failed test (t/Config.t at line 9) # got: '0' # expected: '1' # Looks like you failed 1 tests of 2. t/Config....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 2 Failed 1/2 tests, 50.00% okay Failed Test Stat Wstat Total Fail Failed List of Failed ---------------------------------------------------------------------- +--------- t/Config.t 1 256 2 1 50.00% 2 Failed 1/1 test scripts, 0.00% okay. 1/2 subtests failed, 50.00% okay. make: *** [test_dynamic] Error 2
It looks like the initialisation never happened - but I know it happened because I sprinkled a lot of print's in _initialise() to see it working. So it looks like the our $initialised = 0; is clobbering the value set in the BEGIN, at run time. I hope I'm not using the terms compile/run time too loosely.
After pondering and playing for a while, I came up with this version that seems to be correct.
package Config; sub _initialise { ... # as before } BEGIN { our $initialised = 0; _initialise(); } 1;
This works correctly (though I still get the warning about the var only used once), but the declaration of the our $initialised in the BEGIN looks weird. I suppose if you think about the BEGIN as getting run-time behaviour at compile time, it looks less weird, if you squint.
So if I can't rely on the our $initialised outside the BEGIN as 'existing' when the BEGIN runs, what are the rules about what is in scope before/during/after a BEGIN ?
use brain;
|
|---|