1 package namespace::clean;
2 # ABSTRACT: Keep imports and functions out of your namespace
7 use vars qw( $STORAGE_VAR );
10 our $VERSION = '0.20_01';
12 $STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
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);
83 tie( %^H, 'namespace::clean::_TieHintHash', namespace::clean::_ScopeGuard->arm(shift) );
100 use Carp qw(croak); # 'croak' will be removed
102 sub bar { 23 } # 'bar' will be removed
104 # remove all previously defined functions
105 use namespace::clean;
107 sub baz { bar() } # 'baz' still defined, 'bar' still bound
109 # begin to collection function names from here again
112 sub quux { baz() } # 'quux' will be removed
114 # remove all functions defined after the 'no' unimport
115 use namespace::clean;
117 # Will print: 'No', 'No', 'Yes' and 'No'
118 print +(__PACKAGE__->can('croak') ? 'Yes' : 'No'), "\n";
119 print +(__PACKAGE__->can('bar') ? 'Yes' : 'No'), "\n";
120 print +(__PACKAGE__->can('baz') ? 'Yes' : 'No'), "\n";
121 print +(__PACKAGE__->can('quux') ? 'Yes' : 'No'), "\n";
127 =head2 Keeping packages clean
129 When you define a function, or import one, into a Perl package, it will
130 naturally also be available as a method. This does not per se cause
131 problems, but it can complicate subclassing and, for example, plugin
132 classes that are included via multiple inheritance by loading them as
135 The C<namespace::clean> pragma will remove all previously declared or
136 imported symbols at the end of the current package's compile cycle.
137 Functions called in the package itself will still be bound by their
138 name, but they won't show up as methods on your class or instances.
140 By unimporting via C<no> you can tell C<namespace::clean> to start
141 collecting functions for the next C<use namespace::clean;> specification.
143 You can use the C<-except> flag to tell C<namespace::clean> that you
144 don't want it to remove a certain function or method. A common use would
145 be a module exporting an C<import> method along with some functions:
147 use ModuleExportingImport;
148 use namespace::clean -except => [qw( import )];
150 If you just want to C<-except> a single sub, you can pass it directly.
151 For more than one value you have to use an array reference.
153 =head2 Explicitly removing functions when your scope is compiled
155 It is also possible to explicitly tell C<namespace::clean> what packages
156 to remove when the surrounding scope has finished compiling. Here is an
162 # blessed NOT available
165 use Scalar::Util qw( blessed );
166 use namespace::clean qw( blessed );
169 return blessed shift;
172 # blessed NOT available
176 When using C<namespace::clean> together with L<Moose> you want to keep
177 the installed C<meta> method. So your classes should look like:
181 use namespace::clean -except => 'meta';
184 Same goes for L<Moose::Role>.
186 =head2 Cleaning other packages
188 You can tell C<namespace::clean> that you want to clean up another package
189 instead of the one importing. To do this you have to pass in the C<-cleanee>
192 package My::MooseX::namespace::clean;
195 use namespace::clean (); # no cleanup, just load
198 namespace::clean->import(
199 -cleanee => scalar(caller),
204 If you don't care about C<namespace::clean>s discover-and-C<-except> logic, and
205 just want to remove subroutines, try L</clean_subroutines>.
209 =head2 clean_subroutines
211 This exposes the actual subroutine-removal logic.
213 namespace::clean->clean_subroutines($cleanee, qw( subA subB ));
215 will remove C<subA> and C<subB> from C<$cleanee>. Note that this will remove the
216 subroutines B<immediately> and not wait for scope end. If you want to have this
217 effect at a specific time (e.g. C<namespace::clean> acts on scope compile end)
218 it is your responsibility to make sure it runs at that time.
222 my $sub_utils_loaded;
223 my $DebuggerRename = sub {
224 my ($f, $sub, $cleanee_stash, $deleted_stash) = @_;
226 if (! defined $sub_utils_loaded ) {
227 $sub_utils_loaded = do {
229 eval { require Sub::Name; Sub::Name->VERSION($sn_ver) }
230 or die "Sub::Name $sn_ver required when running under -d or equivalent: $@";
233 eval { require Sub::Identify; Sub::Identify->VERSION($si_ver) }
234 or die "Sub::Identify $si_ver required when running under -d or equivalent: $@";
240 if ( Sub::Identify::sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
241 my $new_fq = $deleted_stash->name . "::$f";
242 Sub::Name::subname($new_fq, $sub);
243 $deleted_stash->add_symbol("&$f", $sub);
247 my $RemoveSubs = sub {
250 my $cleanee_stash = Package::Stash->new($cleanee);
256 # ignore already removed symbols
257 next SYMBOL if $store->{exclude}{ $f };
259 my $sub = $cleanee_stash->get_symbol("&$f")
262 if ($^P and ref(\$cleanee_stash->namespace->{$f}) eq 'GLOB') {
263 # convince the Perl debugger to work
264 # it assumes that sub_fullname($sub) can always be used to find the CV again
265 # since we are deleting the glob where the subroutine was originally
266 # defined, that assumption no longer holds, so we need to move it
267 # elsewhere and point the CV's name to the new glob.
272 $deleted_stash ||= Package::Stash->new("namespace::clean::deleted::$cleanee"),
278 my $def = $cleanee_stash->get_symbol($name);
279 defined($def) ? [$name, $def] : ()
282 $cleanee_stash->remove_glob($f);
284 $cleanee_stash->add_symbol(@$_) for @symbols;
288 sub clean_subroutines {
289 my ($nc, $cleanee, @subs) = @_;
290 $RemoveSubs->($cleanee, {}, @subs);
295 Makes a snapshot of the current defined functions and installs a
296 L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
301 my ($pragma, @args) = @_;
303 my (%args, $is_explicit);
308 if ($args[0] =~ /^\-/) {
309 my $key = shift @args;
310 my $value = shift @args;
311 $args{ $key } = $value;
319 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
322 $RemoveSubs->($cleanee, {}, @args);
327 # calling class, all current functions and our storage
328 my $functions = $pragma->get_functions($cleanee);
329 my $store = $pragma->get_class_store($cleanee);
330 my $stash = Package::Stash->new($cleanee);
332 # except parameter can be array ref or single value
333 my %except = map {( $_ => 1 )} (
335 ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
339 # register symbols for removal, if they have a CODE entry
340 for my $f (keys %$functions) {
341 next if $except{ $f };
342 next unless $stash->has_symbol("&$f");
343 $store->{remove}{ $f } = 1;
346 # register EOF handler on first call to import
347 unless ($store->{handler_is_installed}) {
349 $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
351 $store->{handler_is_installed} = 1;
360 This method will be called when you do a
364 It will start a new section of code that defines functions to clean up.
369 my ($pragma, %args) = @_;
371 # the calling class, the current functions and our storage
372 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
373 my $functions = $pragma->get_functions($cleanee);
374 my $store = $pragma->get_class_store($cleanee);
376 # register all unknown previous functions as excluded
377 for my $f (keys %$functions) {
378 next if $store->{remove}{ $f }
379 or $store->{exclude}{ $f };
380 $store->{exclude}{ $f } = 1;
386 =head2 get_class_store
388 This returns a reference to a hash in a passed package containing
389 information about function names included and excluded from removal.
393 sub get_class_store {
394 my ($pragma, $class) = @_;
395 my $stash = Package::Stash->new($class);
396 my $var = "%$STORAGE_VAR";
397 $stash->add_symbol($var, {})
398 unless $stash->has_symbol($var);
399 return $stash->get_symbol($var);
404 Takes a class as argument and returns all currently defined functions
405 in it as a hash reference with the function name as key and a typeglob
406 reference to the symbol as value.
411 my ($pragma, $class) = @_;
413 my $stash = Package::Stash->new($class);
415 map { $_ => $stash->get_symbol("&$_") }
416 $stash->list_all_symbols('CODE')
420 =head1 IMPLEMENTATION DETAILS
422 This module works through the effect that a
424 delete $SomePackage::{foo};
426 will remove the C<foo> symbol from C<$SomePackage> for run time lookups
427 (e.g., method calls) but will leave the entry alive to be called by
428 already resolved names in the package itself. C<namespace::clean> will
429 restore and therefor in effect keep all glob slots that aren't C<CODE>.
431 A test file has been added to the perl core to ensure that this behaviour
432 will be stable in future releases.
434 Just for completeness sake, if you want to remove the symbol completely,
435 use C<undef> instead.
439 This module is fully functional in a pure-perl environment, where
440 L<B::Hooks::EndOfScope> (with the XS dependency L<Variable::Magic>), may
441 not be available. However in this case this module falls back to a
442 L<tie()|perlfunc/tie> of L<%^H|perlvar/%^H> which may or may not interfere
443 with some crack you may be doing independently of namespace::clean.
445 If you want to ensure that your codebase is protected from this unlikely
446 clash, you need to explicitly depend on L<B::Hooks::EndOfScope>.
450 L<B::Hooks::EndOfScope>
454 Many thanks to Matt S Trout for the inspiration on the whole idea.
462 Robert 'phaylon' Sedlacek <rs@474.at>
466 Florian Ragwitz <rafl@debian.org>
470 Jesse Luehrs <doy@tozt.net>
474 Peter Rabbitson <ribasushi@cpan.org>
478 =head1 COPYRIGHT AND LICENSE
480 This software is copyright (c) 2011 by Robert 'phaylon' Sedlacek.
482 This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
487 'Danger! Laws of Thermodynamics may not apply.'