ignore mymetas
[p5sagit/namespace-clean.git] / lib / namespace / clean.pm
CommitLineData
40aef9d6 1package namespace::clean;
04312494 2# ABSTRACT: Keep imports and functions out of your namespace
40aef9d6 3
4use warnings;
5use strict;
6
8177960a 7use vars qw( $STORAGE_VAR );
9887772b 8use Package::Stash;
40aef9d6 9
8620b198 10our $VERSION = '0.21';
fa84e425 11
8177960a 12$STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
40aef9d6 13
9887772b 14BEGIN {
1cd61d2e 15
7b026c56 16 use warnings;
17 use strict;
18
1cd61d2e 19 # when changing also change in Makefile.PL
20 my $b_h_eos_req = '0.07';
21
9887772b 22 if (eval {
23 require B::Hooks::EndOfScope;
1cd61d2e 24 B::Hooks::EndOfScope->VERSION($b_h_eos_req);
9887772b 25 1
26 } ) {
27 B::Hooks::EndOfScope->import('on_scope_end');
28 }
29 else {
30 eval <<'PP' or die $@;
31
1cd61d2e 32 use Tie::Hash ();
33
34 {
35 package namespace::clean::_TieHintHash;
36
7b026c56 37 use warnings;
38 use strict;
39
1cd61d2e 40 use base 'Tie::ExtraHash';
41 }
42
9887772b 43 {
44 package namespace::clean::_ScopeGuard;
45
7b026c56 46 use warnings;
47 use strict;
48
9887772b 49 sub arm { bless [ $_[1] ] }
50
51 sub DESTROY { $_[0]->[0]->() }
52 }
53
9887772b 54
55 sub on_scope_end (&) {
56 $^H |= 0x020000;
57
58 if( my $stack = tied( %^H ) ) {
1cd61d2e 59 if ( (my $c = ref $stack) ne 'namespace::clean::_TieHintHash') {
60 die <<EOE;
61========================================================================
62 !!! F A T A L E R R O R !!!
63
64 foreign tie() of %^H detected
65========================================================================
66
67namespace::clean is currently operating in pure-perl fallback mode, because
68your system is lacking the necessary dependency B::Hooks::EndOfScope $b_h_eos_req.
69In this mode namespace::clean expects to be able to tie() the hinthash %^H,
70however it is apparently already tied by means unknown to the tie-class
71$c
72
73Since this is a no-win situation execution will abort here and now. Please
74try to find out which other module is relying on hinthash tie() ability,
75and file a bug for both the perpetrator and namespace::clean, so that the
76authors can figure out an acceptable way of moving forward.
77
78EOE
79 }
9887772b 80 push @$stack, namespace::clean::_ScopeGuard->arm(shift);
81 }
82 else {
1cd61d2e 83 tie( %^H, 'namespace::clean::_TieHintHash', namespace::clean::_ScopeGuard->arm(shift) );
9887772b 84 }
85 }
86
87 1;
88
89PP
90
91 }
92}
93
09e4e594 94=head1 NAME
95
96namespace::clean - Keep imports and functions out of your namespace
97
40aef9d6 98=head1 SYNOPSIS
99
100 package Foo;
101 use warnings;
102 use strict;
103
6c0ece9b 104 use Carp qw(croak); # 'croak' will be removed
40aef9d6 105
6c0ece9b 106 sub bar { 23 } # 'bar' will be removed
40aef9d6 107
6c0ece9b 108 # remove all previously defined functions
40aef9d6 109 use namespace::clean;
110
6c0ece9b 111 sub baz { bar() } # 'baz' still defined, 'bar' still bound
40aef9d6 112
6c0ece9b 113 # begin to collection function names from here again
9b680ffe 114 no namespace::clean;
115
6c0ece9b 116 sub quux { baz() } # 'quux' will be removed
9b680ffe 117
6c0ece9b 118 # remove all functions defined after the 'no' unimport
9b680ffe 119 use namespace::clean;
120
6c0ece9b 121 # Will print: 'No', 'No', 'Yes' and 'No'
40aef9d6 122 print +(__PACKAGE__->can('croak') ? 'Yes' : 'No'), "\n";
123 print +(__PACKAGE__->can('bar') ? 'Yes' : 'No'), "\n";
124 print +(__PACKAGE__->can('baz') ? 'Yes' : 'No'), "\n";
9b680ffe 125 print +(__PACKAGE__->can('quux') ? 'Yes' : 'No'), "\n";
40aef9d6 126
127 1;
128
129=head1 DESCRIPTION
130
de673bbf 131=head2 Keeping packages clean
132
40aef9d6 133When you define a function, or import one, into a Perl package, it will
134naturally also be available as a method. This does not per se cause
135problems, but it can complicate subclassing and, for example, plugin
04312494 136classes that are included via multiple inheritance by loading them as
6c0ece9b 137base classes.
40aef9d6 138
139The C<namespace::clean> pragma will remove all previously declared or
140imported symbols at the end of the current package's compile cycle.
6c0ece9b 141Functions called in the package itself will still be bound by their
142name, but they won't show up as methods on your class or instances.
143
144By unimporting via C<no> you can tell C<namespace::clean> to start
145collecting functions for the next C<use namespace::clean;> specification.
40aef9d6 146
53e92ec5 147You can use the C<-except> flag to tell C<namespace::clean> that you
472d4b1e 148don't want it to remove a certain function or method. A common use would
149be a module exporting an C<import> method along with some functions:
53e92ec5 150
151 use ModuleExportingImport;
152 use namespace::clean -except => [qw( import )];
153
472d4b1e 154If you just want to C<-except> a single sub, you can pass it directly.
155For more than one value you have to use an array reference.
156
271df965 157=head2 Explicitly removing functions when your scope is compiled
de673bbf 158
271df965 159It is also possible to explicitly tell C<namespace::clean> what packages
de673bbf 160to remove when the surrounding scope has finished compiling. Here is an
161example:
162
163 package Foo;
164 use strict;
165
166 # blessed NOT available
167
168 sub my_class {
169 use Scalar::Util qw( blessed );
170 use namespace::clean qw( blessed );
171
172 # blessed available
173 return blessed shift;
174 }
175
176 # blessed NOT available
177
1a1be5dc 178=head2 Moose
179
180When using C<namespace::clean> together with L<Moose> you want to keep
181the installed C<meta> method. So your classes should look like:
182
183 package Foo;
184 use Moose;
185 use namespace::clean -except => 'meta';
186 ...
187
188Same goes for L<Moose::Role>.
189
fcfe7810 190=head2 Cleaning other packages
191
192You can tell C<namespace::clean> that you want to clean up another package
193instead of the one importing. To do this you have to pass in the C<-cleanee>
194option like this:
195
196 package My::MooseX::namespace::clean;
197 use strict;
198
199 use namespace::clean (); # no cleanup, just load
200
201 sub import {
202 namespace::clean->import(
203 -cleanee => scalar(caller),
204 -except => 'meta',
205 );
206 }
207
208If you don't care about C<namespace::clean>s discover-and-C<-except> logic, and
209just want to remove subroutines, try L</clean_subroutines>.
210
fa84e425 211=head1 METHODS
212
213=head2 clean_subroutines
40aef9d6 214
fcfe7810 215This exposes the actual subroutine-removal logic.
216
217 namespace::clean->clean_subroutines($cleanee, qw( subA subB ));
218
219will remove C<subA> and C<subB> from C<$cleanee>. Note that this will remove the
220subroutines B<immediately> and not wait for scope end. If you want to have this
221effect at a specific time (e.g. C<namespace::clean> acts on scope compile end)
222it is your responsibility to make sure it runs at that time.
40aef9d6 223
224=cut
225
017bd598 226my $sub_utils_loaded;
227my $DebuggerRename = sub {
228 my ($f, $sub, $cleanee_stash, $deleted_stash) = @_;
229
230 if (! defined $sub_utils_loaded ) {
231 $sub_utils_loaded = do {
232 my $sn_ver = 0.04;
233 eval { require Sub::Name; Sub::Name->VERSION($sn_ver) }
234 or die "Sub::Name $sn_ver required when running under -d or equivalent: $@";
235
236 my $si_ver = 0.04;
237 eval { require Sub::Identify; Sub::Identify->VERSION($si_ver) }
238 or die "Sub::Identify $si_ver required when running under -d or equivalent: $@";
fcfe7810 239
017bd598 240 1;
241 } ? 1 : 0;
242 }
243
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);
248 }
249};
250
251my $RemoveSubs = sub {
de673bbf 252 my $cleanee = shift;
253 my $store = shift;
16d8eca5 254 my $cleanee_stash = Package::Stash->new($cleanee);
017bd598 255 my $deleted_stash;
256
de673bbf 257 SYMBOL:
258 for my $f (@_) {
017bd598 259
de673bbf 260 # ignore already removed symbols
261 next SYMBOL if $store->{exclude}{ $f };
de673bbf 262
017bd598 263 my $sub = $cleanee_stash->get_symbol("&$f")
264 or next SYMBOL;
226432f6 265
017bd598 266 if ($^P and ref(\$cleanee_stash->namespace->{$f}) eq 'GLOB') {
226432f6 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.
017bd598 272 $DebuggerRename->(
273 $f,
274 $sub,
275 $cleanee_stash,
276 $deleted_stash ||= Package::Stash->new("namespace::clean::deleted::$cleanee"),
277 );
de673bbf 278 }
d6aecfbf 279
d64ad6f8 280 my @symbols = map {
281 my $name = $_ . $f;
282 my $def = $cleanee_stash->get_symbol($name);
283 defined($def) ? [$name, $def] : ()
ad4b1a60 284 } '$', '@', '%', '';
d64ad6f8 285
c86d6ae2 286 $cleanee_stash->remove_glob($f);
d64ad6f8 287
288 $cleanee_stash->add_symbol(@$_) for @symbols;
de673bbf 289 }
290};
53e92ec5 291
fcfe7810 292sub clean_subroutines {
293 my ($nc, $cleanee, @subs) = @_;
294 $RemoveSubs->($cleanee, {}, @subs);
295}
296
fa84e425 297=head2 import
fcfe7810 298
299Makes a snapshot of the current defined functions and installs a
300L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
301
302=cut
303
de673bbf 304sub import {
305 my ($pragma, @args) = @_;
53e92ec5 306
de673bbf 307 my (%args, $is_explicit);
fcfe7810 308
309 ARG:
310 while (@args) {
311
312 if ($args[0] =~ /^\-/) {
313 my $key = shift @args;
314 my $value = shift @args;
315 $args{ $key } = $value;
316 }
317 else {
318 $is_explicit++;
319 last ARG;
320 }
9b680ffe 321 }
322
fcfe7810 323 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
de673bbf 324 if ($is_explicit) {
aa2aafae 325 on_scope_end {
de673bbf 326 $RemoveSubs->($cleanee, {}, @args);
aa2aafae 327 };
9b680ffe 328 }
de673bbf 329 else {
330
331 # calling class, all current functions and our storage
332 my $functions = $pragma->get_functions($cleanee);
333 my $store = $pragma->get_class_store($cleanee);
16d8eca5 334 my $stash = Package::Stash->new($cleanee);
de673bbf 335
336 # except parameter can be array ref or single value
337 my %except = map {( $_ => 1 )} (
338 $args{ -except }
339 ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
340 : ()
341 );
342
343 # register symbols for removal, if they have a CODE entry
344 for my $f (keys %$functions) {
345 next if $except{ $f };
c86d6ae2 346 next unless $stash->has_symbol("&$f");
de673bbf 347 $store->{remove}{ $f } = 1;
348 }
349
350 # register EOF handler on first call to import
351 unless ($store->{handler_is_installed}) {
aa2aafae 352 on_scope_end {
de673bbf 353 $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
aa2aafae 354 };
de673bbf 355 $store->{handler_is_installed} = 1;
356 }
357
358 return 1;
359 }
9b680ffe 360}
361
fa84e425 362=head2 unimport
9b680ffe 363
364This method will be called when you do a
365
366 no namespace::clean;
367
368It will start a new section of code that defines functions to clean up.
369
370=cut
371
372sub unimport {
fcfe7810 373 my ($pragma, %args) = @_;
9b680ffe 374
6c0ece9b 375 # the calling class, the current functions and our storage
fcfe7810 376 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
9b680ffe 377 my $functions = $pragma->get_functions($cleanee);
378 my $store = $pragma->get_class_store($cleanee);
379
6c0ece9b 380 # register all unknown previous functions as excluded
9b680ffe 381 for my $f (keys %$functions) {
382 next if $store->{remove}{ $f }
383 or $store->{exclude}{ $f };
384 $store->{exclude}{ $f } = 1;
385 }
386
387 return 1;
388}
389
fa84e425 390=head2 get_class_store
9b680ffe 391
04312494 392This returns a reference to a hash in a passed package containing
6c0ece9b 393information about function names included and excluded from removal.
9b680ffe 394
395=cut
396
397sub get_class_store {
398 my ($pragma, $class) = @_;
16d8eca5 399 my $stash = Package::Stash->new($class);
5460fcfb 400 my $var = "%$STORAGE_VAR";
c86d6ae2 401 $stash->add_symbol($var, {})
402 unless $stash->has_symbol($var);
403 return $stash->get_symbol($var);
40aef9d6 404}
405
fa84e425 406=head2 get_functions
40aef9d6 407
408Takes a class as argument and returns all currently defined functions
409in it as a hash reference with the function name as key and a typeglob
410reference to the symbol as value.
411
412=cut
413
414sub get_functions {
415 my ($pragma, $class) = @_;
416
16d8eca5 417 my $stash = Package::Stash->new($class);
40aef9d6 418 return {
c86d6ae2 419 map { $_ => $stash->get_symbol("&$_") }
420 $stash->list_all_symbols('CODE')
40aef9d6 421 };
422}
423
6c0ece9b 424=head1 IMPLEMENTATION DETAILS
425
04312494 426This module works through the effect that a
6c0ece9b 427
428 delete $SomePackage::{foo};
429
430will remove the C<foo> symbol from C<$SomePackage> for run time lookups
431(e.g., method calls) but will leave the entry alive to be called by
472d4b1e 432already resolved names in the package itself. C<namespace::clean> will
433restore and therefor in effect keep all glob slots that aren't C<CODE>.
6c0ece9b 434
435A test file has been added to the perl core to ensure that this behaviour
436will be stable in future releases.
437
438Just for completeness sake, if you want to remove the symbol completely,
439use C<undef> instead.
440
9887772b 441=head1 CAVEATS
442
443This module is fully functional in a pure-perl environment, where
c8e3fb7e 444L<B::Hooks::EndOfScope> (with the XS dependency L<Variable::Magic>), may
445not be available. However in this case this module falls back to a
9887772b 446L<tie()|perlfunc/tie> of L<%^H|perlvar/%^H> which may or may not interfere
447with some crack you may be doing independently of namespace::clean.
448
c8e3fb7e 449If you want to ensure that your codebase is protected from this unlikely
450clash, you need to explicitly depend on L<B::Hooks::EndOfScope>.
451
40aef9d6 452=head1 SEE ALSO
453
705fe1b1 454L<B::Hooks::EndOfScope>
40aef9d6 455
04312494 456=head1 THANKS
40aef9d6 457
04312494 458Many thanks to Matt S Trout for the inspiration on the whole idea.
40aef9d6 459
fa84e425 460=head1 AUTHORS
461
462=over
463
464=item *
465
466Robert 'phaylon' Sedlacek <rs@474.at>
467
468=item *
469
470Florian Ragwitz <rafl@debian.org>
471
472=item *
473
474Jesse Luehrs <doy@tozt.net>
475
476=item *
477
478Peter Rabbitson <ribasushi@cpan.org>
479
480=back
481
482=head1 COPYRIGHT AND LICENSE
483
484This software is copyright (c) 2011 by Robert 'phaylon' Sedlacek.
485
486This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
487
40aef9d6 488=cut
489
de673bbf 490no warnings;
491'Danger! Laws of Thermodynamics may not apply.'