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