Reputation: 23
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
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)?".
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.
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.
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