Dependency between perl modules - perl

Suppose I have three perl modules as given below :
Test.pm
package Test;
use strict;
use warnings;
use Check;
our $data = Check->getX;
1;
Initialize.pm
package Initialize;
use Check;
use Test;
Check->setX(10);
our $t = $Test::data;
print $t;
1;
Check.pm
package Check;
my $x = 12;
sub setX {
my ($self,$value) = #_;
$x = $value;
}
sub getX
{
return $x;
}
1;
Now, when I run Initialize.pm, I am initializing $x in Check.pm to 10 and $x is assigned to $data in Test.pm. But the the actual valuethat is assigned to $data is 12 which is the initial value given in Check.pm.
So, When are the global variables initialized in perl? How can I enforce that the new value set by me in the Initialize.pm to x is what is loaded into $data?
Now if I replace the statement use Test in Initalize.pm with require Test; and move the statement Check->setX(10) before this require statement then $data is correctly initialized to the new value 10. What is it that is happening differently in this case ?

In general modules have little or no executable code. Object-oriented modules just define the object methods, and sometimes some class data.
When you use Test the whole of Test.pm is compiled and executed, so the value of $data is set at this point.
The call to setX happens straight afterwards, but is too late to affect the asignment of $data.
As I said in my comment your code has a very odd structure, and modules shouldn't have a time dependency on each other at all. You should really remove all executable statements from your modules, but to force your code to do what you want you can write
use strict;
use warnings;
use Check;
BEGIN {
Check->setX(10);
}
use Test;
our $t = $Test::data;
print $t;
But don't do that!

Perl executes a use statement prior to executing anything else in the file.
So the execution order is:
use Check;
$x = 12;
use Test;
use Check; -This only does importing as the file is already executed
$data = Check->getX();
Check->setX(10);
If you replace use with require the instruction is evaluated at the same time as the rest of the instructions and if you move Check->setX(10); before the require it will be evaluated before the get in Test

Related

Conditionally including a module in perl

I am new to perl, just encountered one case.
Can someone tell why does this fail with error
Undefined subroutine &main::color
$condition = 1;
use if ( $condition ), Term::ANSIColor;
print color('bold red');
print "hii";
print color('reset');
and this passes
use if ( 1 ), Term::ANSIColor;
print color('bold red');
print "hii";
print color('reset');
This is because use statements are executed at compile time, while your assignment is performed at run time and hasn't been executed yet
You can fix this by using a BEGIN block to do the assigmment at compile time as well, like this. Note that the variable must be declared outside the block, otherwise it will be local to the block and will disappear before it is neded
my $condition;
BEGIN {
$condition = 1;
}
use if $condition, 'Term::ANSIColor';
print color('bold red');
print "hii";
print color('reset');
Note also that you should always have use strict and use warnings 'all' at the top of every Perl program. If you had these in place you would need to quote the module name, as shown above

Issues with function calls for search routine

My aim is to have multiple searches of specific files recursively. So I have these files:
/dir/here/tmp1/recursive/foo2013.log
/dir/here/tmp1/recursive/foo2014.log
/dir/here/tmp2/recursive/foo2013.log
/dir/here/tmp2/recursive/foo2014.log
where the 2013 and 2014 says in which year the files got modified lastly.
I then want to find the more up to date files (foo2014.log) for each directory tree (tmp1 and tmp2 likewise).
Referring to this answer I have the following code in script.pl:
#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
func("tmp1");
print "===\n";
func("tmp2");
sub func{
my $varName = shift;
my %times;
find(\&upToDateFiles, "/dir/here");
for my $dir (keys %times) {
if ($times{$dir}{file} =~ m{$varName}){
print $times{$dir}{file}, "\n";
# do stuff here
}
}
sub upToDateFiles {
return unless (-f && /^foo/);
my $mod = -M $_;
if (!defined($times{$File::Find::dir})
or $mod < $times{$File::Find::dir}{mod})
{
$times{$File::Find::dir}{mod} = $mod;
$times{$File::Find::dir}{file} = $File::Find::name;
}
}
}
which will give me this output:
Variable "%times" will not stay shared at ./script.pl line 25.
/dir/here/tmp1/recursive/foo2014.log
===
I have three questions:
Why isn't the second call of the function func working like the first one? Variables are just defined in the scope of the function so why am I getting interferences?
Why do I get the notification for variable %times and how can I get rid of it?
If I define the function upToDateFiles outside of func I am getting this error: Execution of ./script.pl aborted due to compilation errors. I think this is because the variables aren't defined outside of func. Is it possible to change this and still get the desired output?
For starters - embedding a sub within another sub is rather nasty. If you use diagnostics; you'll get:
(W closure) An inner (nested) named subroutine is referencing a
lexical variable defined in an outer named subroutine.
When the inner subroutine is called, it will see the value of
the outer subroutine's variable as it was before and during the *first*
call to the outer subroutine; in this case, after the first call to the
outer subroutine is complete, the inner and outer subroutines will no
longer share a common value for the variable. In other words, the
variable will no longer be shared.
This problem can usually be solved by making the inner subroutine
anonymous, using the sub {} syntax. When inner anonymous subs that
reference variables in outer subroutines are created, they
are automatically rebound to the current values of such variables.
Which is directly relevant to the problem you're having. Try to avoid nesting your subs, and you won't have this problem. It certainly looks like you're trying to be far more complicated than you need to. Have you considered something like:
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
use File::Find;
my %filenames;
sub compare_tree {
return unless -f && m/^foo/;
my $mtime = -M $File::Find::name;
if ( !$filenames{$_} || $mtime < $filenames{$_}{mtime} ) {
$filenames{$_} = {
newest => $File::Find::name,
mtime => $mtime,
};
}
}
find( \&compare_tree, "/dir/here" );
foreach my $filename ( keys %filenames ) {
print "$filename has newest version path of:", $filenames{$filename}{newest}, "\n";
print "$filename has newest mtime of:", $filenames{$filename}{mtime}, "\n";
}
I'd also note - you seem to be using $File::Find::dir - this looks wrong to me, based on what you describe you're doing. Likewise - you're running find twice on the same directory structure, which is not a very efficient approach - very big finds are expensive operations, so doubling the work needed isn't good.
Edit: Caught out by forgetting that -M was: -M Script start time minus file modification time, in days.. So 'newer' files are the lower number, not the higher. (So have amended above accordingly).

How can I use "member" variables of a module inside a function?

I have this code:
#!/usr/bin/perl
package Modules::TextStuff;
use strict;
use warnings;
use Exporter;
our #ISA = qw(Exporter);
our #EXPORT = qw(get_text);
my $author;
my $text_tmp1 =<<'ENG';
This is a template text
by $author.
ENG
sub get_text {
my $tmp = shift #_;
$author = shift #_;
print "In sub author= $author lang = $tmp \n";
my $final_str = eval('$text_'.$tmp);
print "$final_str \n";
return $final_str;
}
1;
Test script:
#!/usr/bin/perl
use strict;
use warnings;
use Modules::TextStuff;
my $str = get_text('tmp1','jim');
print $str;
When I run the test script it does not work. I get:
In sub author=jim lang = eng
Variable "$text_tmp1" is not available at (eval 1) line 2. Use of
uninitialized value $final_str in concatenation (.) or string
How can I fix this?
Combining strings to create variables names is usually a bad idea. You could salvage your current program using our $text_tmp1 = ... instead of my $text_tmp1 = ..., but I think you should consider a different approach, like a hash:
my %templates = (
tmp1 => <<ENG,
This is a template text
by \$author.
ENG
tmp2 => <<ESP,
Esta es templata texta de \$author.
ESP
);
sub get_text {
...
my $final_str = eval( $templates{$tmp} );
...
}
The error you asked about is generated when eval EXPR tries to grab the value of a variable that did exist, but no longer exists.
>perl -wE"{ my $x = 123; sub f { eval '$x' } } say '<'.f().'>';"
Variable "$x" is not available at (eval 1) line 2.
Use of uninitialized value in concatenation (.) or string at -e line 1.
<>
Remember, executing a file (such as a script or a module) is done in its own a lexical scope, just like the one the curlies create above.
It can be fixed by keeping the variable alive by not letting it go out of scope
>perl -wE"my $x = 123; sub f { eval '$x' } say '<'.f().'>';"
<123>
But that's not an option for you.
Other options include making the variable a global variable.
>perl -wE"{ our $x = 123; sub f { eval '$x' } } say '<'.f().'>';"
<123>
Or forcing the sub to capture it so it doesn't cease to exist.
>perl -wE"{ my $x = 123; sub f { $x if 0; eval '$x' } } say '<'.f().'>';"
<123>
(The if 0 silences the "void context" warning.)
That said, it looks like you're trying to re-invent the wheel. Don't invent another half-assed templating system.
I'm looking at several things:
First of all, $text_tmp1 is not a package variable. It's lexically scoped since you declared it with my. If you need it as a package variable and for it to be visible in all or your subroutines, you need to declare it with our.
Your module doesn't compile as written. You are trying to source in $author, but it's not defined.
What are you doing with eval? This is wrong on so many levels.
Here's how I would do it:
#! /usr/bin/env perl
package Modules::TextStuff;
use strict;
use warnings;
use Exporter qw(import);
use Carp;
our #EXPORT_OK = qw(get_text);
our %templates; # This is now a package variable
#
# TEMPLATES
#
$templates{tmp1}=<<TEMPLATE; # We'll use `%s` for replacements
This is a template text
by %s.
TEMPLATE
$templates{tmp2}=<<TEMPLATE;
This is another template and we will substitute
in %s in this one too.
TEMPLATE
sub get_text {
my $template = shift;
my $author = shift;
if ( not exists $templates{$template} ) {
croak qq(Invalid template name "$template");
}
return sprintf $templates{$template}, $author;
}
1;
I'll make each of these templates an entry in my %templates hash. No need for eval to calculate out a variable name for the template. Also notice that I can now actually test whether the user passed in a valid template or not with the exists.
Also note that %template is declared with our and not my. This makes it available in the entire package including any subroutines in my package.
I also use #EXPORT_OK instead of #EXPORT. It's considered more polite. You're requesting permission to pollute the user's namespace. It's like knocking on someone's door and asking if you can have a beer rather than barging in and rummaging through their fridge for a beer.
Note how I use sprintf to handle the replaceable parameters. This again removes the need for eval.
I also prefer to use #! /usr/bin/env perl on my program header since it's more compatible with things like Perlbrew. You're using /usr/bin/env to find the executable Perl program that's in the user's path. This way, you don't have to know whether it's /bin/perl, /usr/bin/perl, /usr/local/bin/perl, or $HOME/perl5/perlbrew/perls/perl-5.18.0/bin/perl
To use your module, I would do this:
#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
use Modules::TextStuff qw(get_text);
say get_text('tmp1','jim');
Pretty much the same call you made. This prints out:
This is a template text
by jim.

Lexically importing useful functions in a big script

Sometimes I need a useful utility function, like List::Util::max in the middle of a large program that does lots of stuff. So if I do
use List::Util 'max';
At the top of my program, I'm stuck with that symbol, polluting my whole namespace, even though I only need it in one subroutine.
So I've been thinking of trying a different pattern, instead:
use List::Util ();
# a whole bunch of stuff later...
sub blah {
List::Util->import( 'max' );
$blah = max #foobar;
...
}
There are two problems with this, though. For one, it doesn't automatically unimport at the end of the block (drat.) I would have to undo everything with an unimport.
The other problem is that apparently prototypes don't get applied correctly, so I have to say max( #foobar ) instead of the prettier parenthesisless version.
Is there an easy way to temporarily import symbols for a block, which would automagically make them go away at the end of the block, and which would also handle prototypes correctly?
Just do this, it's much better and cleaner:
package Foo;
use strict; use warnings;
use List::Util 'max';
use namespace::autoclean;
# your method definitions here...
namespace::autoclean will "unimport" the symbol after the package's compilation cycle is done. The call to it in your method will still work, but you have no namespace pollution (the *Foo::max symbol is removed) and calling $obj->max() will fail.
Alternatively, you might want to take a look at Lexical::Import (I know nothing about it; an irc birdie mentioned it).
If you only use max in one subroutine, I wouldn't import it into the namespace at all. My solution is to
use List::Util;
sub blah {
print List::Util::max(#list);
}
You can localize a symbol table entry:
use List::Util ();
#y = qw(1 3 5 -9 4);
sub max { # return maximum *absolute value* of list
my $max = abs(shift);
$max<abs($_) && ($max=$abs($_)) for #_;
return $max;
}
sub max2 {
local *max = *List::Util::max;
return max(#_);
}
print "My max: ", max(#y), "\n"; # ==> 9
print "List::Util::max ", max2(#y), "\n"; # ==> 5
perlfunc implies that no MODULE should do what you want:
sub blah {
use List::Util qw(max);
say max #foobar;
no List::Util;
}
but that doesn't work -- at least not for List::Util. I believe that it would need to define an unimport method. Even then, I'm not sure if you could have a bare max in your module call different definitions.

What does "1;" mean in Perl?

I have come across a few Perl modules that for example look similar to the following code:
package MyPackage;
use strict;
use warnings;
use constant PERL510 => ( $] >= 5.0100 );
require Exporter;
our #ISA = qw(Exporter);
our #EXPORT = qw( );
{ #What is the significance of this curly brace?
my $somevar;
sub Somesub {
#Some code here
}
}
1;
What is the significance of 1; and of the curly braces that enclose the $somevar and the Sub?
1 at the end of a module means that the module returns true to use/require statements. It can be used to tell if module initialization is successful. Otherwise, use/require will fail.
$somevar is a variable which is accessable only inside the block. It is used to simulate "static" variables. Starting from Perl 5.10 you can use keyword state keyword to have the same results:
## Starting from Perl 5.10 you can specify "static" variables directly.
sub Somesub {
state $somevar;
}
When you load a module "Foo" with use Foo or require(), perl executes the Foo.pm file like an ordinary script. It expects it to return a true value if the module was loaded correctly. The 1; does that. It could be 2; or "hey there"; just as well.
The block around the declaration of $somevar and the function Somesub limits the scope of the variable. That way, it is only accessible from Somesub and doesn't get cleared on each invocation of Somesub (which would be the case if it was declared inside the function body). This idiom has been superseded in recent versions of perl (5.10 and up) which have the state keyword.
Modules have to return a true value. 1 is a true value.
Perl modules must return something that evaluates to true. If they don't, Perl reports an error.
C:\temp>cat MyTest.pm
package MyTest;
use strict;
sub test { print "test\n"; }
#1; # commented out to show error
C:\temp>perl -e "use MyTest"
MyTest.pm did not return a true value at -e line 1.
BEGIN failed--compilation aborted at -e line 1.
C:\temp>
Although it's customary to use "1;", anything that evaluates to true will work.
C:\temp>cat MyTest.pm
package MyTest;
use strict;
sub test { print "test\n"; }
"false";
C:\temp>perl -e "use MyTest"
C:\temp> (no error here)
For obvious reasons another popular return value is 42.
There's a list of cool return values maintained at http://returnvalues.useperl.at/values.html.
The curly braces limit the scope of the local variable $somevar:
{
my $somevar;
...
} # $somevar's scope ends here
From the documentation for require:
The file must return true as the last
statement to indicate successful
execution of any initialization code,
so it's customary to end such a file
with 1; unless you're sure it'll
return true otherwise. But it's better
just to put the 1; , in case you add
more statements.
I don't know much about Perl, but usually you create a scope using curly braces. Probably $somevar shoudln't be available globally?