File Coverage

blib/lib/Env/Export.pm
Criterion Covered Total %
statement 91 91 100.0
branch 38 38 100.0
condition 6 6 100.0
subroutine 13 13 100.0
pod n/a
total 148 148 100.0


line stmt bran cond sub pod time code
1             ###############################################################################
2             #
3             # This file copyright (c) 2009-2011 by Randy J. Ray, all rights reserved
4             #
5             # Copying and distribution are permitted under the terms of the Artistic
6             # License 2.0 (http://www.opensource.org/licenses/artistic-license-2.0.php) or
7             # the GNU LGPL (http://www.opensource.org/licenses/lgpl-2.1.php).
8             #
9             ###############################################################################
10             #
11             # Description: Export environment variables as constant subs
12             #
13             # Functions: import
14             #
15             # Environment: Umm, yeah... that's kind of the point of it all...
16             #
17             ###############################################################################
18              
19             package Env::Export;
20              
21 9     9   17956 use 5.006001;
  9         29  
  9         359  
22 9     9   47 use strict;
  9         14  
  9         314  
23 9     9   51 use warnings;
  9         24  
  9         333  
24 9     9   43 use vars qw($VERSION);
  9         28  
  9         504  
25 9     9   6365 use subs qw(import);
  9         103  
  9         59  
26              
27 9     9   354 use Carp qw(croak carp);
  9         19  
  9         1113  
28              
29             $VERSION = '0.22';
30             $VERSION = eval $VERSION; ## no critic(ProhibitStringyEval)
31              
32             ###############################################################################
33             #
34             # Sub Name: import
35             #
36             # Description: Do the actual import work, namespace wrangling, etc.
37             #
38             # Arguments: NAME IN/OUT TYPE DESCRIPTION
39             # $class in scalar Class we're called in
40             # @patterns in list One or more patterns or
41             # keywords used to select %ENV
42             # keys to export
43             #
44             # Environment: Yeah
45             #
46             # Returns: void
47             #
48             ###############################################################################
49             sub import ## no critic(ProhibitExcessComplexity)
50             {
51 46     46   24275 my ($class, @patterns) = @_;
52 46         93 my $me = "${class}::import";
53              
54             ## no critic(ProhibitNoStrict)
55             ## no critic(ProhibitProlongedStrictureOverride)
56             ## no critic(ProhibitNoWarnings)
57 9     9   43 no strict 'refs';
  9         15  
  9         252  
58 9     9   41 no warnings qw(redefine prototype);
  9         21  
  9         8870  
59              
60 46 100       141 if (! @patterns)
61             {
62 1         17 return; # Nothing to do if they didn't request anything
63             }
64              
65 45         154 my ($calling_pkg) = caller;
66 45         65 my $callersym = \%{"${calling_pkg}::"};
  45         116  
67              
68             # Values that are tweaked by keywords that may appear in the @patterns
69             # stream:
70 45         64 my $warn = 1;
71 45         50 my $link = 0;
72 45         53 my $prefix = q{};
73 45         51 my $override = 0;
74 45         56 my $split = q{};
75              
76             # Establish the set of allowable %ENV keys that are eligible for export.
77             # This will avoid repeated iterations over %ENV later, and will remove
78             # any keys that could not be used to create valid sub names
79 45         346 my @choices = grep { /^[A-Za-z_]\w*$/ } keys %ENV;
  1815         3904  
80             # This list will accumulate the set of subs to be created, in the form of
81             # metadata:
82 45         141 my @subs = ();
83              
84 45         127 while (my $pat = shift @patterns)
85             {
86             # This would be a lot cleaner if I could assume the presence of the
87             # "switch" statement. But I'm not ready to limit this code to 5.10+
88              
89             # Because ":split" only applies to the very next argument after it,
90             # we have to handle it specially. It gets cleared at the end of every
91             # iteration of this loop, so if it is here, peel off the next argument
92             # then re-assign $pat to the one after that.
93 86 100       199 if ($pat eq ':split')
94             {
95 5         6 $split = shift @patterns;
96 5         8 $pat = shift @patterns;
97             }
98              
99             # Do the keywords first, in most cases they just flip flags back and
100             # forth
101 86 100       622 if ($pat =~ /^:(no)?warn$/) ## no critic(ProhibitCascadingIfElse)
    100          
    100          
    100          
    100          
    100          
    100          
    100          
102             {
103 7 100       22 $warn = $1 ? 0 : 1;
104             }
105             elsif ($pat =~ /^:(no)?prefix$/)
106             {
107 6 100       17 $prefix = $1 ? q{} : shift @patterns;
108             }
109             elsif ($pat =~ /^:(no)?override$/)
110             {
111 6 100       15 $override = $1 ? 0 : 1;
112             }
113             elsif ($pat =~ /^:(no)?link$/)
114             {
115 7 100       22 $link = $1 ? 0 : 1;
116             }
117             elsif ($pat eq ':all')
118             {
119 3         6 for (@choices)
120             {
121 48         146 push @subs, { key => $_,
122             warn => $warn,
123             prefix => $prefix,
124             override => $override,
125             link => $link,
126             split => $split, };
127             }
128             }
129             # Now handle explicit names, shell-style patterns and regexen:
130             # Pre-compiled Perl regexen:
131             elsif (ref($pat) eq 'Regexp')
132             {
133             # Add an entry to @subs for each matching key
134 3         6 for (grep { $_ =~ $pat } @choices)
  54         137  
135             {
136 7         31 push @subs, { key => $_,
137             warn => $warn,
138             prefix => $prefix,
139             override => $override,
140             link => $link,
141             split => $split, };
142             }
143             }
144             # Shell style (* => .*, ? => ., ?* => .+):
145             elsif ($pat =~ /[*?]/)
146             {
147             # Change the shell-style globbing patterns to regex equivalents
148 4         13 $pat =~ s/[?][*]/.+/g;
149 4         12 $pat =~ s/[*]/.*/g;
150 4         12 $pat =~ s/[?]/./g;
151 4         56 $pat = qr/^$pat$/;
152              
153             # Add an entry to @subs for each matching key
154 4         13 for (grep { $_ =~ $pat } @choices)
  1088         2746  
155             {
156 288         946 push @subs, { key => $_,
157             warn => $warn,
158             prefix => $prefix,
159             override => $override,
160             link => $link,
161             split => $split, };
162             }
163             }
164             # Lastly, acceptable strings:
165             elsif ($pat =~ /^[A-Za-z_]\w*$/)
166             {
167             # Just add a single entry to @subs for the string
168 49         219 push @subs, { key => $pat,
169             warn => $warn,
170             prefix => $prefix,
171             override => $override,
172             link => $link,
173             split => $split, };
174             }
175             # And if we got here it was almost certainly a pattern that would not
176             # be a valid Perl subname. Note that this is not suppressed by :nowarn.
177             else
178             {
179 1         161 carp "$me: Unrecognized pattern or keyword '$pat', skipped";
180             }
181              
182             # Since :split is defined to apply to only the next name or pattern,
183             # we have to clear it every iteration just to be safe...
184 86         275 $split = q{};
185             }
186              
187 45         83 foreach (@subs)
188             {
189 392         717 my $subname = "$_->{prefix}$_->{key}";
190 392         511 my $envkey = $_->{key};
191              
192 392 100 100     866 if (exists($callersym->{$subname}) &&
  19   100     131  
193             defined(&{$callersym->{$subname}}) &&
194             ! $_->{override})
195             {
196             # We don't overwrite existing subroutines unless they OK'd it
197             # with :override
198 7 100       18 if ($_->{warn})
199             {
200 2         329 carp "$me: Will not redefine ${calling_pkg}::$subname, " .
201             'skipping';
202             }
203 7         33 next;
204             }
205              
206 385         468 $subname = "${calling_pkg}::$subname";
207              
208             # This may look like a great candidate for a lookup-table of code
209             # blocks to eval, but I'd actually prefer to avoid that as it would
210             # also require multiple substitutions each iteration as well...
211 385 100       656 if ($_->{link})
212             {
213 8 100       17 if ($_->{split})
214             {
215 1         3 my $localsplit = $_->{split};
216 1         5 *{$subname} = sub () {
217 2     2   622 return split $localsplit, $ENV{$envkey};
218 1         5 };
219             }
220             else
221             {
222 7         33 *{$subname} = sub () {
223 7     7   55 return $ENV{$envkey};
224 7         21 };
225             }
226             }
227             else
228             {
229 377 100       560 if ($_->{split})
230             {
231 5         53 my @value = split $_->{split}, $ENV{$envkey};
232 5         24 *{$subname} = sub () {
233 10     10   90 return @value;
234 5         48 };
235             }
236             else
237             {
238 372         739 my $value = $ENV{$envkey};
239 372         2348 *{$subname} = sub () {
240 356     356   2150 return $value;
241 372         1099 };
242             }
243             }
244             }
245              
246 45         6493 return;
247             }
248              
249             1;
250              
251             __END__