Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery

BEGIN block and failure to initialize constants

by temporal (Pilgrim)
on Sep 17, 2012 at 21:06 UTC ( #994105=perlquestion: print w/ replies, xml ) Need Help??
temporal has asked for the wisdom of the Perl Monks concerning the following question:

I have a Perl module that I'd like to link a config file to. This was slightly trickier than I initially anticipated since I'd like to keep it as portable as possible such that no matter where the module is located it always looks for its config file in the same directory. Piece of cake if I'm calling the module directly or from scripts in the same path, but when I use it in a script located in a different directory it looks for the module's config file in the running script's directory rather than the module's directory.

I'd rather not fiddle with @INC just for a config file. So I came up with something like this:

#! perl -w package mylib; my ($MODULE_PATH, $CONF_PATH); ($CONF_PATH = __FILE__) =~ s/pm$/conf/i; use constant { MODULE_PATH => __FILE__, CONF_PATH => $CONF_PATH }; print CONF_PATH;

This code warns me that the constant is uninitialized in the print statement. Hm. So things must be happening in a different order than I thought at compile-time, right?

So my next stab at it:

#! perl -w package mylib; my ($MODULE_PATH, $CONF_PATH); # assign value at compile-time before constants BEGIN { ($CONF_PATH = __FILE__) =~ s/pm$/conf/i; } use constant { MODULE_PATH => __FILE__, CONF_PATH => $CONF_PATH, }; print CONF_PATH;

And now I'm getting what I want: a constant that contains the absolute path of my config file for later use with Config::General and also drops it in @EXPORT_OK in case some calling script ever needs to see it.

So I'm wondering what's really going on under the hood here. Looking at the docs, constants get set at compile-time and I guess since I declare the BEGIN block first it beats the constant declaration to the punch.

Anyway, just curious if this is a classic case of "I'm doing it wrong" or maybe get some tips/suggestions for an intelligent way to implement this.

Comment on BEGIN block and failure to initialize constants
Select or Download Code
Re: BEGIN block and failure to initialize constants
by kennethk (Monsignor) on Sep 17, 2012 at 21:52 UTC
    So, as described in use, use mylib; is exactly equivalent to BEGIN { require Module; Module->import( LIST ); }. When you don't define your value in a BEGIN block, the implicit BEGIN in use constant beats it in the foot race, since (as documented in BEGIN, UNITCHECK, CHECK, INIT and END)
    A BEGIN code block is executed as soon as possible, that is, the moment it is completely defined, even before the rest of the containing file (or string) is parsed. You may have multiple BEGIN blocks within a file (or eval'ed string); they will execute in order of definition.
    You can even have nested BEGIN blocks, where order of operations gets applied recursively. In fact, since you will use mylib; to get to your module, that's exactly what happens.

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      Oh man, I was overthinking this problem way too much. For some reason I thought constants were a special case and forgot entirely about use's equivalence to a BEGIN block as you just described.

      Thanks for dusting that knowledge off for me kennethk.

      Also, cool note about nesting those blocks, TIL. =)

      Strange things are afoot at the Circle-K.

Re: BEGIN block and failure to initialize constants
by GrandFather (Cardinal) on Sep 17, 2012 at 22:03 UTC

    I get the path of a loaded module using:

    sub GetVariousPath { # Hacky way to figure out relative location of TaskManager::Variou from # running script's context my @variousPaths = map {$INC{$_}} grep {m!TaskManager[\\/]Various\.pm$!} keys %INC; push @variousPaths, __FILE__; s!(.*)[\\/].*!$1! for @variousPaths; -d $_ && return $_ for @variousPaths; die "Unable to determine path to TaskManager::Various module\n"; }

    and then use that path as teh context for getting at a configuration file.

    True laziness is hard work
Re: BEGIN block and failure to initialize constants
by tobyink (Abbot) on Sep 17, 2012 at 22:10 UTC

    The problem with your original version is that the use constant executes at compile time, but the regular expression executes at run time - i.e. after everything has been compiled - so it's setting the $CONF_PATH variable too late. To see the difference between compile time and run time, run this:

    print "World\n"; BEGIN { print "Hello " };

    And the use keyword (plus its friend no) runs conceptually in a BEGIN block.

    A do { ... } block provides a neat solution:

    package mylib; use constant { MODULE_PATH => __FILE__, CONF_PATH => do { $_ = __FILE__; s/pm$/conf/i; $_ }, }; print CONF_PATH;

    If your perl is fairly recent, you can do away with do and use /r.

    package mylib; use constant { MODULE_PATH => __FILE__, CONF_PATH => (__FILE__ =~ s/pm$/conf/ir), }; print CONF_PATH;
    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: BEGIN block and failure to initialize constants
by temporal (Pilgrim) on Sep 18, 2012 at 14:26 UTC

    Thanks for the replies. I like tobyink's solution for figuring out the path within the module - nice and clean. Unfortunately the Perl on my production machines is too elderly to support the /r substitution option. Looks like that came about in v5.14 and since I'm still being coerced into living with v5.10 I'll have to just leave a lovelorn comment showing how it could be prettier.

    And Grandfather gives a great way to figure it out from the running script's perspective. Also handy!

    As always, much appreciated =)

    Strange things are afoot at the Circle-K.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://994105]
Approved by GrandFather
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (13)
As of 2014-07-28 18:38 GMT
Find Nodes?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:

    Results (206 votes), past polls