framontb
framontb

Reputation: 2067

Perl: Use custom nested modules organised in nested sub-directories

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

Answers (1)

ikegami
ikegami

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

Related Questions