PrePAN

Sign in to PrePAN

File::FindLocalLib easier loading of local libs

Good

Synopsis

  # your_project/bin/somewhere/deep/down/script.pl
  use strict;
  use warnings;
  # look up from the file's location until we find a directory containing a directory
  # named 'lib'. Add this dir to @INC
  use File::FindLocalLib qw(lib);

  # look up until we find a dir that contains both 'lib' and 'foo', add both to @INC
  use File::FindLocalLib qw(lib foo);

  # look up until we find 'lib' and 'local'. Add 'lib' to @INC, load 'local' via local::lib
  use File::FindLocalLib qw(lib local::lib=local);

  # based on the dir we found earlier, go up one dir and try to add
  # 'Your-OtherModule/lib' and 'Dark-PAN/lib' to @INC
  File::FindLocalLib->load_extra(Your-OtherModule Dark-PAN);

Description

I'm usually using a setup like this:

 .
 ├── AProject
 │   ├── bin
 │   │   ├── db
 │   │   │   └── init.pl
 │   │   ├── onetime
 │   │   │   ├── fixup
 │   │   │   │   └── RT666_fix_up_fubared_data.pl
 │   │   │   └── import_data.pl
 │   │   └── web.psgi
 │   ├── lib
 │   └── local
 ├── MyHelperStuff
 │   └── lib
 └── CoolLib-NotYetOnCPAN
     └── lib

There is AProject, which is the actual code I'm working on. There is also probably BProject, e.g. another microservice for the same customer. A-Project has it's own code in lib and it's CPAN dependencies in local (managed via Carton and used via local::lib). There are a bunch of scripts / "binaries" in bin, in a lot of different directories of varying depth.

I have some generic helper code I use in several projects in MyHelperStuff/lib. It will never go to CPAN. I have some other code in CoolLib-NotYetOnCPAN/lib (but it might end up on CPAN if I ever get to clean it up...)

File::FindLocalLib makes it easy to add all these paths to @INC so I can use the code.

In each script, I just have to say:

use File::FindLocalLib qw(lib local::lib=local);

lib is added to the beginning of @INC, and local is loaded via local::lib, without me having to know how deep in bin the current script is located.

I can also add

File::FindLocalLib->load_extra(qw(MyHelperStuff CoolLib-NotYetOnCPAN));

to get my other code pushed to @INC. (Though currently I put this line, and some other setup code like initialising Log::Any into AProject::Run, and just use AProject::Run;)

Comments

I don't really like the name I came up with, but didn't find anything better. Some ideas:

* File::FindProjectRoot - does not make it clear the I also manipulate INC
* File::FindRootAndLoadLibs - ugly!
* FindRoot::LoadLibs - top-level namespace?

any ideas?
It could even be a lower-case, pragma-like name, as all the other @INC-manipulating modules I am aware of (lib, local::lib, blib). lib::find? libX::find?

Possibly similar CPAN modules:
* https://metacpan.org/pod/Find::Lib
* https://metacpan.org/pod/File::FindLib
I don't like pragma-like names for non-pragmas - and File::FindLib was a module I was investigating before i realized that it's not doing enought of what i want.

But thanks for the feedback!
Hi Domm. Sounds like a useful module.

I'm not keen on its name starting File::Find*, because that makes it sound a bit like it's going to be yet another variant on a generic file-finding (for when an application needs to do that as part of its functionality) rather than setting up its own infrastructure.

Given its a smarter variant on writing a use lib line, something like lib::projectroot would work for me, because it means changing lines like:

use lib '../../../lib';

into:

use lib::projectroot 'lib';

It doesn't really need “find” in there, because that's sort-of an implementation detail: the point of the module is that it adds your project's lib directories to the search path; that it has to find them in order to do so is just how it works.

Also, since lib is the common name that many people use for, it'd be nice for that to be the default if no directory names are specified, so one could just do:

use lib::projectroot;

I take your point about this not really being a pragma, but @INC-manipulation is something that affects how the rest of your program is interpreted, and fitting in with other lowercase @INC-adding modules is an advantage. But Lib::ProjectRoot could work too.
I quite like Smylers suggestions!

Just make sure that it doesn't pose a security vulnerability maybe by limiting the number of upper directories to search.
Perl stopped searching in the current directory for modules some versions ago as far as I remember because it served as a possible attack vector.
I do also like Smylers suggestions, and I'm even considering going with lib::projectroot.

@abraxxa: I'll look into the security issues, but if can add the proposed module to a script and run the script, I think you'll always be able to load any other code anyway. This might of course not be true if the module is running as part of a bigger system where users can add their own code, but I still think that - given the fact that @INC is public - you cannot really prevent the owner of some code to run any code they want

Please sign up to post a review.