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