Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Path relative to package

by DreamT (Pilgrim)
on Jun 01, 2018 at 14:49 UTC ( #1215639=perlquestion: print w/replies, xml ) Need Help??

DreamT has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I have the following components:
1. A package, /App/Package.pm . The task of the package includes opening a file.
package App::Package; use File::Slurp; sub get_config { my $json = read_file("???/Config/Config.json"); #... return $something; }
2. A config file, located in a subfolder: /App/Config/Config.json.

3. A script that can be located outside the directory structure.
use lib 'Some/Random/Path/To/App'; my $result = get_config();
My problem occurs when I try to read the config file; The problem is that I don't know the relative path between the package and the config file.
• I know that I can use FindBin to get the path to the executing script - But I want the package to figure out the path "on it's own". It should be aware of the path to the config file without depending on the calling script
• I also know that the path is available in @INC (via use lib...). But I don't know how to get the correct one from here. I guess that I could regex it, but...I'm looking for a more secure method to obtain it.

Any ideas?

Replies are listed 'Best First'.
Re: Path relative to package
by hippo (Bishop) on Jun 01, 2018 at 15:01 UTC
    I also know that the path is available in @INC (via use lib...). But I don't know how to get the correct one from here.

    Using DBI.pm as an example of a file to find:

    #!/usr/bin/env perl use strict; use warnings; my ($path) = grep { -f "$_/DBI.pm" } @INC; print $path;

    Not absolutely the most efficient, but it will work.

Re: Path relative to package
by haj (Curate) on Jun 01, 2018 at 20:37 UTC

    Well, you can iterate over @INC, or directly evaluate $INC{'App/Package.pm'}, or use __FILE__ as already suggested. But I'm aware that you are looking for a more secure method, and here it can get murky.

    If you have relative paths in @INC you must assert that your code did not do a chdir between loading the library and reading the config file, or you might either not find your config file, or read some other file which per accident (or per malicious action) has the relative path you are using after changing the directory.

    I would also consider whether putting a configuration file in the library path is a good idea. If the contents of the config file never change, then you have the option of putting them in a __DATA__ section of a config module, and have get_config() reading from the DATA file handle in that module. If, however, the contents may change over time, then you need to grant write permission to a file within the library path, which doesn't play well with the module installers and may create issues when upgrading the application.

    I recommend to check out CPAN's File::HomeDir module, which has functions like my_dist_data and my_dist_config to obtain paths to directories for data and configuration files. They are outside the library path, though, so your installation / initialisation steps need to be adjusted accordingly.

Re: Path relative to package
by bliako (Monsignor) on Jun 01, 2018 at 16:47 UTC

    What about __FILE__?

    A special token that returns the name of the file in which it occurs.
Re: Path relative to package
by tinita (Parson) on Jun 01, 2018 at 21:38 UTC
Re: Path relative to package
by Your Mother (Archbishop) on Jun 01, 2018 at 22:54 UTC

    One of many ways including the __FILE__ suggestion from before, tested–

    package App::Moo; use strictures; use Path::Tiny; use JSON::XS; my $__root__; sub root { $__root__ ||= path(__FILE__)->parent->parent->absolute; } sub get_config { decode_json( path( root(), "Config.json" )->slurp ); } 1;
Re: Path relative to package
by Anonymous Monk on Jun 01, 2018 at 14:54 UTC
Re: Path relative to package
by ikegami (Pope) on Jun 01, 2018 at 23:42 UTC
    use Cwd qw( abs_path ); use File::Basename qw( dirname ); my $dir_of_pm = dirname(abs_path(__FILE__));

    BUT! This sounds like a job for File::ShareDir (or maybe File::HomeDir).

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1215639]
Approved by marto
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (3)
As of 2021-10-21 22:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My first memorable Perl project was:







    Results (85 votes). Check out past polls.

    Notices?