KiraLive
KiraLive

Reputation: 23

Perl - Relative path of a file inside a module

I am building up a website using Perl. I organized my files as follow:

Inside databaseFunctions.pm I have a function X() that reads and writes on database.xml. In X() I have specified relative path of the database as follow:

sub X{
    my $db_path='../../database/database.xml';
    my $parser=XML::LibXML->new();
    my $doc=$parser->parse_file($db_path);
    ....
    ....
 }

Here is the problem:
I have to call X() from index.cgi and indexCheck.cgi but I get an error the following error:

Could not create file parser context for file "../../database/database.xml": No such file or directory at perl/modules/databaseFunctions.pm line 21.

I think the problem is that when I call X() inside index.cgi or inside /perl/indexCheck.cgi the relative path of the database is different but I don't know how to set a path that works for index.cgi and /perl/indexCheck.cgi.

Upvotes: 2

Views: 757

Answers (1)

PerlDuck
PerlDuck

Reputation: 5720

I think the problem boils down to "How to find out the path of the current script (*.pl)?" and "How to find out the path of the current module (*.pm)?".

Scripts

For scripts, there is a very convenient module, FindBin, that offers 4 variables for the current script's name and path with either symlinks resolved or not. Usually $FindBin::Bin is what you are looking for. It's the path of the current script. I often use it to enhance the @INC path so that my scripts find additional (own) modules like so:

use FindBin;
use lib "$FindBin::Bin/my_mod_path";
use MyModule;

In this case MyModule.pm is searched for in the directory my_mod_path below the current script's path. Very convenient.

The module is part of the core distribution, i.e. no further installation is neccessary.

Modules

FindBin may not safely be used from inside modules because then it depends who (script or module) makes the first use FindBin;. So if you don't want to care about the order, don't use FindBin; in modules, only in scripts.

For modules, there is some trick. Use the perl function caller(). Depending on the context called in, it returns the $filename of the file where it actually was called.

Thus, in modules you can safely use the following to get the module's path:

use File::Basename;
my $path_of_this_module = File::Basename::dirname( eval { ( caller() )[1] } );

Given that path you can navigate relative to it in order to find the other files you need, e.g. "$path_of_this_module/../.." and so on.

EDIT

index.cgi:

#!/usr/bin/env perl

use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/perl/modules";
use databaseFunctions;

databaseFunctions::X( "called from index.cgi\n" );

perl/indexCheck.cgi:

#!/usr/bin/env perl

use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/modules";
use databaseFunctions;

databaseFunctions::X( "called from indexCheck.cgi\n" );

perl/modules/databaseFunctions.pm:

package databaseFunctions;

use File::Basename;

my $path_of_this_module = File::Basename::dirname( eval { ( caller() )[1] } );

sub X {
    my $arg = shift;

    my $db_path="$path_of_this_module/../../database/database.xml";

    open(my $fh, '>>', $db_path) or die "cannot open $db_path: $!\n";
    print $fh $arg;
    close($fh);
}

1;

When I now call ./index.cgi and then ./perl/indexCheck.cgi, then I get the following:

database/database.xml:

called from index.cgi
called from indexCheck.cgi

Exactly, what I thought you were looking for.

Upvotes: 3

Related Questions