package namespace::clean;
-
-=head1 NAME
-
-namespace::clean - Keep imports and functions out of your namespace
-
-=cut
+# ABSTRACT: Keep imports and functions out of your namespace
use warnings;
use strict;
-use vars qw( $VERSION $STORAGE_VAR $SCOPE_HOOK_KEY $SCOPE_EXPLICIT );
-use B::Hooks::EndOfScope;
-use Package::Stash;
-use Sub::Identify qw(sub_fullname);
-use Sub::Name qw(subname);
-
-=head1 VERSION
-
-0.13
-
-=cut
+use vars qw( $STORAGE_VAR );
+use Package::Stash 0.22;
+use B::Hooks::EndOfScope 0.07;
-$VERSION = '0.14';
-$STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
+$STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
=head1 SYNOPSIS
When you define a function, or import one, into a Perl package, it will
naturally also be available as a method. This does not per se cause
problems, but it can complicate subclassing and, for example, plugin
-classes that are included via multiple inheritance by loading them as
+classes that are included via multiple inheritance by loading them as
base classes.
The C<namespace::clean> pragma will remove all previously declared or
If you just want to C<-except> a single sub, you can pass it directly.
For more than one value you have to use an array reference.
-=head2 Explicitely removing functions when your scope is compiled
+=head2 Explicitly removing functions when your scope is compiled
-It is also possible to explicitely tell C<namespace::clean> what packages
+It is also possible to explicitly tell C<namespace::clean> what packages
to remove when the surrounding scope has finished compiling. Here is an
example:
If you don't care about C<namespace::clean>s discover-and-C<-except> logic, and
just want to remove subroutines, try L</clean_subroutines>.
-=head1 METHODS
-
-You shouldn't need to call any of these. Just C<use> the package at the
-appropriate place.
-
-=cut
-
-=head2 clean_subroutines
+=method clean_subroutines
This exposes the actual subroutine-removal logic.
=cut
-my $RemoveSubs = sub {
+my $sub_utils_loaded;
+my $DebuggerRename = sub {
+ my ($f, $sub, $cleanee_stash, $deleted_stash) = @_;
+
+ if (! defined $sub_utils_loaded ) {
+ $sub_utils_loaded = do {
+ my $sn_ver = 0.04;
+ eval { require Sub::Name; Sub::Name->VERSION($sn_ver) }
+ or die "Sub::Name $sn_ver required when running under -d or equivalent: $@";
+
+ my $si_ver = 0.04;
+ eval { require Sub::Identify; Sub::Identify->VERSION($si_ver) }
+ or die "Sub::Identify $si_ver required when running under -d or equivalent: $@";
+ 1;
+ } ? 1 : 0;
+ }
+
+ if ( Sub::Identify::sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
+ my $new_fq = $deleted_stash->name . "::$f";
+ Sub::Name::subname($new_fq, $sub);
+ $deleted_stash->add_symbol("&$f", $sub);
+ }
+};
+
+my $RemoveSubs = sub {
my $cleanee = shift;
my $store = shift;
my $cleanee_stash = Package::Stash->new($cleanee);
- my $deleted_stash = Package::Stash->new("namespace::clean::deleted::$cleanee");
+ my $deleted_stash;
+
SYMBOL:
for my $f (@_) {
- my $variable = "&$f";
+
# ignore already removed symbols
next SYMBOL if $store->{exclude}{ $f };
- next SYMBOL unless $cleanee_stash->has_package_symbol($variable);
+ my $sub = $cleanee_stash->get_symbol("&$f")
+ or next SYMBOL;
- if (ref(\$cleanee_stash->namespace->{$f}) eq 'GLOB') {
+ if ($^P and ref(\$cleanee_stash->namespace->{$f}) eq 'GLOB') {
# convince the Perl debugger to work
# it assumes that sub_fullname($sub) can always be used to find the CV again
# since we are deleting the glob where the subroutine was originally
# defined, that assumption no longer holds, so we need to move it
# elsewhere and point the CV's name to the new glob.
- my $sub = $cleanee_stash->get_package_symbol($variable);
- if ( sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
- my $new_fq = $deleted_stash->name . "::$f";
- subname($new_fq, $sub);
- $deleted_stash->add_package_symbol($variable, $sub);
- }
+ $DebuggerRename->(
+ $f,
+ $sub,
+ $cleanee_stash,
+ $deleted_stash ||= Package::Stash->new("namespace::clean::deleted::$cleanee"),
+ );
}
- $cleanee_stash->remove_package_symbol($variable);
+ my @symbols = map {
+ my $name = $_ . $f;
+ my $def = $cleanee_stash->get_symbol($name);
+ defined($def) ? [$name, $def] : ()
+ } '$', '@', '%', '';
+
+ $cleanee_stash->remove_glob($f);
+
+ $cleanee_stash->add_symbol(@$_) for @symbols;
}
};
$RemoveSubs->($cleanee, {}, @subs);
}
-=head2 import
+=method import
Makes a snapshot of the current defined functions and installs a
L<B::Hooks::EndOfScope> hook in the current scope to invoke the cleanups.
# register symbols for removal, if they have a CODE entry
for my $f (keys %$functions) {
next if $except{ $f };
- next unless $stash->has_package_symbol("&$f");
+ next unless $stash->has_symbol("&$f");
$store->{remove}{ $f } = 1;
}
}
}
-=head2 unimport
+=method unimport
This method will be called when you do a
return 1;
}
-=head2 get_class_store
+=method get_class_store
-This returns a reference to a hash in a passed package containing
+This returns a reference to a hash in a passed package containing
information about function names included and excluded from removal.
=cut
sub get_class_store {
my ($pragma, $class) = @_;
my $stash = Package::Stash->new($class);
- return $stash->get_package_symbol("%$STORAGE_VAR");
+ my $var = "%$STORAGE_VAR";
+ $stash->add_symbol($var, {})
+ unless $stash->has_symbol($var);
+ return $stash->get_symbol($var);
}
-=head2 get_functions
+=method get_functions
Takes a class as argument and returns all currently defined functions
in it as a hash reference with the function name as key and a typeglob
my $stash = Package::Stash->new($class);
return {
- map { $_ => $stash->get_package_symbol("&$_") }
- $stash->list_all_package_symbols('CODE')
+ map { $_ => $stash->get_symbol("&$_") }
+ $stash->list_all_symbols('CODE')
};
}
-=head1 BUGS
-
-C<namespace::clean> will clobber any formats that have the same name as
-a deleted sub. This is due to a bug in perl that makes it impossible to
-re-assign the FORMAT ref into a new glob.
-
=head1 IMPLEMENTATION DETAILS
-This module works through the effect that a
+This module works through the effect that a
delete $SomePackage::{foo};
L<B::Hooks::EndOfScope>
-=head1 AUTHOR AND COPYRIGHT
-
-Robert 'phaylon' Sedlacek C<E<lt>rs@474.atE<gt>>, with many thanks to
-Matt S Trout for the inspiration on the whole idea.
-
-=head1 LICENSE
+=head1 THANKS
-This program is free software; you can redistribute it and/or modify
-it under the same terms as perl itself.
+Many thanks to Matt S Trout for the inspiration on the whole idea.
=cut