vivify the storage var manually
[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);
10use Package::Stash 0.03;
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
d6aecfbf 153 next SYMBOL unless $cleanee_stash->has_package_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.
d6aecfbf 161 my $sub = $cleanee_stash->get_package_symbol($variable);
162 if ( sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
163 my $new_fq = $deleted_stash->name . "::$f";
226432f6 164 subname($new_fq, $sub);
d6aecfbf 165 $deleted_stash->add_package_symbol($variable, $sub);
226432f6 166 }
de673bbf 167 }
d6aecfbf 168
169 $cleanee_stash->remove_package_symbol($variable);
de673bbf 170 }
171};
53e92ec5 172
fcfe7810 173sub clean_subroutines {
174 my ($nc, $cleanee, @subs) = @_;
175 $RemoveSubs->($cleanee, {}, @subs);
176}
177
04312494 178=method import
fcfe7810 179
180Makes a snapshot of the current defined functions and installs a
181L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
182
183=cut
184
de673bbf 185sub import {
186 my ($pragma, @args) = @_;
53e92ec5 187
de673bbf 188 my (%args, $is_explicit);
fcfe7810 189
190 ARG:
191 while (@args) {
192
193 if ($args[0] =~ /^\-/) {
194 my $key = shift @args;
195 my $value = shift @args;
196 $args{ $key } = $value;
197 }
198 else {
199 $is_explicit++;
200 last ARG;
201 }
9b680ffe 202 }
203
fcfe7810 204 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
de673bbf 205 if ($is_explicit) {
aa2aafae 206 on_scope_end {
de673bbf 207 $RemoveSubs->($cleanee, {}, @args);
aa2aafae 208 };
9b680ffe 209 }
de673bbf 210 else {
211
212 # calling class, all current functions and our storage
213 my $functions = $pragma->get_functions($cleanee);
214 my $store = $pragma->get_class_store($cleanee);
16d8eca5 215 my $stash = Package::Stash->new($cleanee);
de673bbf 216
217 # except parameter can be array ref or single value
218 my %except = map {( $_ => 1 )} (
219 $args{ -except }
220 ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
221 : ()
222 );
223
224 # register symbols for removal, if they have a CODE entry
225 for my $f (keys %$functions) {
226 next if $except{ $f };
d6aecfbf 227 next unless $stash->has_package_symbol("&$f");
de673bbf 228 $store->{remove}{ $f } = 1;
229 }
230
231 # register EOF handler on first call to import
232 unless ($store->{handler_is_installed}) {
aa2aafae 233 on_scope_end {
de673bbf 234 $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
aa2aafae 235 };
de673bbf 236 $store->{handler_is_installed} = 1;
237 }
238
239 return 1;
240 }
9b680ffe 241}
242
04312494 243=method unimport
9b680ffe 244
245This method will be called when you do a
246
247 no namespace::clean;
248
249It will start a new section of code that defines functions to clean up.
250
251=cut
252
253sub unimport {
fcfe7810 254 my ($pragma, %args) = @_;
9b680ffe 255
6c0ece9b 256 # the calling class, the current functions and our storage
fcfe7810 257 my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
9b680ffe 258 my $functions = $pragma->get_functions($cleanee);
259 my $store = $pragma->get_class_store($cleanee);
260
6c0ece9b 261 # register all unknown previous functions as excluded
9b680ffe 262 for my $f (keys %$functions) {
263 next if $store->{remove}{ $f }
264 or $store->{exclude}{ $f };
265 $store->{exclude}{ $f } = 1;
266 }
267
268 return 1;
269}
270
04312494 271=method get_class_store
9b680ffe 272
04312494 273This returns a reference to a hash in a passed package containing
6c0ece9b 274information about function names included and excluded from removal.
9b680ffe 275
276=cut
277
278sub get_class_store {
279 my ($pragma, $class) = @_;
16d8eca5 280 my $stash = Package::Stash->new($class);
5460fcfb 281 my $var = "%$STORAGE_VAR";
282 $stash->add_package_symbol($var, {})
283 unless $stash->has_package_symbol($var);
284 return $stash->get_package_symbol($var);
40aef9d6 285}
286
04312494 287=method get_functions
40aef9d6 288
289Takes a class as argument and returns all currently defined functions
290in it as a hash reference with the function name as key and a typeglob
291reference to the symbol as value.
292
293=cut
294
295sub get_functions {
296 my ($pragma, $class) = @_;
297
16d8eca5 298 my $stash = Package::Stash->new($class);
40aef9d6 299 return {
d6aecfbf 300 map { $_ => $stash->get_package_symbol("&$_") }
301 $stash->list_all_package_symbols('CODE')
40aef9d6 302 };
303}
304
6c0ece9b 305=head1 IMPLEMENTATION DETAILS
306
04312494 307This module works through the effect that a
6c0ece9b 308
309 delete $SomePackage::{foo};
310
311will remove the C<foo> symbol from C<$SomePackage> for run time lookups
312(e.g., method calls) but will leave the entry alive to be called by
472d4b1e 313already resolved names in the package itself. C<namespace::clean> will
314restore and therefor in effect keep all glob slots that aren't C<CODE>.
6c0ece9b 315
316A test file has been added to the perl core to ensure that this behaviour
317will be stable in future releases.
318
319Just for completeness sake, if you want to remove the symbol completely,
320use C<undef> instead.
321
40aef9d6 322=head1 SEE ALSO
323
705fe1b1 324L<B::Hooks::EndOfScope>
40aef9d6 325
04312494 326=head1 THANKS
40aef9d6 327
04312494 328Many thanks to Matt S Trout for the inspiration on the whole idea.
40aef9d6 329
330=cut
331
de673bbf 332no warnings;
333'Danger! Laws of Thermodynamics may not apply.'