File Coverage

blib/lib/Devel/Hide.pm
Criterion Covered Total %
statement 99 103 96.1
branch 37 42 88.1
condition 10 12 83.3
subroutine 23 24 95.8
pod n/a
total 169 181 93.3


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