File Coverage

blib/lib/Module/UseFrom.pm
Criterion Covered Total %
statement 108 111 97.3
branch 21 24 87.5
condition 9 10 90.0
subroutine 19 19 100.0
pod 0 5 0.0
total 157 169 92.9


line stmt bran cond sub pod time code
1             package Module::UseFrom;
2             {
3             $Module::UseFrom::VERSION = '0.03_001';
4             }
5              
6 6     6   327109 use strict;
  6         16  
  6         259  
7 6     6   47 use warnings;
  6         14  
  6         182  
8              
9 6     6   99 use v5.8.1;
  6         20  
  6         290  
10              
11 6     6   31 use Carp;
  6         8  
  6         636  
12 6     6   30395 use Module::CoreList;
  6         3142197  
  6         99  
13 6     6   4816 use Scalar::Util qw/dualvar/;
  6         16  
  6         1125  
14 6     6   6489 use ExtUtils::Installed;
  6         3477703  
  6         382  
15 6     6   53 use version 0.77;
  6         204  
  6         63  
16              
17 6     6   9876 use Devel::Declare ();
  6         56830  
  6         2818  
18              
19             my $inst = ExtUtils::Installed->new();
20              
21             our $verbose;
22             my %export_ok = (
23             'use_from' => { const => \&rewrite_use_from },
24             'use_if_available' => { const => \&rewrite_use_if_available },
25             );
26              
27             ######################
28             ## utility functions
29              
30             sub _my_warn {
31 47     47   62 my $string = shift;
32 47 100       108 if ($verbose) {
33 41 50 50     115 if ((ref $verbose || '') eq 'SCALAR') {
34 41         111 $$verbose .= "$string\n";
35             } else {
36 0         0 warn $string;
37             }
38             }
39             }
40              
41             sub find_module_version {
42 7     7 0 9 my $module = shift;
43              
44             my $version =
45 7 100 100     14 eval { $inst->version($module) } ||
      100        
46             exists $Module::CoreList::version{$]}{$module}
47             ? ( $Module::CoreList::version{$]}{$module} || 1e-7 )
48             : 0;
49              
50             # some core module do not have version numbers, they are returned as 1e-7
51              
52 7         3338 $version = version->parse($version);
53              
54 7 100       178 _my_warn $version ? "\tFound version $version" : "\tCouldn't find version info for $module!";
55              
56 7         33 return $version;
57             }
58              
59             sub get_varref_by_name {
60 9     9 0 18 my ($caller, $var) = @_;
61 9         23 my $varname = $caller . '::' . $var;
62              
63 9         33 _my_warn "\tInvestigating package $caller for variable $caller, resolved as $varname";
64              
65 6     6   136 no strict 'refs';
  6         13  
  6         1713  
66 9         38 my $varref =
67 9         21 (defined ${$varname})
68 9 50       15 ? \${$varname}
69             : croak "Cannot access variable \$$varname";
70              
71 9         20 return $varref;
72             }
73              
74             ###########
75             ## import
76              
77             sub import {
78 6     6   88 my $class = shift;
79              
80             # setup from explicit imports
81 6         18 my $export = {};
82 6         20 foreach my $keyword (@_) {
83              
84             # :all tag
85 3 100       21 if ($keyword eq ':all') {
86 1         2 $export = \%export_ok;
87 1         3 last;
88             }
89              
90             # check import is available
91 2 50       10 unless ( exists $export_ok{$keyword} ) {
92 0         0 carp "Keyword $keyword is not exported by Module::UseFrom";
93 0         0 next;
94             }
95              
96             # setup specific keyword
97 2         7 $export->{$keyword} = $export_ok{$keyword};
98             }
99              
100             # if called without explicit imports
101 6 100       42 unless (keys %$export) {
102 3         8 $export->{'use_from'} = $export_ok{'use_from'};
103             }
104              
105 6         25 my $caller = caller;
106              
107 6         83 Devel::Declare->setup_for( $caller, $export );
108              
109 6         167 foreach my $keyword (keys %$export) {
110 6     6   42 no strict 'refs';
  6         11  
  6         5166  
111 7     9   77 *{$caller.'::'.$keyword} = sub (@) {};
  7         6107  
  9         37  
112             }
113              
114             }
115              
116             ##################
117             ## rewrite rules
118              
119             sub rewrite_use_from {
120 2     2 0 53616 my $linestr = Devel::Declare::get_linestr;
121              
122 2         11 _my_warn "use_from got: $linestr";
123              
124 2         7 my $caller = Devel::Declare::get_curstash_name;
125              
126 2         23 $linestr =~ s/use_from\s+\$(\w+)/
127 2         9 my $varref = get_varref_by_name($caller, $1);
128 2         4 my $module = $$varref;
129 2         6 "use_from; use $module";
130             /e;
131            
132 2         9 _my_warn "use_from returned: $linestr";
133              
134 2         14 Devel::Declare::set_linestr($linestr);
135             }
136              
137             sub rewrite_use_if_available {
138 7     7 0 3320749 my $linestr = Devel::Declare::get_linestr;
139              
140 7         26 _my_warn "use_if_available got: $linestr";
141              
142 7         18 my $caller = Devel::Declare::get_curstash_name;
143              
144 7         67 $linestr =~ s/use_if_available\s+\$(\w+)(\s+[^\s;]+)?/
145 7         23 my $name = $1;
146 7         12 my $version = $2;
147 7         18 do_use_if_available($caller, $name, $version);
148             /e;
149            
150 7         24 _my_warn "use_if_available returned: $linestr";
151              
152 7         75 Devel::Declare::set_linestr($linestr);
153             }
154              
155             sub do_use_if_available {
156 7     7 0 16 my ($caller, $name, $version) = @_;
157 7         11 my $return = 'use_if_available ';
158              
159 7         17 my $varref = get_varref_by_name($caller, $name);
160 7         14 my $module = $$varref;
161              
162 7         18 _my_warn "\tFound request for module $module";
163              
164 7         15 my $found_version = find_module_version($module);
165              
166 7 100       81 unless ($found_version) {
167 1         7 my $dualvar = dualvar 0, $module;
168 1         2 $$varref = $dualvar;
169 1         7 return $return;
170             }
171              
172 6         19 my $requested_version;
173 6 100       15 if ($version) {
174 4         6 $requested_version = eval { version->parse($version) };
  4         22  
175 4         16 _my_warn "\tRequested version $requested_version";
176             }
177              
178 6 100 100     61 if (defined $requested_version and $requested_version > $found_version) {
179 2         5 _my_warn "\tInsufficient version found, skipping import!";
180 2         6 my $dualvar = dualvar 0, $module;
181 2         2 $$varref = $dualvar;
182 2         12 return $return;
183             }
184              
185 4         54 my $dualvar = dualvar $found_version->numify, $module;
186 4         7 $$varref = $dualvar;
187              
188 4         11 $return .= "; use $module";
189 4 100       8 $return .= $version if $version;
190              
191 4         25 return $return;
192             }
193              
194             1;
195              
196             __END__