File Coverage

inc/namespace/clean.pm
Criterion Covered Total %
statement 12 14 85.7
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             #line 1
2             package namespace::clean;
3 3     3   3092 BEGIN {
4             $namespace::clean::AUTHORITY = 'cpan:PHAYLON';
5             }
6 3     3   63 BEGIN {
7             $namespace::clean::VERSION = '0.20';
8             }
9             # ABSTRACT: Keep imports and functions out of your namespace
10 3     3   24  
  3         11  
  3         93  
11 3     3   17 use warnings;
  3         6  
  3         106  
12             use strict;
13 3     3   17  
  3         6  
  3         186  
14 3     3   7776 use vars qw( $STORAGE_VAR );
  0            
  0            
15             use Sub::Name 0.04 qw(subname);
16             use Sub::Identify 0.04 qw(sub_fullname);
17             use Package::Stash 0.22;
18             use B::Hooks::EndOfScope 0.07;
19              
20             $STORAGE_VAR = '__NAMESPACE_CLEAN_STORAGE';
21              
22              
23             my $RemoveSubs = sub {
24              
25             my $cleanee = shift;
26             my $store = shift;
27             my $cleanee_stash = Package::Stash->new($cleanee);
28             my $deleted_stash = Package::Stash->new("namespace::clean::deleted::$cleanee");
29             SYMBOL:
30             for my $f (@_) {
31             my $variable = "&$f";
32             # ignore already removed symbols
33             next SYMBOL if $store->{exclude}{ $f };
34              
35             next SYMBOL unless $cleanee_stash->has_symbol($variable);
36              
37             if (ref(\$cleanee_stash->namespace->{$f}) eq 'GLOB') {
38             # convince the Perl debugger to work
39             # it assumes that sub_fullname($sub) can always be used to find the CV again
40             # since we are deleting the glob where the subroutine was originally
41             # defined, that assumption no longer holds, so we need to move it
42             # elsewhere and point the CV's name to the new glob.
43             my $sub = $cleanee_stash->get_symbol($variable);
44             if ( sub_fullname($sub) eq ($cleanee_stash->name . "::$f") ) {
45             my $new_fq = $deleted_stash->name . "::$f";
46             subname($new_fq, $sub);
47             $deleted_stash->add_symbol($variable, $sub);
48             }
49             }
50              
51             my ($scalar, $array, $hash, $io) = map {
52             $cleanee_stash->get_symbol($_ . $f)
53             } '$', '@', '%', '';
54             $cleanee_stash->remove_glob($f);
55             for my $var (['$', $scalar], ['@', $array], ['%', $hash], ['', $io]) {
56             next unless defined $var->[1];
57             $cleanee_stash->add_symbol($var->[0] . $f, $var->[1]);
58             }
59             }
60             };
61              
62             sub clean_subroutines {
63             my ($nc, $cleanee, @subs) = @_;
64             $RemoveSubs->($cleanee, {}, @subs);
65             }
66              
67              
68             sub import {
69             my ($pragma, @args) = @_;
70              
71             my (%args, $is_explicit);
72              
73             ARG:
74             while (@args) {
75              
76             if ($args[0] =~ /^\-/) {
77             my $key = shift @args;
78             my $value = shift @args;
79             $args{ $key } = $value;
80             }
81             else {
82             $is_explicit++;
83             last ARG;
84             }
85             }
86              
87             my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
88             if ($is_explicit) {
89             on_scope_end {
90             $RemoveSubs->($cleanee, {}, @args);
91             };
92             }
93             else {
94              
95             # calling class, all current functions and our storage
96             my $functions = $pragma->get_functions($cleanee);
97             my $store = $pragma->get_class_store($cleanee);
98             my $stash = Package::Stash->new($cleanee);
99              
100             # except parameter can be array ref or single value
101             my %except = map {( $_ => 1 )} (
102             $args{ -except }
103             ? ( ref $args{ -except } eq 'ARRAY' ? @{ $args{ -except } } : $args{ -except } )
104             : ()
105             );
106              
107             # register symbols for removal, if they have a CODE entry
108             for my $f (keys %$functions) {
109             next if $except{ $f };
110             next unless $stash->has_symbol("&$f");
111             $store->{remove}{ $f } = 1;
112             }
113              
114             # register EOF handler on first call to import
115             unless ($store->{handler_is_installed}) {
116             on_scope_end {
117             $RemoveSubs->($cleanee, $store, keys %{ $store->{remove} });
118             };
119             $store->{handler_is_installed} = 1;
120             }
121              
122             return 1;
123             }
124             }
125              
126              
127             sub unimport {
128             my ($pragma, %args) = @_;
129              
130             # the calling class, the current functions and our storage
131             my $cleanee = exists $args{ -cleanee } ? $args{ -cleanee } : scalar caller;
132             my $functions = $pragma->get_functions($cleanee);
133             my $store = $pragma->get_class_store($cleanee);
134              
135             # register all unknown previous functions as excluded
136             for my $f (keys %$functions) {
137             next if $store->{remove}{ $f }
138             or $store->{exclude}{ $f };
139             $store->{exclude}{ $f } = 1;
140             }
141              
142             return 1;
143             }
144              
145              
146             sub get_class_store {
147             my ($pragma, $class) = @_;
148             my $stash = Package::Stash->new($class);
149             my $var = "%$STORAGE_VAR";
150             $stash->add_symbol($var, {})
151             unless $stash->has_symbol($var);
152             return $stash->get_symbol($var);
153             }
154              
155              
156             sub get_functions {
157             my ($pragma, $class) = @_;
158              
159             my $stash = Package::Stash->new($class);
160             return {
161             map { $_ => $stash->get_symbol("&$_") }
162             $stash->list_all_symbols('CODE')
163             };
164             }
165              
166              
167             no warnings;
168             'Danger! Laws of Thermodynamics may not apply.'
169              
170             __END__