File Coverage

blib/lib/above.pm
Criterion Covered Total %
statement 65 76 85.5
branch 9 20 45.0
condition 7 12 58.3
subroutine 13 13 100.0
pod 0 1 0.0
total 94 122 77.0


line stmt bran cond sub pod time code
1             package above;
2              
3 12     12   77564 use strict;
  12         16  
  12         265  
4 12     12   37 use warnings;
  12         48  
  12         273  
5              
6 11     11   34 use Cwd qw(getcwd);
  11         14  
  11         445  
7 11     11   34 use File::Spec qw();
  11         14  
  11         1770  
8              
9             our $VERSION = '0.03'; # No BumpVersion
10              
11             sub import {
12 13     13   59 my $package = shift;
13 13         29 for (@_) {
14 13         24 use_package($_);
15             }
16             }
17              
18             our %used_libs;
19             BEGIN {
20 11 50   11   50 %used_libs = ($ENV{PERL_USED_ABOVE} ? (map { $_ => 1 } split(":", $ENV{PERL_USED_ABOVE})) : ());
  0         0  
21 11         5283 for my $path (keys %used_libs) {
22 0         0 my $error = do {
23 0         0 local $@;
24 0         0 eval "use lib '$path';";
25 0         0 $@;
26             };
27 0 0       0 die "Failed to use library path '$path' from the environment PERL_USED_ABOVE?: $error" if $error;
28             }
29             };
30              
31             sub _caller_use {
32 13     13   26 my ($caller, $class) = @_;
33 13         14 my $error = do {
34 13         19 local $@;
35 11     11   3585 eval "package $caller; use $class";
  11     1   23  
  11         81  
  13         772  
  1         3  
  1         1  
  1         3  
36 13         57 $@;
37             };
38 13 50       6932 die $error if $error;
39             }
40              
41             sub _dev {
42 50     50   46 my $path = shift;
43 50         704 return (stat($path))[0];
44             }
45              
46             sub use_package {
47 13     13 0 18 my $class = shift;
48 13         82 my $caller = (caller(1))[0];
49 13         314 my $module = File::Spec->join(split(/::/, $class)) . '.pm';
50              
51             ## paths already found in %used_above have
52             ## higher priority than paths based on cwd
53 13         47 for my $path (keys %used_libs) {
54 0 0       0 if (-e File::Spec->join($path, $module)) {
55 0         0 _caller_use($caller, $class);
56 0         0 return;
57             }
58             }
59              
60 13         25 my $xdev = $ENV{ABOVE_DISCOVERY_ACROSS_FILESYSTEM};
61 13         50 my $cwd = getcwd();
62 13 50       31 unless ($cwd) {
63 0         0 die "cwd failed: $!";
64             }
65 13         68 my $dev = _dev($cwd);
66             my $abort_crawl = sub {
67 49     49   90 my @parts = @_;
68 49 100 66     166 return 1 if (@parts == 1 && $parts[0] eq ''); # hit root dir
69 37         220 my $path = File::Spec->join(@parts);
70 37   33     95 return !($xdev || _dev($path) == $dev); # crossed device
71 13         56 };
72             my $found_module_at = sub {
73 63     63   48 my $path = shift;
74 63         901 return (-e File::Spec->join($path, $module));
75 13         30 };
76              
77 13         129 my @parts = File::Spec->splitdir($cwd);
78 13         19 my $path;
79 13   100     12 do {
80 50         334 $path = File::Spec->join(@parts);
81 50         84 pop @parts;
82             } until ($found_module_at->($path) || $abort_crawl->(@parts));
83              
84 13 100       27 if ($found_module_at->($path)) {
85 1         3 while ($path =~ s:/[^/]+/\.\./:/:) { 1 } # simplify
  0         0  
86 1 50       3 unless ($used_libs{$path}) {
87 1 0 33     2 print STDERR "Using libraries at $path\n" unless $ENV{PERL_ABOVE_QUIET} or $ENV{COMP_LINE};
88 1         1 my $error = do {
89 1         1 local $@;
90 1         45 eval "use lib '$path';";
91 1         76 $@;
92             };
93 1 50       3 die $error if $error;
94 1         1 $used_libs{$path} = 1;
95 1         3 my $env_value = join(":", sort keys %used_libs);
96 1         5 $ENV{PERL_USED_ABOVE} = $env_value;
97             }
98             }
99              
100 13         29 _caller_use($caller, $class);
101             };
102              
103             1;
104              
105             =pod
106              
107             =head1 NAME
108              
109             above - auto "use lib" when a module is in the tree of the PWD
110              
111             =head1 SYNOPSIS
112              
113             use above "My::Module";
114              
115             =head1 DESCRIPTION
116              
117             Used by the command-line wrappers for Command modules which are developer tools.
118              
119             Do NOT use this in modules, or user applications.
120              
121             Uses a module as though the cwd and each of its parent directories were at the beginnig of @INC.
122             If found in that path, the parent directory is kept as though by "use lib".
123              
124             Set ABOVE_DISCOVERY_ACROSS_FILESYSTEM shell variable to true value to crawl past device boundaries.
125              
126             =head1 EXAMPLES
127              
128             # given
129             /home/me/perlsrc/My/Module.pm
130              
131             # in
132             /home/me/perlsrc/My/Module/Some/Path/
133              
134             # in myapp.pl:
135             use above "My::Module";
136              
137             # does this ..if run anywhere under /home/me/perlsrc:
138             use lib '/home/me/perlsrc/'
139             use My::Module;
140              
141             =head1 AUTHOR
142              
143             Scott Smith
144             Nathaniel Nutter
145              
146             =cut