1
1
package namespace::clean;
3
$namespace::clean::AUTHORITY = 'cpan:PHAYLON';
6
$namespace::clean::VERSION = '0.20';
8
2
# ABSTRACT: Keep imports and functions out of your namespace
13
7
use vars qw( $STORAGE_VAR );
14
use Sub::Name 0.04 qw(subname);
15
use Sub::Identify 0.04 qw(sub_fullname);
16
use Package::Stash 0.22;
17
use B::Hooks::EndOfScope 0.07;
10
our $VERSION = '0.21';
19
12
$STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
22
my $RemoveSubs = sub {
26
my $cleanee_stash = Package::Stash->new($cleanee);
27
my $deleted_stash = Package::Stash->new("namespace::clean::deleted::$cleanee");
31
# ignore already removed symbols
32
next SYMBOL if $store->{exclude}{ $f };
34
next SYMBOL unless $cleanee_stash->has_symbol($variable);
36
if (ref(\$cleanee_stash->namespace->{$f}) eq 'GLOB') {
37
# convince the Perl debugger to work
38
# it assumes that sub_fullname($sub) can always be used to find the CV again
39
# since we are deleting the glob where the subroutine was originally
40
# defined, that assumption no longer holds, so we need to move it
41
# elsewhere and point the CV's name to the new glob.
42
my $sub = $cleanee_stash->get_symbol($variable);
43
if ( sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
44
my $new_fq = $deleted_stash->name . "::$f";
45
subname($new_fq, $sub);
46
$deleted_stash->add_symbol($variable, $sub);
50
my ($scalar, $array, $hash, $io) = map {
51
$cleanee_stash->get_symbol($_ . $f)
53
$cleanee_stash->remove_glob($f);
54
for my $var (['$', $scalar], ['@', $array], ['%', $hash], ['', $io]) {
55
next unless defined $var->[1];
56
$cleanee_stash->add_symbol($var->[0] . $f, $var->[1]);
61
sub clean_subroutines {
62
my ($nc, $cleanee, @subs) = @_;
63
$RemoveSubs->($cleanee, {}, @subs);
68
my ($pragma, @args) = @_;
70
my (%args, $is_explicit);
75
if ($args[0] =~ /^\-/) {
76
my $key = shift @args;
77
my $value = shift @args;
78
$args{ $key } = $value;
86
my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
89
$RemoveSubs->($cleanee, {}, @args);
19
# when changing also change in Makefile.PL
20
my $b_h_eos_req = '0.07';
23
require B::Hooks::EndOfScope;
24
B::Hooks::EndOfScope->VERSION($b_h_eos_req);
27
B::Hooks::EndOfScope->import('on_scope_end');
30
eval <<'PP' or die $@;
35
package namespace::clean::_TieHintHash;
40
use base 'Tie::ExtraHash';
44
package namespace::clean::_ScopeGuard;
49
sub arm { bless [ $_[1] ] }
51
sub DESTROY { $_[0]->[0]->() }
55
sub on_scope_end (&) {
58
if( my $stack = tied( %^H ) ) {
59
if ( (my $c = ref $stack) ne 'namespace::clean::_TieHintHash') {
61
========================================================================
62
!!! F A T A L E R R O R !!!
64
foreign tie() of %^H detected
65
========================================================================
67
namespace::clean is currently operating in pure-perl fallback mode, because
68
your system is lacking the necessary dependency B::Hooks::EndOfScope $b_h_eos_req.
69
In this mode namespace::clean expects to be able to tie() the hinthash %^H,
70
however it is apparently already tied by means unknown to the tie-class
73
Since this is a no-win situation execution will abort here and now. Please
74
try to find out which other module is relying on hinthash tie() ability,
75
and file a bug for both the perpetrator and namespace::clean, so that the
76
authors can figure out an acceptable way of moving forward.
80
push @$stack, namespace::clean::_ScopeGuard->arm(shift);
94
# calling class, all current functions and our storage
95
my $functions = $pragma->get_functions($cleanee);
96
my $store = $pragma->get_class_store($cleanee);
97
my $stash = Package::Stash->new($cleanee);
99
# except parameter can be array ref or single value
100
my %except = map {( $_ => 1 )} (
102
? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
106
# register symbols for removal, if they have a CODE entry
107
for my $f (keys %$functions) {
108
next if $except{ $f };
109
next unless $stash->has_symbol("&$f");
110
$store->{remove}{ $f } = 1;
113
# register EOF handler on first call to import
114
unless ($store->{handler_is_installed}) {
116
$RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
118
$store->{handler_is_installed} = 1;
127
my ($pragma, %args) = @_;
129
# the calling class, the current functions and our storage
130
my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
131
my $functions = $pragma->get_functions($cleanee);
132
my $store = $pragma->get_class_store($cleanee);
134
# register all unknown previous functions as excluded
135
for my $f (keys %$functions) {
136
next if $store->{remove}{ $f }
137
or $store->{exclude}{ $f };
138
$store->{exclude}{ $f } = 1;
145
sub get_class_store {
146
my ($pragma, $class) = @_;
147
my $stash = Package::Stash->new($class);
148
my $var = "%$STORAGE_VAR";
149
$stash->add_symbol($var, {})
150
unless $stash->has_symbol($var);
151
return $stash->get_symbol($var);
156
my ($pragma, $class) = @_;
158
my $stash = Package::Stash->new($class);
160
map { $_ => $stash->get_symbol("&$_") }
161
$stash->list_all_symbols('CODE')
167
'Danger! Laws of Thermodynamics may not apply.'
83
tie( %^H, 'namespace::clean::_TieHintHash', namespace::clean::_ScopeGuard->arm(shift) );
176
namespace::clean - Keep imports and functions out of your namespace
96
namespace::clean - keep imports and functions out of your namespace
301
221
effect at a specific time (e.g. C<namespace::clean> acts on scope compile end)
302
222
it is your responsibility to make sure it runs at that time.
226
my $sub_utils_loaded;
227
my $DebuggerRename = sub {
228
my ($f, $sub, $cleanee_stash, $deleted_stash) = @_;
230
if (! defined $sub_utils_loaded ) {
231
$sub_utils_loaded = do {
233
eval { require Sub::Name; Sub::Name->VERSION($sn_ver) }
234
or die "Sub::Name $sn_ver required when running under -d or equivalent: $@";
237
eval { require Sub::Identify; Sub::Identify->VERSION($si_ver) }
238
or die "Sub::Identify $si_ver required when running under -d or equivalent: $@";
244
if ( Sub::Identify::sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
245
my $new_fq = $deleted_stash->name . "::$f";
246
Sub::Name::subname($new_fq, $sub);
247
$deleted_stash->add_symbol("&$f", $sub);
251
my $RemoveSubs = sub {
254
my $cleanee_stash = Package::Stash->new($cleanee);
260
# ignore already removed symbols
261
next SYMBOL if $store->{exclude}{ $f };
263
my $sub = $cleanee_stash->get_symbol("&$f")
266
if ($^P and ref(\$cleanee_stash->namespace->{$f}) eq 'GLOB') {
267
# convince the Perl debugger to work
268
# it assumes that sub_fullname($sub) can always be used to find the CV again
269
# since we are deleting the glob where the subroutine was originally
270
# defined, that assumption no longer holds, so we need to move it
271
# elsewhere and point the CV's name to the new glob.
276
$deleted_stash ||= Package::Stash->new("namespace::clean::deleted::$cleanee"),
282
my $def = $cleanee_stash->get_symbol($name);
283
defined($def) ? [$name, $def] : ()
286
$cleanee_stash->remove_glob($f);
288
$cleanee_stash->add_symbol(@$_) for @symbols;
292
sub clean_subroutines {
293
my ($nc, $cleanee, @subs) = @_;
294
$RemoveSubs->($cleanee, {}, @subs);
306
299
Makes a snapshot of the current defined functions and installs a
307
300
L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
305
my ($pragma, @args) = @_;
307
my (%args, $is_explicit);
312
if ($args[0] =~ /^\-/) {
313
my $key = shift @args;
314
my $value = shift @args;
315
$args{ $key } = $value;
323
my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
326
$RemoveSubs->($cleanee, {}, @args);
331
# calling class, all current functions and our storage
332
my $functions = $pragma->get_functions($cleanee);
333
my $store = $pragma->get_class_store($cleanee);
334
my $stash = Package::Stash->new($cleanee);
336
# except parameter can be array ref or single value
337
my %except = map {( $_ => 1 )} (
339
? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
343
# register symbols for removal, if they have a CODE entry
344
for my $f (keys %$functions) {
345
next if $except{ $f };
346
next unless $stash->has_symbol("&$f");
347
$store->{remove}{ $f } = 1;
350
# register EOF handler on first call to import
351
unless ($store->{handler_is_installed}) {
353
$RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
355
$store->{handler_is_installed} = 1;
311
364
This method will be called when you do a
315
368
It will start a new section of code that defines functions to clean up.
373
my ($pragma, %args) = @_;
375
# the calling class, the current functions and our storage
376
my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
377
my $functions = $pragma->get_functions($cleanee);
378
my $store = $pragma->get_class_store($cleanee);
380
# register all unknown previous functions as excluded
381
for my $f (keys %$functions) {
382
next if $store->{remove}{ $f }
383
or $store->{exclude}{ $f };
384
$store->{exclude}{ $f } = 1;
317
390
=head2 get_class_store
319
392
This returns a reference to a hash in a passed package containing
320
393
information about function names included and excluded from removal.
397
sub get_class_store {
398
my ($pragma, $class) = @_;
399
my $stash = Package::Stash->new($class);
400
my $var = "%$STORAGE_VAR";
401
$stash->add_symbol($var, {})
402
unless $stash->has_symbol($var);
403
return $stash->get_symbol($var);
322
406
=head2 get_functions
324
408
Takes a class as argument and returns all currently defined functions
325
409
in it as a hash reference with the function name as key and a typeglob
326
410
reference to the symbol as value.
415
my ($pragma, $class) = @_;
417
my $stash = Package::Stash->new($class);
419
map { $_ => $stash->get_symbol("&$_") }
420
$stash->list_all_symbols('CODE')
328
424
=head1 IMPLEMENTATION DETAILS
330
426
This module works through the effect that a