File Coverage

lib/lib/abs.pm
Criterion Covered Total %
statement 69 75 92.0
branch 18 30 60.0
condition 15 21 71.4
subroutine 13 15 86.6
pod 1 3 33.3
total 116 144 80.5


line stmt bran cond sub pod time code
1             #
2             # Copyright (c) 2007-2020 Mons Anderson . All rights reserved
3             # This program is free software; you can redistribute it and/or
4             # modify it under the same terms as Perl itself.
5             package lib::abs;
6              
7             =head1 NAME
8              
9             lib::abs - C that makes relative path absolute to caller.
10              
11             =cut
12              
13             our $VERSION = '0.95';
14              
15             =head1 SYNOPSIS
16              
17             Simple use like C:
18              
19             use lib::abs qw(./mylibs1 ../mylibs2);
20             use lib::abs 'mylibs';
21            
22             # if your path may not exists and it is ok, then:
23             use lib::abs -soft => qw(./mylibs1 ../mylibs2);
24              
25             Extended syntax (glob)
26              
27             use lib::abs 'modules/*/lib';
28              
29             There are also may be used helper function from lib::abs (see example/ex4):
30              
31             use lib::abs;
32             # ...
33             my $path = lib::abs::path('../path/relative/to/me'); # returns absolute path
34              
35             =head1 DESCRIPTION
36              
37             The main reason of this library is transformate relative paths to absolute at the C stage, and push transformed to C<@INC>.
38             Relative path basis is not the current working directory, but the location of file, where the statement is (caller file).
39             When using common C, relative paths stays relative to curernt working directory,
40              
41             # For ex:
42             # script: /opt/scripts/my.pl
43             use lib::abs '../lib';
44              
45             # We run `/opt/scripts/my.pl` having cwd /home/mons
46             # The @INC will contain '/opt/lib';
47              
48             # We run `./my.pl` having cwd /opt
49             # The @INC will contain '/opt/lib';
50              
51             # We run `../my.pl` having cwd /opt/lib
52             # The @INC will contain '/opt/lib';
53              
54             Also this module is useful when writing tests, when you want to load strictly the module from ../lib, respecting the test file.
55              
56             # t/00-test.t
57             use lib::abs '../lib';
58              
59             Also this is useful, when you running under C, use something like C, and your application may change working directory.
60             So in case of chdir C fails to reload module if the @INC contain relative paths.
61              
62             =head1 RATIONALE
63              
64             Q: We already have C and C, why we need this module?
65              
66             A: There are several reasons:
67              
68             =over 4
69              
70             =item 1) C could find path incorrectly under C
71              
72             =item 2) C works relatively to executed binary instead of relatively to caller
73              
74             =item 3) Perl is linguistic language, and C<`use lib::abs "..."'> semantically more clear and looks more beautiful than C<`use FindBin; use lib "$FindBin::Bin/../lib";'>
75              
76             =item 4) C b work incorrectly, if will be called not from executed binary (see L comparison for details)
77              
78             =back
79              
80             =head1 BUGS
81              
82             None known
83              
84             =head1 COPYRIGHT AND LICENSE
85              
86             This software is copyright (c) 2007-2020 by Mons Anderson.
87              
88             This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
89              
90             =head1 AUTHOR
91              
92             Mons Anderson, C<< >>
93              
94             =head1 CONTRIBUTORS
95              
96             Oleg Kostyuk, C<< >>
97              
98             =cut
99              
100 7     7   479569 use 5.006;
  7         30  
101 7     7   42 use strict;
  7         14  
  7         148  
102 7     7   36 use warnings;
  7         15  
  7         220  
103 7     7   35 use lib ();
  7         17  
  7         239  
104 7     7   38 use Cwd 3.12 qw(abs_path);
  7         182  
  7         1376  
105             $lib::abs::sep = {
106             ( map { $_ => qr{[^\\/]+$}o } qw(mswin32 netware symbian dos) ),
107             ( map { $_ => qr{[^:]+:?$}o } qw(macos) ),
108             }->{lc$^O} || qr{[^/]+$}o;
109              
110 7 50   7   3185 BEGIN { *DEBUG = sub () { 0 } unless defined &DEBUG } # use constants is heavy
111              
112 0     0   0 sub _carp { require Carp; goto &Carp::carp }
  0         0  
113 2     2   13 sub _croak { require Carp; goto &Carp::croak }
  2         405  
114 0     0   0 sub _debug ($@) { printf STDERR shift()." at @{[ (caller)[1,2] ]}\n",@_ }
  0         0  
115              
116             sub mkapath($) {
117 65     65 0 92 my $depth = shift;
118            
119             # Prepare absolute base bath
120 65         436 my ($pkg,$file) = (caller($depth))[0,1];
121 65         131 _debug "file = $file " if DEBUG > 1;
122 65         506 $file =~ s/${lib::abs::sep}//s;
123 65 50       185 $file = '.' unless length $file;
124 65         82 _debug "base path = $file" if DEBUG > 1;
125 65         1589 my $f = abs_path($file) . '/';
126 65         163 _debug "source dir = $f " if DEBUG > 1;
127 65         250 $f;
128             }
129              
130             sub path {
131 12     12 1 10619 local $_ = shift;
132 12         31 s{^\./+}{};
133 12         54 local $!;
134 12         32 my $abs = mkapath(1) . $_;
135 12 0       447 my $ret = abs_path( $abs ) or _carp("Bad path specification: `$_' => `$abs'" . ($! ? " ($!)" : ''));
    50          
136 12         30 _debug "$_ => $ret" if DEBUG > 1;
137 12         88 $ret;
138             }
139             our $SOFT;
140             sub transform {
141 65     65 0 93 my $prefix;
142 7     7   62 no warnings 'uninitialized';
  7         15  
  7         3872  
143             map {
144 65 100 100     125 ref || m{^/} ? $_ : do {
  107         520  
145 83         116 my $lib = $_;
146 83         215 s{^\./+}{};
147 83         440 local $!;
148 83   66     264 my $abs = ( $prefix ||= mkapath(2) ) . $_;
149 83 100 100     351 if (index($abs,'*') != -1 or index($abs,'?') !=-1) {
150 12         23 _debug "transforming $abs using glob" if DEBUG > 1;
151             map {
152 12         1396 my $x;
  30         61  
153 30 0 33     2070 $x = abs_path( $_ ) and -d $x
    0 33        
154             or $SOFT or _croak("Bad path specification: `$lib' => `$x'" . ($! ? " ($!)" : ''));
155 30 50       193 defined $x ? ($x) : ();
156             } glob $abs;
157             } else {
158 71 50 66     110 eval {
    100 100        
159 71         2625 $_ = abs_path( $abs );
160 71         979 1} and -d $_
161             or $SOFT or _croak("Bad path specification: `$lib' => `$abs'" . ($! ? " ($!)" : ''));
162 69         143 _debug "$lib => $_" if DEBUG > 1;
163 69 100       400 defined $_ ? ($_) : ();
164             }
165             }
166             } @_;
167             }
168              
169             sub import {
170 54     54   68023 shift;
171 54 100       160 return unless @_;
172 53         90 my $soft = 0;
173 53 100       126 if ($_[0] eq '-soft') {
174 2         4 $soft = 1;
175 2         2 shift @_;
176             }
177 53         86 local $SOFT = $soft;
178 53         176 @_ = ( lib => transform @_ = @_ );
179 51         83 _debug "use @_\n" if DEBUG > 0;
180 51         443 goto &lib::import;
181 0         0 return;
182             }
183              
184             sub unimport {
185 12     12   15060 shift;
186 12 50       37 return unless @_;
187 12         41 @_ = ( lib => transform @_ = @_ );
188 12         26 _debug "no @_\n" if DEBUG > 0;
189 12         49 goto &lib::unimport;
190 0           return;
191             }
192              
193             1;