1
1
package namespace::clean;
3
$namespace::clean::AUTHORITY = 'cpan:PHAYLON';
6
$namespace::clean::VERSION = '0.17';
8
# ABSTRACT: Keep imports and functions out of your namespace
13
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.03;
17
use B::Hooks::EndOfScope 0.07;
19
$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_package_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_package_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_package_symbol($variable, $sub);
50
$cleanee_stash->remove_package_symbol($variable);
54
sub clean_subroutines {
55
my ($nc, $cleanee, @subs) = @_;
56
$RemoveSubs->($cleanee, {}, @subs);
61
my ($pragma, @args) = @_;
63
my (%args, $is_explicit);
68
if ($args[0] =~ /^\-/) {
69
my $key = shift @args;
70
my $value = shift @args;
71
$args{ $key } = $value;
79
my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
82
$RemoveSubs->($cleanee, {}, @args);
87
# calling class, all current functions and our storage
88
my $functions = $pragma->get_functions($cleanee);
89
my $store = $pragma->get_class_store($cleanee);
90
my $stash = Package::Stash->new($cleanee);
92
# except parameter can be array ref or single value
93
my %except = map {( $_ => 1 )} (
95
? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
99
# register symbols for removal, if they have a CODE entry
100
for my $f (keys %$functions) {
101
next if $except{ $f };
102
next unless $stash->has_package_symbol("&$f");
103
$store->{remove}{ $f } = 1;
106
# register EOF handler on first call to import
107
unless ($store->{handler_is_installed}) {
109
$RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
111
$store->{handler_is_installed} = 1;
120
my ($pragma, %args) = @_;
122
# the calling class, the current functions and our storage
123
my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
124
my $functions = $pragma->get_functions($cleanee);
125
my $store = $pragma->get_class_store($cleanee);
127
# register all unknown previous functions as excluded
128
for my $f (keys %$functions) {
129
next if $store->{remove}{ $f }
130
or $store->{exclude}{ $f };
131
$store->{exclude}{ $f } = 1;
138
sub get_class_store {
139
my ($pragma, $class) = @_;
140
my $stash = Package::Stash->new($class);
141
return $stash->get_package_symbol("%$STORAGE_VAR");
146
my ($pragma, $class) = @_;
148
my $stash = Package::Stash->new($class);
150
map { $_ => $stash->get_package_symbol("&$_") }
151
$stash->list_all_package_symbols('CODE')
157
'Danger! Laws of Thermodynamics may not apply.'
5
166
namespace::clean - Keep imports and functions out of your namespace
12
use vars qw( $VERSION $STORAGE_VAR $SCOPE_HOOK_KEY $SCOPE_EXPLICIT );
13
use Symbol qw( qualify_to_ref gensym );
14
use B::Hooks::EndOfScope;
15
use Sub::Identify qw(sub_fullname);
16
use Sub::Name qw(subname);
25
$STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
155
291
effect at a specific time (e.g. C<namespace::clean> acts on scope compile end)
156
292
it is your responsibility to make sure it runs at that time.
160
my $RemoveSubs = sub {
166
my $fq = "${cleanee}::$f";
168
# ignore already removed symbols
169
next SYMBOL if $store->{exclude}{ $f };
172
next SYMBOL unless exists ${ "${cleanee}::" }{ $f };
174
if (ref(\${ "${cleanee}::" }{ $f }) eq 'GLOB') {
175
# convince the Perl debugger to work
176
# it assumes that sub_fullname($sub) can always be used to find the CV again
177
# since we are deleting the glob where the subroutine was originally
178
# defined, that assumption no longer holds, so we need to move it
179
# elsewhere and point the CV's name to the new glob.
181
if ( sub_fullname($sub) eq $fq ) {
182
my $new_fq = "namespace::clean::deleted::$fq";
183
subname($new_fq, $sub);
189
# keep original value to restore non-code slots
190
{ no warnings 'uninitialized'; # fix possible unimports
191
*__tmp = *{ ${ "${cleanee}::" }{ $f } };
192
delete ${ "${cleanee}::" }{ $f };
196
# restore non-code slots to symbol.
197
# omit the FORMAT slot, since perl erroneously puts it into the
198
# SCALAR slot of the new glob.
199
for my $t (qw( SCALAR ARRAY HASH IO )) {
200
next SLOT unless defined *__tmp{ $t };
201
*{ "${cleanee}::$f" } = *__tmp{ $t };
205
# A non-glob in the stash is assumed to stand for some kind
206
# of function. So far they all do, but the core might change
207
# this some day. Watch perl5-porters.
208
delete ${ "${cleanee}::" }{ $f };
213
sub clean_subroutines {
214
my ($nc, $cleanee, @subs) = @_;
215
$RemoveSubs->($cleanee, {}, @subs);
220
296
Makes a snapshot of the current defined functions and installs a
221
297
L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
226
my ($pragma, @args) = @_;
228
my (%args, $is_explicit);
233
if ($args[0] =~ /^\-/) {
234
my $key = shift @args;
235
my $value = shift @args;
236
$args{ $key } = $value;
244
my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
247
$RemoveSubs->($cleanee, {}, @args);
252
# calling class, all current functions and our storage
253
my $functions = $pragma->get_functions($cleanee);
254
my $store = $pragma->get_class_store($cleanee);
256
# except parameter can be array ref or single value
257
my %except = map {( $_ => 1 )} (
259
? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
263
# register symbols for removal, if they have a CODE entry
264
for my $f (keys %$functions) {
265
next if $except{ $f };
266
next unless $functions->{ $f }
267
and *{ $functions->{ $f } }{CODE};
268
$store->{remove}{ $f } = 1;
271
# register EOF handler on first call to import
272
unless ($store->{handler_is_installed}) {
274
$RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
276
$store->{handler_is_installed} = 1;
285
301
This method will be called when you do a
289
305
It will start a new section of code that defines functions to clean up.
294
my ($pragma, %args) = @_;
296
# the calling class, the current functions and our storage
297
my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
298
my $functions = $pragma->get_functions($cleanee);
299
my $store = $pragma->get_class_store($cleanee);
301
# register all unknown previous functions as excluded
302
for my $f (keys %$functions) {
303
next if $store->{remove}{ $f }
304
or $store->{exclude}{ $f };
305
$store->{exclude}{ $f } = 1;
311
307
=head2 get_class_store
313
This returns a reference to a hash in a passed package containing
309
This returns a reference to a hash in a passed package containing
314
310
information about function names included and excluded from removal.
318
sub get_class_store {
319
my ($pragma, $class) = @_;
321
return \%{ "${class}::${STORAGE_VAR}" };
324
312
=head2 get_functions
326
314
Takes a class as argument and returns all currently defined functions
327
315
in it as a hash reference with the function name as key and a typeglob
328
316
reference to the symbol as value.
333
my ($pragma, $class) = @_;
336
map { @$_ } # key => value
337
grep { *{ $_->[1] }{CODE} } # only functions
338
map { [$_, qualify_to_ref( $_, $class )] } # get globref
339
grep { $_ !~ /::$/ } # no packages
340
do { no strict 'refs'; keys %{ "${class}::" } } # symbol entries
346
C<namespace::clean> will clobber any formats that have the same name as
347
a deleted sub. This is due to a bug in perl that makes it impossible to
348
re-assign the FORMAT ref into a new glob.
350
318
=head1 IMPLEMENTATION DETAILS
352
This module works through the effect that a
320
This module works through the effect that a
354
322
delete $SomePackage::{foo};
369
337
L<B::Hooks::EndOfScope>
371
=head1 AUTHOR AND COPYRIGHT
373
Robert 'phaylon' Sedlacek C<E<lt>rs@474.atE<gt>>, with many thanks to
374
Matt S Trout for the inspiration on the whole idea.
378
This program is free software; you can redistribute it and/or modify
379
it under the same terms as perl itself.
341
Many thanks to Matt S Trout for the inspiration on the whole idea.
349
Robert 'phaylon' Sedlacek <rs@474.at>
353
Florian Ragwitz <rafl@debian.org>
357
Jesse Luehrs <doy@tozt.net>
361
=head1 COPYRIGHT AND LICENSE
363
This software is copyright (c) 2010 by Robert 'phaylon' Sedlacek.
365
This is free software; you can redistribute it and/or modify it under
366
the same terms as the Perl 5 programming language system itself.
384
'Danger! Laws of Thermodynamics may not apply.'