Version 0.20
[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 );
04312494 8use Sub::Name 0.04 qw(subname);
9use Sub::Identify 0.04 qw(sub_fullname);
20c9e0be 10use Package::Stash 0.22;
04312494 11use B::Hooks::EndOfScope 0.07;
40aef9d6 12
8177960a 13$STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
40aef9d6 14
15=head1 SYNOPSIS
16
17 package Foo;
18 use warnings;
19 use strict;
20
6c0ece9b 21 use Carp qw(croak); # 'croak' will be removed
40aef9d6 22
6c0ece9b 23 sub bar { 23 } # 'bar' will be removed
40aef9d6 24
6c0ece9b 25 # remove all previously defined functions
40aef9d6 26 use namespace::clean;
27
6c0ece9b 28 sub baz { bar() } # 'baz' still defined, 'bar' still bound
40aef9d6 29
6c0ece9b 30 # begin to collection function names from here again
9b680ffe 31 no namespace::clean;
32
6c0ece9b 33 sub quux { baz() } # 'quux' will be removed
9b680ffe 34
6c0ece9b 35 # remove all functions defined after the 'no' unimport
9b680ffe 36 use namespace::clean;
37
6c0ece9b 38 # Will print: 'No', 'No', 'Yes' and 'No'
40aef9d6 39 print +(__PACKAGE__->can('croak') ? 'Yes' : 'No'), "\n";
40 print +(__PACKAGE__->can('bar') ? 'Yes' : 'No'), "\n";
41 print +(__PACKAGE__->can('baz') ? 'Yes' : 'No'), "\n";
9b680ffe 42 print +(__PACKAGE__->can('quux') ? 'Yes' : 'No'), "\n";
40aef9d6 43
44 1;
45
46=head1 DESCRIPTION
47
de673bbf 48=head2 Keeping packages clean
49
40aef9d6 50When you define a function, or import one, into a Perl package, it will
51naturally also be available as a method. This does not per se cause
52problems, but it can complicate subclassing and, for example, plugin
04312494 53classes that are included via multiple inheritance by loading them as
6c0ece9b 54base classes.
40aef9d6 55
56The C<namespace::clean> pragma will remove all previously declared or
57imported symbols at the end of the current package's compile cycle.
6c0ece9b 58Functions called in the package itself will still be bound by their
59name, but they won't show up as methods on your class or instances.
60
61By unimporting via C<no> you can tell C<namespace::clean> to start
62collecting functions for the next C<use namespace::clean;> specification.
40aef9d6 63
53e92ec5 64You can use the C<-except> flag to tell C<namespace::clean> that you
472d4b1e 65don't want it to remove a certain function or method. A common use would
66be a module exporting an C<import> method along with some functions:
53e92ec5 67
68 use ModuleExportingImport;
69 use namespace::clean -except => [qw( import )];
70
472d4b1e 71If you just want to C<-except> a single sub, you can pass it directly.
72For more than one value you have to use an array reference.
73
de673bbf 74=head2 Explicitely removing functions when your scope is compiled
75
76It is also possible to explicitely tell C<namespace::clean> what packages
77to remove when the surrounding scope has finished compiling. Here is an
78example:
79
80 package Foo;
81 use strict;
82
83 # blessed NOT available
84
85 sub my_class {
86 use Scalar::Util qw( blessed );
87 use namespace::clean qw( blessed );
88
89 # blessed available
90 return blessed shift;
91 }
92
93 # blessed NOT available
94
1a1be5dc 95=head2 Moose
96
97When using C<namespace::clean> together with L<Moose> you want to keep
98the installed C<meta> method. So your classes should look like:
99
100 package Foo;
101 use Moose;
102 use namespace::clean -except => 'meta';
103 ...
104
105Same goes for L<Moose::Role>.
106
fcfe7810 107=head2 Cleaning other packages
108
109You can tell C<namespace::clean> that you want to clean up another package
110instead of the one importing. To do this you have to pass in the C<-cleanee>
111option like this:
112
113 package My::MooseX::namespace::clean;
114 use strict;
115
116 use namespace::clean (); # no cleanup, just load
117
118 sub import {
119 namespace::clean->import(
120 -cleanee => scalar(caller),
121 -except => 'meta',
122 );
123 }
124
125If you don't care about C<namespace::clean>s discover-and-C<-except> logic, and
126just want to remove subroutines, try L</clean_subroutines>.
127
04312494 128=method clean_subroutines
40aef9d6 129
fcfe7810 130This exposes the actual subroutine-removal logic.
131
132 namespace::clean->clean_subroutines($cleanee, qw( subA subB ));
133
134will remove C<subA> and C<subB> from C<$cleanee>. Note that this will remove the
135subroutines B<immediately> and not wait for scope end. If you want to have this
136effect at a specific time (e.g. C<namespace::clean> acts on scope compile end)
137it is your responsibility to make sure it runs at that time.
40aef9d6 138
139=cut
140
de673bbf 141my $RemoveSubs = sub {
fcfe7810 142
de673bbf 143 my $cleanee = shift;
144 my $store = shift;
16d8eca5 145 my $cleanee_stash = Package::Stash->new($cleanee);
146 my $deleted_stash = Package::Stash->new("namespace::clean::deleted::$cleanee");
de673bbf 147 SYMBOL:
148 for my $f (@_) {
d6aecfbf 149 my $variable = "&$f";
de673bbf 150 # ignore already removed symbols
151 next SYMBOL if $store->{exclude}{ $f };
de673bbf 152
c86d6ae2 153 next SYMBOL unless $cleanee_stash->has_symbol($variable);
226432f6 154
d6aecfbf 155 if (ref(\$cleanee_stash->namespace->{$f}) eq 'GLOB') {
226432f6 156 # convince the Perl debugger to work
157 # it assumes that sub_fullname($sub) can always be used to find the CV again
158 # since we are deleting the glob where the subroutine was originally
159 # defined, that assumption no longer holds, so we need to move it
160 # elsewhere and point the CV's name to the new glob.
c86d6ae2 161 my $sub = $cleanee_stash->get_symbol($variable);
d6aecfbf 162 if ( sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
163 my $new_fq = $deleted_stash->name . "::$f";
226432f6 164 subname($new_fq, $sub);
c86d6ae2 165 $deleted_stash->add_symbol($variable, $sub);
226432f6 166 }
de673bbf 167 }
d6aecfbf 168
ad4b1a60 169 my ($scalar, $array, $hash, $io) = map {
c86d6ae2 170 $cleanee_stash->get_symbol($_ . $f)
ad4b1a60 171 } '$', '@', '%', '';
c86d6ae2 172 $cleanee_stash->remove_glob($f);
ad4b1a60 173 for my $var (['$', $scalar], ['@', $array], ['%', $hash], ['', $io]) {
174 next unless defined $var->[1];
c86d6ae2 175 $cleanee_stash->add_symbol($var->[0] . $f, $var->[1]);
ad4b1a60 176 }
de673bbf 177 }
178};
53e92ec5 179
fcfe7810 180sub clean_subroutines {
181 my ($nc, $cleanee, @subs) = @_;
182 $RemoveSubs->($cleanee, {}, @subs);
183}
184
04312494 185=method import
fcfe7810 186
187Makes a snapshot of the current defined functions and installs a
188L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
189
190=cut
191
de673bbf 192sub import {
193 my ($pragma, @args) = @_;
53e92ec5 194
de673bbf 195 my (%args, $is_explicit);
fcfe7810 196
197 ARG:
198 while (@args) {
199
200 if ($args[0] =~ /^\-/) {
201 my $key = shift @args;
202 my $value = shift @args;
203 $args{ $key } = $value;
204 }
205 else {
206 $is_explicit++;
207 last ARG;
208 }
9b680ffe 209 }
210
fcfe7810 211 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
de673bbf 212 if ($is_explicit) {
aa2aafae 213 on_scope_end {
de673bbf 214 $RemoveSubs->($cleanee, {}, @args);
aa2aafae 215 };
9b680ffe 216 }
de673bbf 217 else {
218
219 # calling class, all current functions and our storage
220 my $functions = $pragma->get_functions($cleanee);
221 my $store = $pragma->get_class_store($cleanee);
16d8eca5 222 my $stash = Package::Stash->new($cleanee);
de673bbf 223
224 # except parameter can be array ref or single value
225 my %except = map {( $_ => 1 )} (
226 $args{ -except }
227 ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
228 : ()
229 );
230
231 # register symbols for removal, if they have a CODE entry
232 for my $f (keys %$functions) {
233 next if $except{ $f };
c86d6ae2 234 next unless $stash->has_symbol("&$f");
de673bbf 235 $store->{remove}{ $f } = 1;
236 }
237
238 # register EOF handler on first call to import
239 unless ($store->{handler_is_installed}) {
aa2aafae 240 on_scope_end {
de673bbf 241 $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
aa2aafae 242 };
de673bbf 243 $store->{handler_is_installed} = 1;
244 }
245
246 return 1;
247 }
9b680ffe 248}
249
04312494 250=method unimport
9b680ffe 251
252This method will be called when you do a
253
254 no namespace::clean;
255
256It will start a new section of code that defines functions to clean up.
257
258=cut
259
260sub unimport {
fcfe7810 261 my ($pragma, %args) = @_;
9b680ffe 262
6c0ece9b 263 # the calling class, the current functions and our storage
fcfe7810 264 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
9b680ffe 265 my $functions = $pragma->get_functions($cleanee);
266 my $store = $pragma->get_class_store($cleanee);
267
6c0ece9b 268 # register all unknown previous functions as excluded
9b680ffe 269 for my $f (keys %$functions) {
270 next if $store->{remove}{ $f }
271 or $store->{exclude}{ $f };
272 $store->{exclude}{ $f } = 1;
273 }
274
275 return 1;
276}
277
04312494 278=method get_class_store
9b680ffe 279
04312494 280This returns a reference to a hash in a passed package containing
6c0ece9b 281information about function names included and excluded from removal.
9b680ffe 282
283=cut
284
285sub get_class_store {
286 my ($pragma, $class) = @_;
16d8eca5 287 my $stash = Package::Stash->new($class);
5460fcfb 288 my $var = "%$STORAGE_VAR";
c86d6ae2 289 $stash->add_symbol($var, {})
290 unless $stash->has_symbol($var);
291 return $stash->get_symbol($var);
40aef9d6 292}
293
04312494 294=method get_functions
40aef9d6 295
296Takes a class as argument and returns all currently defined functions
297in it as a hash reference with the function name as key and a typeglob
298reference to the symbol as value.
299
300=cut
301
302sub get_functions {
303 my ($pragma, $class) = @_;
304
16d8eca5 305 my $stash = Package::Stash->new($class);
40aef9d6 306 return {
c86d6ae2 307 map { $_ => $stash->get_symbol("&$_") }
308 $stash->list_all_symbols('CODE')
40aef9d6 309 };
310}
311
6c0ece9b 312=head1 IMPLEMENTATION DETAILS
313
04312494 314This module works through the effect that a
6c0ece9b 315
316 delete $SomePackage::{foo};
317
318will remove the C<foo> symbol from C<$SomePackage> for run time lookups
319(e.g., method calls) but will leave the entry alive to be called by
472d4b1e 320already resolved names in the package itself. C<namespace::clean> will
321restore and therefor in effect keep all glob slots that aren't C<CODE>.
6c0ece9b 322
323A test file has been added to the perl core to ensure that this behaviour
324will be stable in future releases.
325
326Just for completeness sake, if you want to remove the symbol completely,
327use C<undef> instead.
328
40aef9d6 329=head1 SEE ALSO
330
705fe1b1 331L<B::Hooks::EndOfScope>
40aef9d6 332
04312494 333=head1 THANKS
40aef9d6 334
04312494 335Many thanks to Matt S Trout for the inspiration on the whole idea.
40aef9d6 336
337=cut
338
de673bbf 339no warnings;
340'Danger! Laws of Thermodynamics may not apply.'