File Coverage

lib/lib/abs.pm
Criterion Covered Total %
statement 70 76 92.1
branch 18 30 60.0
condition 18 24 75.0
subroutine 13 15 86.6
pod 1 3 33.3
total 120 148 81.0


line stmt bran cond sub pod time code
1             #
2             # Copyright (c) 2007-2010 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.94';
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   483206 use 5.006;
  7         30  
101 7     7   40 use strict;
  7         15  
  7         169  
102 7     7   38 use warnings;
  7         13  
  7         225  
103 7     7   38 use lib ();
  7         13  
  7         206  
104 7     7   39 use Cwd 3.12 qw(abs_path);
  7         125  
  7         1356  
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   3190 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   11 sub _croak { require Carp; goto &Carp::croak }
  2         328  
114 0     0   0 sub _debug ($@) { printf STDERR shift()." at @{[ (caller)[1,2] ]}\n",@_ }
  0         0  
115              
116             sub mkapath($) {
117 65     65 0 101 my $depth = shift;
118            
119             # Prepare absolute base bath
120 65         435 my ($pkg,$file) = (caller($depth))[0,1];
121 65         128 _debug "file = $file " if DEBUG > 1;
122 65         503 $file =~ s/${lib::abs::sep}//s;
123 65 50       186 $file = '.' unless length $file;
124 65         82 _debug "base path = $file" if DEBUG > 1;
125 65         1619 my $f = abs_path($file) . '/';
126 65         147 _debug "source dir = $f " if DEBUG > 1;
127 65         291 $f;
128             }
129              
130             sub path {
131 12     12 1 11414 local $_ = shift;
132 12         29 s{^\./+}{};
133 12         60 local $!;
134 12         29 my $abs = mkapath(1) . $_;
135 12 0       474 my $ret = abs_path( $abs ) or _carp("Bad path specification: `$_' => `$abs'" . ($! ? " ($!)" : ''));
    50          
136 12         29 _debug "$_ => $ret" if DEBUG > 1;
137 12         96 $ret;
138             }
139             our $SOFT;
140             sub transform {
141 65     65 0 85 my $prefix;
142 7     7   60 no warnings 'uninitialized';
  7         16  
  7         4207  
143             map {
144 65 100 100     121 ref || m{^/} ? $_ : do {
  107         505  
145 83         121 my $lib = $_;
146 83         192 s{^\./+}{};
147 83         793 local $!;
148 83   66     273 my $abs = ( $prefix ||= mkapath(2) ) . $_;
149 83 100 100     347 if (index($abs,'*') != -1 or index($abs,'?') !=-1) {
150 12         50 _debug "transforming $abs using glob" if DEBUG > 1;
151             map {
152 12         1311 my $x;
  30         62  
153 30 0 33     2024 $x = abs_path( $_ ) and -d $x
    0 33        
154             or $SOFT or _croak("Bad path specification: `$lib' => `$x'" . ($! ? " ($!)" : ''));
155 30 50       535 defined $x ? ($x) : ();
156             } glob $abs;
157             } else {
158 71         102 local $@;
159 71         117 eval {
160 71         2718 $_ = abs_path( $abs );
161             };
162 71 50 66     1006 $_ and !$@ and -d $_
    100 100        
      100        
163             or $SOFT or _croak("Bad path specification: `$lib' => `$abs'" . ($! ? " ($!)" : ''));
164 69         130 _debug "$lib => $_" if DEBUG > 1;
165 69 100       441 defined $_ ? ($_) : ();
166             }
167             }
168             } @_;
169             }
170              
171             sub import {
172 54     54   72436 shift;
173 54 100       170 return unless @_;
174 53         91 my $soft = 0;
175 53 100       131 if ($_[0] eq '-soft') {
176 2         3 $soft = 1;
177 2         4 shift @_;
178             }
179 53         85 local $SOFT = $soft;
180 53         163 @_ = ( lib => transform @_ = @_ );
181 51         85 _debug "use @_\n" if DEBUG > 0;
182 51         210 goto &lib::import;
183 0         0 return;
184             }
185              
186             sub unimport {
187 12     12   13919 shift;
188 12 50       40 return unless @_;
189 12         52 @_ = ( lib => transform @_ = @_ );
190 12         25 _debug "no @_\n" if DEBUG > 0;
191 12         53 goto &lib::unimport;
192 0           return;
193             }
194              
195             1;