File Coverage

blib/lib/Devel/Hide.pm
Criterion Covered Total %
statement 96 100 96.0
branch 37 42 88.1
condition 10 12 83.3
subroutine 22 23 95.6
pod n/a
total 165 177 93.2


line stmt bran cond sub pod time code
1             package Devel::Hide;
2              
3 12     12   2535272 use 5.008;
  12         47  
4 12     12   73 use strict;
  12         27  
  12         384  
5 12     12   72 use warnings;
  12         25  
  12         1293  
6              
7             our $VERSION = '0.0016';
8              
9             # blech! package variables
10             #
11             # @HIDDEN is one of the ways to populate the global hidden list
12             # $phase is used to identify which version of the hints hash to
13             # use - either %^H when we're updating it, or pulling it out
14             # of caller() when we want to read it
15             our @HIDDEN;
16             our $phase;
17              
18 12     12   5659 BEGIN { $phase = 'runtime'; }
19              
20             # settings are a comma- (and only comma, no quotes or spaces)
21             # -separated list of key,value,key,value,... There is no
22             # attempt to support data containing commas.
23             #
24             # The list of hidden modules is a comma (and *only* comma,
25             # no white space, no quotes) separated list of module
26             # names.
27             #
28             # yes, this is a ridiculous way of storing data. It is,
29             # however, compatible with what we're going to have to
30             # store in the hints hash for lexical hiding, as that
31             # only supports string data.
32             my %GLOBAL_SETTINGS;
33             _set_setting('global', children => 0);
34             _set_setting('global', verbose =>
35             defined $ENV{DEVEL_HIDE_VERBOSE}
36             ? $ENV{DEVEL_HIDE_VERBOSE}
37             : 1
38             );
39              
40             # convert a mixed list of modules and filenames to a list of
41             # filenames
42             sub _as_filenames {
43 12     12   25 return map { /^(\w+::)*\w+$/
44 19 100       123 ? do { my $f = "$_.pm"; $f =~ s|::|/|g; $f }
  13         27  
  13         27  
  13         47  
45             : $_
46             } @_;
47             }
48              
49             # Pushes a list to the set of hidden modules/filenames
50             # warns about the modules which could not be hidden (always)
51             # and about the ones that were successfully hidden (if verbose)
52             #
53             # It works as a batch producing warning messages
54             # at each invocation (when appropriate).
55             #
56             # the first arg is a reference to the config hash to use,
57             # either global or lexical
58             sub _push_hidden {
59 22     22   43 my $config = shift;
60              
61 22 100       1155 return unless @_;
62              
63 12         20 my @too_late;
64 12         35 for ( _as_filenames(@_) ) {
65 19 100       62 if ( $INC{$_} ) {
66 2         4 push @too_late, $_;
67             }
68             else {
69             $config->{'Devel::Hide/hidden'} =
70             $config->{'Devel::Hide/hidden'}
71 17 100       72 ? join(',', $config->{'Devel::Hide/hidden'}, $_)
72             : $_;
73             }
74             }
75 12 100       42 if ( @too_late ) {
76 2         29 warn __PACKAGE__, ': Too late to hide ', join( ', ', @too_late ), "\n";
77             }
78 12 50 66     1284 if ( _get_setting('verbose') && $config->{'Devel::Hide/hidden'}) {
79 12     12   99 no warnings 'uninitialized';
  12         21  
  12         4167  
80             warn __PACKAGE__ . ' hides ' .
81             join(
82             ', ',
83             sort split(
84 4         80 /,/, $config->{'Devel::Hide/hidden'}
85             )
86             ) . "\n";
87             }
88             }
89              
90             sub _dont_load {
91 18     18   48 my $filename = shift;
92 18 100       51 my $hidden_by = _get_setting('verbose')
93             ? 'hidden'
94             : 'hidden by ' . __PACKAGE__;
95 18         198 die "Can't locate $filename in \@INC ($hidden_by)\n";
96             }
97              
98             =begin private
99              
100             =item B<_core_modules>
101              
102             @core = _core_modules($perl_version);
103              
104             Returns the list of core modules according to
105             Module::CoreList.
106              
107             !!! UNUSED BY NOW
108              
109             It is aimed to expand the tag ':core' into all core
110             modules in the current version of Perl ($]).
111             Requires Module::CoreList.
112              
113             =end private
114              
115             =cut
116              
117             sub _core_modules {
118 0     0   0 require Module::CoreList; # XXX require 2.05 or newer
119 0         0 return Module::CoreList->find_modules( qr/.*/, shift );
120             }
121              
122             # _append_to_perl5opt(@to_be_hidden)
123             sub _append_to_perl5opt {
124              
125             $ENV{PERL5OPT} = join( ' ',
126 1 50   1   1838 defined($ENV{PERL5OPT}) ? $ENV{PERL5OPT} : (),
127             '-MDevel::Hide=' . join(',', @_)
128             );
129              
130             }
131              
132             sub _is_hidden {
133 12     12   86 no warnings 'uninitialized';
  12         22  
  12         4818  
134 190     190   361 my $module = shift;
135              
136             +{
137 400         1933 map { $_ => 1 }
138             map {
139 380         867 split(',', _get_config_ref($_)->{'Devel::Hide/hidden'})
140             } qw(global lexical)
141 190         457 }->{$module};
142             }
143              
144             sub _get_setting {
145 41     41   76 my $name = shift;
146 41 100       88 _exists_setting('lexical', $name)
147             ? _get_setting_from('lexical', $name)
148             : _get_setting_from('global', $name)
149             }
150              
151             sub _get_setting_from {
152 41     41   87 my($source, $name) = @_;
153              
154 41         114 my $config = _get_config_ref($source);
155 41         85 _setting_hashref($config)->{$name};
156             }
157              
158             sub _exists_setting {
159 41     41   108 my($source, $name) = @_;
160            
161 41         125 my $config = _get_config_ref($source);
162 41         110 exists(_setting_hashref($config)->{$name});
163             }
164              
165             sub _set_setting {
166 30     30   65 my($source, $name, $value) = @_;
167              
168 30         58 my $config = _get_config_ref($source);
169             my %hash = (
170 30         66 %{_setting_hashref($config)},
  30         61  
171             $name => $value
172             );
173             _get_config_ref($source)
174 30         141 ->{'Devel::Hide/settings'} = join(',', %hash);
175             }
176              
177             sub _setting_hashref {
178 112     112   208 my $settings = shift->{'Devel::Hide/settings'};
179 12     12   85 no warnings 'uninitialized';
  12         20  
  12         7090  
180 112         6966 +{ split(/,/, $settings) };
181             }
182              
183             sub _get_config_ref {
184 544     544   987 my $type = shift;
185 544 100       1203 if($type eq 'lexical') {
186 238 100       551 if($phase eq 'compile') {
187 26         98 return \%^H;
188             } else {
189 212         360 my $depth = 1;
190 212         1779 while(my @fields = caller($depth)) {
191 2982         4583 my $hints_hash = $fields[10];
192 2982 100 66     7590 if($hints_hash && grep { /^Devel::Hide\// } keys %{$hints_hash}) {
  14         56  
  8         24  
193             # return a copy
194 8         15 return { %{$hints_hash} };
  8         54  
195             }
196 2974         14269 $depth++;
197             }
198 204         1068 return {};
199             }
200             } else {
201 306         1427 return \%GLOBAL_SETTINGS;
202             }
203             }
204              
205             sub import {
206 14     14   507 shift;
207 14         26 my $which_config = 'global';
208 14         30 local $phase = 'compile';
209 14   100     94 while(@_ && $_[0] =~ /^-/) {
210 8 100       68 if( $_[0] eq '-lexically' ) {
    100          
    50          
211 2         4 $which_config = 'lexical';
212 2 50       7 if($] < 5.010) {
213 0         0 die("Can't 'use Devel::Hide qw(-lexically ...)' on perl 5.8 and below\n");
214             }
215             } elsif( $_[0] eq '-from:children' ) {
216 1         3 _set_setting($which_config, children => 1);
217             } elsif( $_[0] eq '-quiet' ) {
218 5         13 _set_setting($which_config, verbose => 0);
219             } else {
220 0         0 die("Devel::Hide: don't recognize $_[0]\n");
221             }
222 8         46 shift;
223             }
224 14 100       90 if (@_) {
225 10         29 _push_hidden(
226             _get_config_ref($which_config),
227             @_
228             );
229 10 100       4096 if (_get_setting('children')) {
230 1 50       2 _append_to_perl5opt(
231             (_get_setting('verbose') ? () : '-quiet'),
232             @_
233             );
234             }
235             }
236             }
237              
238             # $ENV{DEVEL_HIDE_PM} is split in ' '
239             # as well as @HIDDEN it accepts Module::Module as well as File/Names.pm
240             BEGIN {
241             # unless @HIDDEN was user-defined elsewhere, set default
242 12 100 100 12   145 if ( !@HIDDEN && $ENV{DEVEL_HIDE_PM} ) {
243             # NOTE. "split ' ', $s" is special. Read "perldoc -f split".
244             _push_hidden(
245             _get_config_ref('global'),
246             split q{ }, $ENV{DEVEL_HIDE_PM}
247 1         4 );
248             }
249             else {
250 11         38 _push_hidden(
251             _get_config_ref('global'),
252             @HIDDEN
253             );
254             }
255             }
256              
257             sub _inc_hook {
258 190     190   1042876 my ( $coderef, $filename ) = @_;
259 190 100       624 if ( _is_hidden($filename) ) { _dont_load($filename); }
  18         76  
260 172         93215 else { return undef; }
261             }
262              
263 12     12   1739 use lib ( \&_inc_hook );
  12         2423  
  12         289  
264              
265             # TO DO:
266             # * write unimport() sub
267             # * write decent docs
268             # * refactor private function names
269             # * RT #25528
270              
271             =begin private
272              
273             perl -MDevel::Hide=!:core -e script.pl # hide all non-core modules
274             perl -MDevel::Hide=M,!N -e script.pl # hide all modules but N plus M
275              
276             how to implement
277              
278             %GLOBAL_SETTINGS
279             %IS_EXCEPTION if there is an exception, all but the set of exceptions are to be hidden
280             plus the set of hidden modules
281              
282             :core(5.8)
283             :core synonym to :core($])
284              
285              
286             =end private
287              
288             =cut
289              
290             1;
291              
292             __END__