Re: BEGIN and compile-time
by Anonymous Monk on Nov 02, 2006 at 11:35 UTC
|
BEGIN {
delete $INC{'ModX.pm'};
use lib '../';
}
use ModX;
This will execute the BEGIN block before the use. | [reply] [d/l] |
|
BEGIN { delete $INC{'ModX.pm'}; }
use lib '../';
use ModX;
That way, the statements will execute in the same order as they are in the source.
| [reply] [d/l] [select] |
|
Thanks, that looks nicer. Is there a way of capturing and replaying the original list passed to 'use'?
ah, but...it looks like the top-level use will call import afterwards for us (so we don't need to worry about import...it Just Works with the require). In which case maybe require is more correct, since otherwise we'll call import twice, once when we use it (with no args) and once from the original, script-level use once we finish?
| [reply] |
|
If you want to use a use without import to be called, just put some parens after the module (and nothing inside the parens):
use ModX ();
| [reply] [d/l] |
Re: BEGIN and compile-time
by Mutant (Priest) on Nov 02, 2006 at 11:01 UTC
|
For the import, can't you just call it yourself after the require?
import Modx @foo;
You might get some 'subroutine redifined' warnings, but it should work.
Some people might have a problem with you doing this at all (i.e. it's kind of an odd thing to dynamically load modules like this), but if you have a valid reason, go for it :)
The usage of BEGIN{} etc. is a pretty common place to go wrong. It took me a while to get my head around it (and not sure I even have now). | [reply] [d/l] [select] |
|
What would I put in @foo?
Nod on the BEGIN{} issues. I'm just used to thinking of run-time versus compile-time overall, but the more correct way is to think of run-time and compile-time as a per-block thing. The outer begin block has its run-time at the script's compile-time. The inner begin block has its run-time at the compile-time of the outer begin block.
Big fleas have little fleas, upon their backs to bite 'em
Little fleas have lesser fleas, and so, ad infinitum.
| [reply] |
|
print("first");
BEGIN {
delete $INC{'ModX.pm'};
use lib '../';
use ModX;
}
print("last");
gets executed in the following order
use lib '../'; # Before "delete" because of "use"
use ModX; # Before "delete" because of "use"
delete $INC{'ModX.pm'}; # Before "first" because of "BEGIN"
print("first");
print("last");
In detail:
- Compile print("first");.
- Compile BEGIN { ... }.
- Compile delete $INC{'ModX.pm'};.
- Compile use lib '../';.
- Execute use lib '../';.
- Compile use ModX;.
- Execute use ModX;.
- Execute BEGIN { ... }.
- Execute delete $INC{'ModX.pm'};.
- Compile print("last");.
- "Run phase" starts.
- Execute print("first");.
- Execute print("last");.
| [reply] [d/l] [select] |
|
|
|
Sorry, that code snippet wasn't very clear... @foo is just the list of what you want to import.
| [reply] |
|
|
Re: BEGIN and compile-time
by cephas (Pilgrim) on Nov 02, 2006 at 15:02 UTC
|
I apparently don't understand what you mean by not being able to modify the load path, since your executing use lib '../'; which is modifying @INC.
Why don't you just do
use lib '../';
use ModX;
and be done with it?
Update: After re-reading the original post, it seems you are trying to perform magic within ./ModX.pm to replace itself with ../ModX.pm. Any particular reason you don't just replace it then? | [reply] [d/l] [select] |
|
The reason is that changes over time are likely to ../ModX.pm, which a copy won't keep up to date with.
Until a robust way of changing everything to use ../ModX.pm directly is deployed, it is useful to have ./ModX.pm proxy directly to the live ./ModX.pm
(Oh...and symlinks aren't likely to survive the version control system.)
| [reply] |
Re: BEGIN and compile-time
by perrin (Chancellor) on Nov 02, 2006 at 17:36 UTC
|
"When they BEGIN, the BEGIN..." Apologies to Cole Porter. | [reply] |
|
Heh. Would you believe this is my actual snippet which gave me the aha moment when I ran it? :-)
BEGIN {
print "begin\n";
BEGIN {
print "the beguine\n";
}
}
| [reply] [d/l] |
Re: BEGIN and compile-time
by Firefly258 (Beadle) on Nov 09, 2006 at 03:43 UTC
|
If the modules report version numbers (which normally is expected of modules), it might be best to use the following form of use;
use Module VERSION
# e.g. use Foo::Bar 5.01; # <- just v5.01 please, TYVM
# or better still
eval "use Foo::Bar 5.01" or do { use Foo::Bar }
# fallback incase the 'required' version isnt available
That way, your script works regardless of whether the module is moved about on the filesystem or your script is used on another machine. | [reply] [d/l] |
|
That doesn't do what you expect. The second use Foo::Bar gets executed first, and it gets executed unconditionally.
Why would you want to fallback to the same module anyway? That's the same as not putting a version in the first place! If you wanted to fallback to a different module, the following will do:
BEGIN {
my $module = 'Foo::Bar';
require Foo::Bar;
eval { Foo::Bar->VERSION(5.01) };
if ($@) {
warn(...);
$module = 'Foo::Baz';
require Foo::Baz;
}
import $module qw( ... );
}
VERSION is documented in UNIVERSAL. | [reply] [d/l] [select] |
|
You're right, and i seem to have overseen the fact that use; statements take execution precedence. Moreover, this particular eval() doesn't work too well as a truth assertion (blaming use) and the subsequent use; gets executed no matter what.
Why fallback to the same module?? Well, maybe we do need it but ideally we'd like to take control and load the one version of it identified by a supplied version number but if the module at that version isn't available, prepare the script for the standard version and continue, something along the lines of OP's wants.
So, as has already been examined, "use lib '../'; use Foo::Bar;" seems to have worked simply and elegantly.. but "use lib" places entries at the beginning of @INC and what if non-standard versions of ../strict.pm or ../warnings.pm existed? The user might inadvertently load these modules (OUCH!!) rather than the ones in the original @INC. Placing entries at the beginning of @INC is a bad idea, the risk of polluting the package with bogus code as a result of namespace clashes is real.
{
use lib "..";
use strict; # "../strict.pm" loaded instead of the real one
}
Placing entries at the end of @INC means that modules if found in the original @INC take precendence and are loaded instead of the ones you want loading. The chances are of such a case are slim but entirely possible especially if the user isn't aware of the perl module namespace. Even otherwise, the user might be patching/updating the an already existant module and can ensure the testing release is loaded by using it's $VERSION (wherever the new .pm is located via @INC). Placing entries at the end of @INC is generally safer.
As an added benefit, using the $Foo::Bar::VERSION to validate loading Foo::Bar also ensures that your code is guaranteed to run under Foo::Bar, alternate versions of the module might break something.
So if the OP wanted to load a non-standard Foo::Bar (let's assume v5.00) instead of a standard Foo::Bar (v5.01) and provided the version numbers reported by the 2 modules were different (they really ought to be) ...
#!/usr/bin/perl -W
BEGIN { push @INC, ".." }
use strict; # "../strict.pm" untouched
eval "use Foo::Bar 5.01 qw| raz baz taz |"; # try loading v5.01
if ($@) { # if loading v5.01 failed
warn "@_"; # fallback to using standard (v5.00)
use Foo::Bar qw| baz |; # raz and taz arent valid tags here
}
...
| [reply] [d/l] [select] |
|
|
|
|
I managed to find out why my initial eval " use ..." attempt failed. use; cannot be used in expressions . Hence, it does not return a value (so it's practically useless in truth evaluation constructs) even on a successful run like in the following..
eval "use Foo::Bar" or do { warn "eval failed" }
Here eval"" always evaluates to false as use; returns nothing and we end up with the do BLOCK always being executed.
But getting eval"" to return a TRUE value on a successful eval changes that.
eval "use Foo::Bar; 1" or do { warn "eval failed" }
| [reply] [d/l] [select] |
|
It is more appropriate to test $@.
eval "use Foo::Bar";
if ($@) {
warn "Eval failed: $@";
}
| [reply] [d/l] |
|
|
|
|
|
Ikegami, you're right as always. Perhaps eval "use Foo 5.01; 1" or do { require Foo; ... } does it.
Thanks tye for clearning up that little something, it made me take a closer look at sub VERSION.
Here's an attempt to get use Module VERSION to load the right VERSION.
$ cat a/strict.pm
package strict;
our $VERSION = 1.04;
sub VERSION {
my ($module, $version) = @_;
return $VERSION unless $version;
until ($VERSION == $version) {
shift @INC;
delete $INC{"strict.pm"};
eval " require strict; 1; ";
$VERSION = $strict::VERSION;
}
return $VERSION;
}
$ cat ./strict
#!/usr/bin/perl -Wl
BEGIN {
eval {
local @INC = @INC;
unshift @INC, qw|a b c|;
eval "
use strict 1.03 qw|subs vars refs|;
1;
";
warn "$@" if $@;
};
}
print "strict using $INC{'strict.pm'} version $strict::VERSION ";
$ ./strict
strict using /usr/share/perl/5.8/strict.pm version 1.03
It's kludgy and noisy but it works. | [reply] [d/l] [select] |
|
|