NAME
File::AddInc - a reliable shorthand of use lib dirname($FindBin::Bin)
for Modulino
SYNOPSIS
Assume you have a Modulino at $DIR/lib/MyApp.pm, and you want to use $DIR/lib/MyApp/Util.pm from it. Then:
#!/usr/bin/env perl
package MyApp;
# This manipulates @INC for you!
use File::AddInc;
# So perl can find MyApp/Util.pm from the same module tree correctly.
use MyApp::Util;
...
You can use File::AddInc
to add $DIR/lib to @INC
.
DESCRIPTION
File::AddInc manipulates @INC
for Modulino (a module which is also runnable as a command). If you don't know much about the usefulness of Modulino, See these fine articles [1] [2].
Unfortunately, there is an annoying complexity to write Modulino: @INC
manipulation. Generally, it is responsible for top-level scripts (*.pl, *.psgi) to manipulate @INC
to be able to load all modules correctly. But in programming with Modulino, the Modulino itself is the top-level. To run such Modulino, you must give -Mlib
to perl like below:
perl -Mlib=$PWD ModX.pm ...
Above is disappointingly long, especially for Perl newbies. Instead, imagine if it can be called as a command file like ./ModX.pm, like the following:
./ModX.pm ...
With the above, they can use shell's filename completion and can run it less than a second. To achieve above, you usually need to add following BEGIN {}
block to every Modulinos. (Note. You may want to name your Modulino like ModX::SomeCategory::SomeFunc
):
package ModX;
...
BEGIN {
my ($pack) = __PACKAGE__;
my $libdir = $FindBin::RealBin;
my $depth = (split "::", $pack) - 1;
$libdir = dirname($libdir) while --$depth >= 0;
require lib;
lib->import($libdir);
}
With File::AddInc, you can replace the above block with one line:
use File::AddInc;
Conceptually, this module locates the root of lib directory through the following steps.
Inspect
__FILE__
(using caller()).Resolve symbolic links.
Trim
__PACKAGE__
part from it.
Then adds it to @INC
.
Also, File::AddInc can be used to find the library's root directory reliably. FindBin is enough to manipulate @INC
but not work well to locate something other than use
-ed Perl modules. For example, assume you have a Modulino $DIR/lib/ModY.pm, and it uses some assets under $DIR/assets. You may write ModY
with FindBin
like following:
package ModY;
...
use lib (my $app_dir = dirname(FindBin::RealBin));
our $assets_dir = "$app_dir/assets";
Unfortunately, the above code doesn't work as expected, because FindBin relies on $0
and varies what top-level program uses this ModY
. In such a case, we should use __FILE__
instead of $0
.
package ModY;
...
use lib (my $app_dir = dirname(File::Spec->rel2abs(__FILE__)));
our $assets_dir = "$app_dir/assets";
Unfortunately again, this won't work if ModY.pm is symlinked to somewhere. With File::AddInc, you can rewrite it and can handle symlinks correctly:
package ModY;
...
use File::AddInc;
my $app_dir = dirname(File::AddInc->libdir);
our $assets_dir = "$app_dir/assets";
SUB-PRAGMAS
If you give some arguments to this module, it will treat them as subpragmas. This module invokes corresponding class methods for each subpragmas as the specified order. You can specify any number of subpragmas. If you give no subpragmas, a subpragma -file_inc
is assumed. There are three forms of subpragmas in this module. That is -PRAGMA
, [PRAGMA => @ARGS]
and qw($var)
.
For example, following code:
use File::AddInc -file_inc
, [libdir_var => qw($libdir)]
, qw($libdir2);
is a shorthand of below:
BEGIN {
require File::AddInc;
my $opts = File::AddInc->Opts->new(caller => [caller]);
File::AddInc->declare_file_inc($opts);
File::AddInc->declare_libdir_var($opts, qw($libdir));
File::AddInc->declare_libdir_var($opts, qw($libdir2));
}
-file_inc
This finds libdir from caller and add it to @INC
by "add_inc_if_necessary". This is the default behavior of this module. In other words,
use File::AddInc;
is a shorthand form of below:
use File::AddInc -file_inc;
-local_lib
This also adds $DIR/local/lib/perl5 to @INC
(assumes your module is under $DIR/lib). This subpragma is now implemented in "these_libdirs" subpragma. In other words,
use File::AddInc -local_lib;
is a shorthand form of below:
use File::AddInc [these_libdirs => '', [dirname => "local/lib/perl5"]];
qw($var)
This finds libdir from caller and set it to given scalar variable. This subpragma is now implemented in "libdir_var" subpragma. In other words,
use File::AddInc qw($foo);
is a shorthand form of below:
use File::AddInc [libdir_var => qw($foo)];
[libdir_var => qw($libdir)]
This finds libdir from caller and set it to given scalar variable.
use File::AddInc [libdir_var => qw($foo)];
is an equivalent of the folloing:
use File::AddInc ();
our $foo; BEGIN { $foo = File::AddInc->libdir };
[these_libdirs => @dirSpec]
This finds libdir from caller, generate a list of directories from given @dirSpec
and prepend them to @INC
by "add_inc_if_necessary".
For example, following code:
use File::AddInc [these_libdirs => 'etc', '', [dirname => "local/lib/perl5"]];
adds $libdir/etc
, $libdir
and dirname($libdir)."/local/lib/perl5")
to @INC
.
Each item of @dirSpec
can be one of following two forms:
STRING
In this case,
$libdir."/STRING"
will be added.[dirname => STRING]
In this case,
dirname($libdir)."/STRING"
will be added.
CLASS METHODS
->libdir($PACKNAME, $FILEPATH)
Trims $PACKNAME
portion from $FILEPATH
. When arguments are omitted, results from caller() is used.
my $libdir = File::AddInc->libdir('MyApp::Foobar', "/somewhere/lib/MyApp/Foobar.pm");
# $libdir == "/somewhere/lib"
my $libdir = File::AddInc->libdir(caller);
my $libdir = File::AddInc->libdir;
->add_inc_if_necessary(@libdir)
This method prepends @libdir
to @INC
unless it is already listed in there. Note: this comparison is done through exact match.
MISC
How to inherit and extend
You can inherit this module to implement custom @INC
modifier. For example, you can write your own exporter to invoke declare_these_libdirs
to give traditional pragma usage like following:
use MyExporter 'etc', '', 'perl5';
Such MyExporter.pm could be written like folloing:
package MyExporter;
use strict;
use warnings;
use parent qw/File::AddInc/;
sub import {
my ($pack, @args) = @_;
my $opts = $pack->Opts->new(caller => [caller]);
$pack->declare_these_libdirs($opts, @args);
}
1;
Note for MOP4Import users
This module does *NOT* rely on MOP4Import::Declare but designed to work well with it. Actually, this module provides declare_file_inc
method. So, you can inherit 'File::AddInc' to reuse this pragma.
package MyExporter;
use MOP4Import::Declare -as_base, [parent => 'File::AddInc'];
Then you can use -file_inc
pragma like following:
use MyExporter -file_inc;
CAVEATS
Since this module compares __FILE__
with __PACKAGE__
in a case sensitive manner, it may not work well with modules which rely on case insensitive filesystems.
SEE ALSO
LICENSE
Copyright (C) Kobayasi, Hiroaki.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
AUTHOR
Kobayasi, Hiroaki <buribullet@gmail.com>