1 package namespace::clean;
2 # ABSTRACT: Keep imports and functions out of your namespace
7 use vars qw( $STORAGE_VAR );
10 $STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
14 require B::Hooks::EndOfScope;
15 B::Hooks::EndOfScope->VERSION('0.07');
18 B::Hooks::EndOfScope->import('on_scope_end');
21 eval <<'PP' or die $@;
24 package namespace::clean::_ScopeGuard;
26 sub arm { bless [ $_[1] ] }
28 sub DESTROY { $_[0]->[0]->() }
33 sub on_scope_end (&) {
36 if( my $stack = tied( %^H ) ) {
37 push @$stack, namespace::clean::_ScopeGuard->arm(shift);
40 tie( %^H, 'Tie::ExtraHash', namespace::clean::_ScopeGuard->arm(shift) );
57 use Carp qw(croak); # 'croak' will be removed
59 sub bar { 23 } # 'bar' will be removed
61 # remove all previously defined functions
64 sub baz { bar() } # 'baz' still defined, 'bar' still bound
66 # begin to collection function names from here again
69 sub quux { baz() } # 'quux' will be removed
71 # remove all functions defined after the 'no' unimport
74 # Will print: 'No', 'No', 'Yes' and 'No'
75 print +(__PACKAGE__->can('croak') ? 'Yes' : 'No'), "\n";
76 print +(__PACKAGE__->can('bar') ? 'Yes' : 'No'), "\n";
77 print +(__PACKAGE__->can('baz') ? 'Yes' : 'No'), "\n";
78 print +(__PACKAGE__->can('quux') ? 'Yes' : 'No'), "\n";
84 =head2 Keeping packages clean
86 When you define a function, or import one, into a Perl package, it will
87 naturally also be available as a method. This does not per se cause
88 problems, but it can complicate subclassing and, for example, plugin
89 classes that are included via multiple inheritance by loading them as
92 The C<namespace::clean> pragma will remove all previously declared or
93 imported symbols at the end of the current package's compile cycle.
94 Functions called in the package itself will still be bound by their
95 name, but they won't show up as methods on your class or instances.
97 By unimporting via C<no> you can tell C<namespace::clean> to start
98 collecting functions for the next C<use namespace::clean;> specification.
100 You can use the C<-except> flag to tell C<namespace::clean> that you
101 don't want it to remove a certain function or method. A common use would
102 be a module exporting an C<import> method along with some functions:
104 use ModuleExportingImport;
105 use namespace::clean -except => [qw( import )];
107 If you just want to C<-except> a single sub, you can pass it directly.
108 For more than one value you have to use an array reference.
110 =head2 Explicitly removing functions when your scope is compiled
112 It is also possible to explicitly tell C<namespace::clean> what packages
113 to remove when the surrounding scope has finished compiling. Here is an
119 # blessed NOT available
122 use Scalar::Util qw( blessed );
123 use namespace::clean qw( blessed );
126 return blessed shift;
129 # blessed NOT available
133 When using C<namespace::clean> together with L<Moose> you want to keep
134 the installed C<meta> method. So your classes should look like:
138 use namespace::clean -except => 'meta';
141 Same goes for L<Moose::Role>.
143 =head2 Cleaning other packages
145 You can tell C<namespace::clean> that you want to clean up another package
146 instead of the one importing. To do this you have to pass in the C<-cleanee>
149 package My::MooseX::namespace::clean;
152 use namespace::clean (); # no cleanup, just load
155 namespace::clean->import(
156 -cleanee => scalar(caller),
161 If you don't care about C<namespace::clean>s discover-and-C<-except> logic, and
162 just want to remove subroutines, try L</clean_subroutines>.
164 =method clean_subroutines
166 This exposes the actual subroutine-removal logic.
168 namespace::clean->clean_subroutines($cleanee, qw( subA subB ));
170 will remove C<subA> and C<subB> from C<$cleanee>. Note that this will remove the
171 subroutines B<immediately> and not wait for scope end. If you want to have this
172 effect at a specific time (e.g. C<namespace::clean> acts on scope compile end)
173 it is your responsibility to make sure it runs at that time.
177 my $sub_utils_loaded;
178 my $DebuggerRename = sub {
179 my ($f, $sub, $cleanee_stash, $deleted_stash) = @_;
181 if (! defined $sub_utils_loaded ) {
182 $sub_utils_loaded = do {
184 eval { require Sub::Name; Sub::Name->VERSION($sn_ver) }
185 or die "Sub::Name $sn_ver required when running under -d or equivalent: $@";
188 eval { require Sub::Identify; Sub::Identify->VERSION($si_ver) }
189 or die "Sub::Identify $si_ver required when running under -d or equivalent: $@";
195 if ( Sub::Identify::sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
196 my $new_fq = $deleted_stash->name . "::$f";
197 Sub::Name::subname($new_fq, $sub);
198 $deleted_stash->add_symbol("&$f", $sub);
202 my $RemoveSubs = sub {
205 my $cleanee_stash = Package::Stash->new($cleanee);
211 # ignore already removed symbols
212 next SYMBOL if $store->{exclude}{ $f };
214 my $sub = $cleanee_stash->get_symbol("&$f")
217 if ($^P and ref(\$cleanee_stash->namespace->{$f}) eq 'GLOB') {
218 # convince the Perl debugger to work
219 # it assumes that sub_fullname($sub) can always be used to find the CV again
220 # since we are deleting the glob where the subroutine was originally
221 # defined, that assumption no longer holds, so we need to move it
222 # elsewhere and point the CV's name to the new glob.
227 $deleted_stash ||= Package::Stash->new("namespace::clean::deleted::$cleanee"),
233 my $def = $cleanee_stash->get_symbol($name);
234 defined($def) ? [$name, $def] : ()
237 $cleanee_stash->remove_glob($f);
239 $cleanee_stash->add_symbol(@$_) for @symbols;
243 sub clean_subroutines {
244 my ($nc, $cleanee, @subs) = @_;
245 $RemoveSubs->($cleanee, {}, @subs);
250 Makes a snapshot of the current defined functions and installs a
251 L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
256 my ($pragma, @args) = @_;
258 my (%args, $is_explicit);
263 if ($args[0] =~ /^\-/) {
264 my $key = shift @args;
265 my $value = shift @args;
266 $args{ $key } = $value;
274 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
277 $RemoveSubs->($cleanee, {}, @args);
282 # calling class, all current functions and our storage
283 my $functions = $pragma->get_functions($cleanee);
284 my $store = $pragma->get_class_store($cleanee);
285 my $stash = Package::Stash->new($cleanee);
287 # except parameter can be array ref or single value
288 my %except = map {( $_ => 1 )} (
290 ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
294 # register symbols for removal, if they have a CODE entry
295 for my $f (keys %$functions) {
296 next if $except{ $f };
297 next unless $stash->has_symbol("&$f");
298 $store->{remove}{ $f } = 1;
301 # register EOF handler on first call to import
302 unless ($store->{handler_is_installed}) {
304 $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
306 $store->{handler_is_installed} = 1;
315 This method will be called when you do a
319 It will start a new section of code that defines functions to clean up.
324 my ($pragma, %args) = @_;
326 # the calling class, the current functions and our storage
327 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
328 my $functions = $pragma->get_functions($cleanee);
329 my $store = $pragma->get_class_store($cleanee);
331 # register all unknown previous functions as excluded
332 for my $f (keys %$functions) {
333 next if $store->{remove}{ $f }
334 or $store->{exclude}{ $f };
335 $store->{exclude}{ $f } = 1;
341 =method get_class_store
343 This returns a reference to a hash in a passed package containing
344 information about function names included and excluded from removal.
348 sub get_class_store {
349 my ($pragma, $class) = @_;
350 my $stash = Package::Stash->new($class);
351 my $var = "%$STORAGE_VAR";
352 $stash->add_symbol($var, {})
353 unless $stash->has_symbol($var);
354 return $stash->get_symbol($var);
357 =method get_functions
359 Takes a class as argument and returns all currently defined functions
360 in it as a hash reference with the function name as key and a typeglob
361 reference to the symbol as value.
366 my ($pragma, $class) = @_;
368 my $stash = Package::Stash->new($class);
370 map { $_ => $stash->get_symbol("&$_") }
371 $stash->list_all_symbols('CODE')
375 =head1 IMPLEMENTATION DETAILS
377 This module works through the effect that a
379 delete $SomePackage::{foo};
381 will remove the C<foo> symbol from C<$SomePackage> for run time lookups
382 (e.g., method calls) but will leave the entry alive to be called by
383 already resolved names in the package itself. C<namespace::clean> will
384 restore and therefor in effect keep all glob slots that aren't C<CODE>.
386 A test file has been added to the perl core to ensure that this behaviour
387 will be stable in future releases.
389 Just for completeness sake, if you want to remove the symbol completely,
390 use C<undef> instead.
394 This module is fully functional in a pure-perl environment, where
395 L<Variable::Magic>, a L<B::Hooks::EndOfScope> dependency, may not be
396 available. However in this case this module falls back to a
397 L<tie()|perlfunc/tie> of L<%^H|perlvar/%^H> which may or may not interfere
398 with some crack you may be doing independently of namespace::clean.
402 L<B::Hooks::EndOfScope>
406 Many thanks to Matt S Trout for the inspiration on the whole idea.
411 'Danger! Laws of Thermodynamics may not apply.'