I'm trying to initialize a global variable from a Perl module that has a BEGIN block, but I can't manage to get it work.
This is the Perl module
Package A::B;
our $var;
BEGIN{
$var ||= "/some/default/path";
#create/access files/folders in $var
}
This is my CGI script
use A::B;
$A::B::var = "/correct/path";
But #error returned because $var is not the correct path
The BEGIN block is being executed before the correct path is assigned to $var. Is there a way to work around this without having to remove code from the BEGIN block?
BEGIN { $A::B::var = "/correct/path" }
use A::B;
This answer is unsatisfying to me, but I can't think of any other way, given how your A::B is designed. Ideally a module doesn't depend on who is using it or how often, but this module can really only be used once.
Related
So I am new to the perl programing language and I want to get myself acquainted with creating, using, and passing parameters into modules. I made a hello world module that takes as a parameter two string variables from the main.pl testing program, one that says "hello" and another that says "world" and prints them out. Every time I try running main.pl I keep getting errors and I have spent many days trying to get this otherwise simple program to function correctly.
This is the code for my main.pl function:
use FindBin;
use lib $FindBin::Bin;
use test;
use strict;
my $firststring = "hello";
my $secondstring = "world";
test::printthing(\$firststring, \$secondstring);
And this is the coded for my test.pm module:
package test;
use strict
use Exporter;
our #ISA = qw/Exporter/;
our #EXPORT = qw/&main/;
sub printthing{
my $firstword = $_[0];
my $secondwork = $_[1];
print"$firstword\n";
print"$secondword\n";
}1;
You're missing the semicolon from the end of your use strict line in the module.
You try to export the main() subroutine, but your module doesn't have a subroutine called main().
You pass references to your variables to the subroutine but don't dereference them before printing them.
For the final point, you can either continue to pass in references, but dereference before printing.
test::printthing(\$firststring, \$secondstring);
# and then in the subroutine...
print"$$firstword\n";
print"$$secondword\n";
Or, you could just pass in the variables and ignore references completely.
test::printthing($firststring, $secondstring);
# and then in the subroutine...
print"$firstword\n";
print"$secondword\n";
I use the AnyEvent::DNS module.
I want to disable IPv6, so that the resolver only makes a request for A record.
AnyEvent::DNS, uses the environment variable $ENV{PERL_ANYEVENT_PROTOCOLS}
But setting the variable does not work; the resolver still sends two requests A and AAAA
Code from AnyEvent::DNS:
our %PROTOCOL; # (ipv4|ipv6) => (1|2), higher numbers are preferred
BEGIN {
...;
my $idx;
$PROTOCOL{$_} = ++$idx
for reverse split /\s*,\s*/,
$ENV{PERL_ANYEVENT_PROTOCOLS} || "ipv4,ipv6";
}
How to define an environment variable before loading modules?
Since the code that checks the environment variable is in a BEGIN block, it will be run immediately once the Perl compiler reaches it.
When Perl starts compiling your script, it checks for use statements first. So when you use AnyEvent::DNS, Perl loads that module and parses the file. BEGIN blocks are executed at that stage, while code in methods will only be compiled, not executed.
So if you have something like the following, the code you showed above will be run before you even set that variable.
use strict;
use warnings;
use AnyEvent::DNS;
$ENV{PERL_ANYEVENT_PROTOCOLS} = 'ipv4';
...
There are two ways you can circumvent that.
You can put the assignment in your own BEGIN block before you load AnyEvent::DNS. That way it will be set first.
use strict;
use warnings;
BEGIN {
$ENV{PERL_ANYEVENT_PROTOCOLS} = 'ipv4';
}
use AnyEvent::DNS;
Alternatively, you can just call your program with the environment variable set for it from the shell.
$ PERL_ANYEVENT_PROTOCOLS=ipv4 perl resolver.pl
The second one is more portable, in case you later want it to do IPv6 after all.
Read more about BEGIN in perlmod.
So instead of wanting to put
BEGIN {push #INC , '/folder1', '/folder2', '/folder3'}
to find my module.
I want to be able to put a variable instead such as this.
my $test = '/folder1';
BEGIN {push #INC , $test, '/folder2', '/folder3'}
No matter what I do it still wont find the module I want.
A BEGIN executes during parsing of the code, so it would be executed before a variable assignment outside of it occurs.
The solution is to do the assignment inside the BEGIN block:
my $test;
BEGIN {
$test = "/some/path";
}
use lib $test, "/path2", "path3";
The use lib … is preferable to unshift #INC, …
Instead of manually manipulating #INC, I suggest that you use lib:
use lib '/folder2', '/folder3';
Also, use and BEGIN blocks are executed as soon as possible, so if you want to include a variable in one, it must be initialized in a BEGIN block:
our $test;
BEGIN {
$test = "/some/path";
}
use lib $test, '/folder2', '/folder3';
The BEGIN statement runs before anything else runs -- even if the BEGIN statement appears later in the program. Variables you're using must also appear inside the BEGIN. Any variables declared with my will be out of scope once the BEGIN statement ends.
BEGIN {
my $test = '/folder1';
push #INC, $test, '/folder2', '/folder3';
}
I'm not sure what this is buying you. You still need to modify the program. You may want to use lib;:
use lib qw(/folder1);
use lib qw(/folder2 /folder3);
# use lib qw(/folder4); # Don't use
Is it valid perl to set a variable in a BEGIN block, but declare the variable outside the BEGIN block?
#!/usr/bin/env perl
use strict;
use warnings;
use 5.10.0;
my $var;
BEGIN{ $var = 10 }
say $var;
Yes, it's valid. In fact, you must do it that way, or $var would be local to the BEGIN block and not available in the rest of your program. To quote perlsub:
A my has both a compile-time and a run-time effect. At compile time, the compiler takes notice of it. ... Actual initialization is delayed until run time, though, so it gets executed at the appropriate time, such as each time through a loop, for example.
The compile-time effect is why you can access the variable in the BEGIN block. Be aware that any initialization on the my will take place after the BEGIN block is evaluated (and thus will overwrite any value the BEGIN might set.)
Yes, but you might want to be careful with this pattern, because something very similar will work differently than you might expect:
my $var = 5;
BEGIN { $var = 10 }
say $var; # 5
In the example in perlmod/Perl Modules there is a BEGIN block. I looked at some modules but none of these had a BEGIN block. Should I use such a BEGIN block when writing a module or is it dispensable?
You only need a BEGIN block if you need to execute some code at compile time versus run-time.
An example: Suppose you have a module Foo.pm in a non-standard library directory (like /tmp). You know you can have perl find the module by modifying #INC to include /tmp. However, this will not work:
unshift(#INC, '/tmp');
use Foo; # perl reports Foo.pm not found
The problem is that the use statement is executed at compile time whereas the unshift statement is executed at run time, so when perl looks for Foo.pm, the include path hasn't been modified (yet).
The right way to accomplish this is:
BEGIN { unshift(#INC, '/tmp') };
use Foo;
Now the unshift statement is executed at compile-time and before the use Foo statement.
The vast majority of scripts will not require BEGIN blocks. A lot of what you need in BEGIN blocks can be obtained through use-ing other modules. For instance, in this case we could make sure /tmp is in #INC by using the lib.pm module:
use lib '/tmp';
use Foo;
A BEGIN block in a module is entirely dispensable. You only use it if there is something that must be done by your module when it is loaded, before it is used. There are seldom reasons to do much at that point, so there are seldom reasons to use a BEGIN block.