Reputation: 2067
I'm trying to figure out how to use nested modules organised in nested sub-directories with Perl. I mean, a test.pl program use a Foo module, and this in turn use another Bar module and so on... Let's put a little example, files could be organised in directories like that:
./test.pl
./lib/Foo.pm
./lib/common/Bar.pm
First thing that comes to mind is using the FindBin module in test.pl like that:
use FindBin;
use lib "$FindBin::RealBin/lib/.";
use Foo;
But if you want to do the same inside Foo to "use Bar", you need to include all the relative path from the test.pl program, including "/lib" segment. That means Foo needs to be aware of his relative path to the calling program. This forces kind of rigidity in the directories structure. For example, you can't simply copy&paste your custom modules wherever you want and call it. Besides, it needs to install the FindBin module in order to work.
In order to solve this issues, googling I found this solution: BEGIN solution that directly add paths to @INC. With this in mind, a solution could be:
./test.pl
#!/usr/bin/perl
use strict;
use warnings;
# Include lib in @INC
BEGIN {
use File::Basename;
my($filename, $dirs, $suffix) = fileparse(__FILE__);
my $common_path = $dirs."lib/.";
unshift(@main::INC, $common_path) ;
}
use Foo;
print "Inside: $Foo::message";
./lib/Foo.pm
package Foo;
# Include Common library
BEGIN {
use File::Basename;
my($filename, $dirs, $suffix) = fileparse(__FILE__);
$common_path = $dirs."common/.";
unshift(@main::INC, $common_path) ;
}
use Bar;
$message = " Foo > $Bar::message";
1;
./lib/common/Bar.pm
package Bar;
$message = "Bar";
1;
Executing ./test.pl should print:
Inside: Foo > Bar
You could nest any number of modules in their respective directories (I tested three levels), and what is better, copy&paste at whatever point of path without breaking functionality. Nevertheless I dont't know if this method is advisable or have any disadvantages (I'm newby with perl). For example, is it advisable editing @INC directly like this?. is it advisable use the BEGIN code block for that?. Or, are there better ways to do this (allowing copy&paste directories at arbitrary points of the modules directories structure) so it works without touching code inside the modules?
Upvotes: 4
Views: 548
Reputation: 385496
Don't change @INC
in modules. Leave that to the script.
use FindBin qw( $RealBin );
use lib "$RealBin/lib", "$RealBin/lib/common";
That means Foo needs to be aware of his relative path to the calling program.
If the libs are there for the script and each other, that's not a problem, and using use lib
makes sense.
Otherwise, it's just a module for any script to use, and use lib
shouldn't be used. Instead, it should be installed into standard installation directory (or a custom one specified by env var PERL5LIB
).
By the way, you probably shouldn't put common
inside of lib
. This implies you can do use common::Bar;
, which would be wrong.
Upvotes: 4