File Coverage

blib/lib/namespace/autoclean.pm
Criterion Covered Total %
statement 59 63 93.6
branch 27 34 79.4
condition 6 12 50.0
subroutine 12 13 92.3
pod n/a
total 104 122 85.2


line stmt bran cond sub pod time code
1 9     9   642260 use strict;
  9         89  
  9         262  
2 9     9   46 use warnings;
  9         16  
  9         565  
3              
4             package namespace::autoclean; # git description: 0.28-24-g964adcf
5             # ABSTRACT: Keep imports out of your namespace
6             # KEYWORDS: namespaces clean dirty imports exports subroutines methods development
7              
8             our $VERSION = '0.29';
9              
10 9     9   4273 use B::Hooks::EndOfScope 0.12;
  9         96199  
  9         42  
11 9     9   682 use List::Util qw( first );
  9         19  
  9         933  
12 9     9   4349 use namespace::clean 0.20;
  9         38884  
  9         52  
13              
14             #pod =head1 SYNOPSIS
15             #pod
16             #pod package Foo;
17             #pod use namespace::autoclean;
18             #pod use Some::Package qw/imported_function/;
19             #pod
20             #pod sub bar { imported_function('stuff') }
21             #pod
22             #pod # later on:
23             #pod Foo->bar; # works
24             #pod Foo->imported_function; # will fail. imported_function got cleaned after compilation
25             #pod
26             #pod =head1 DESCRIPTION
27             #pod
28             #pod When you import a function into a Perl package, it will naturally also be
29             #pod available as a method.
30             #pod
31             #pod The C pragma will remove all imported symbols at the end
32             #pod of the current package's compile cycle. Functions called in the package itself
33             #pod will still be bound by their name, but they won't show up as methods on your
34             #pod class or instances.
35             #pod
36             #pod This module is very similar to L, except it
37             #pod will clean all imported functions, no matter if you imported them before or
38             #pod after you Cd the pragma. It will also not touch anything that looks like a
39             #pod method.
40             #pod
41             #pod If you're writing an exporter and you want to clean up after yourself (and your
42             #pod peers), you can use the C<-cleanee> switch to specify what package to clean:
43             #pod
44             #pod package My::MooseX::namespace::autoclean;
45             #pod use strict;
46             #pod
47             #pod use namespace::autoclean (); # no cleanup, just load
48             #pod
49             #pod sub import {
50             #pod namespace::autoclean->import(
51             #pod -cleanee => scalar(caller),
52             #pod );
53             #pod }
54             #pod
55             #pod =head1 WHAT IS AND ISN'T CLEANED
56             #pod
57             #pod C will leave behind anything that it deems a method. For
58             #pod L classes, this the based on the C method
59             #pod on from the L. For non-Moose classes, anything
60             #pod defined within the package will be identified as a method. This should match
61             #pod Moose's definition of a method. Additionally, the magic subs installed by
62             #pod L will not be cleaned.
63             #pod
64             #pod =head1 PARAMETERS
65             #pod
66             #pod =head2 -also => [ ITEM | REGEX | SUB, .. ]
67             #pod
68             #pod =head2 -also => ITEM
69             #pod
70             #pod =head2 -also => REGEX
71             #pod
72             #pod =head2 -also => SUB
73             #pod
74             #pod Sometimes you don't want to clean imports only, but also helper functions
75             #pod you're using in your methods. The C<-also> switch can be used to declare a list
76             #pod of functions that should be removed additional to any imports:
77             #pod
78             #pod use namespace::autoclean -also => ['some_function', 'another_function'];
79             #pod
80             #pod If only one function needs to be additionally cleaned the C<-also> switch also
81             #pod accepts a plain string:
82             #pod
83             #pod use namespace::autoclean -also => 'some_function';
84             #pod
85             #pod In some situations, you may wish for a more I cleaning solution.
86             #pod
87             #pod The C<-also> switch can take a Regex or a CodeRef to match against local
88             #pod function names to clean.
89             #pod
90             #pod use namespace::autoclean -also => qr/^_/
91             #pod
92             #pod use namespace::autoclean -also => sub { $_ =~ m{^_} };
93             #pod
94             #pod use namespace::autoclean -also => [qr/^_/ , qr/^hidden_/ ];
95             #pod
96             #pod use namespace::autoclean -also => [sub { $_ =~ m/^_/ or $_ =~ m/^hidden/ }, sub { uc($_) == $_ } ];
97             #pod
98             #pod =head2 -except => [ ITEM | REGEX | SUB, .. ]
99             #pod
100             #pod =head2 -except => ITEM
101             #pod
102             #pod =head2 -except => REGEX
103             #pod
104             #pod =head2 -except => SUB
105             #pod
106             #pod This takes exactly the same options as C<-also> except that anything this
107             #pod matches will I be cleaned.
108             #pod
109             #pod =head1 CAVEATS
110             #pod
111             #pod When used with L classes, the heuristic used to check for methods won't
112             #pod work correctly for methods from roles consumed at compile time.
113             #pod
114             #pod package My::Class;
115             #pod use Moo;
116             #pod use namespace::autoclean;
117             #pod
118             #pod # Bad, any consumed methods will be cleaned
119             #pod BEGIN { with 'Some::Role' }
120             #pod
121             #pod # Good, methods from role will be maintained
122             #pod with 'Some::Role';
123             #pod
124             #pod Additionally, method detection may not work properly in L classes in
125             #pod perls earlier than 5.10.
126             #pod
127             #pod =head1 SEE ALSO
128             #pod
129             #pod =for :list
130             #pod * L
131             #pod * L
132             #pod * L
133             #pod * L
134             #pod * L
135             #pod * L
136             #pod * L
137             #pod * L
138             #pod
139             #pod =cut
140              
141             sub import {
142 17     17   21428 my ($class, %args) = @_;
143              
144             my $subcast = sub {
145 10     10   19 my $i = shift;
146 10 100       50 return $i if ref $i eq 'CODE';
147 8 100       36 return sub { $_ =~ $i } if ref $i eq 'Regexp';
  10         79  
148 5         27 return sub { $_ eq $i };
  11         79  
149 17         71 };
150              
151             my $runtest = sub {
152 27     27   51 my ($code, $method_name) = @_;
153 27         42 local $_ = $method_name;
154 27         44 return $code->();
155 17         58 };
156              
157 17 100       83 my $cleanee = exists $args{-cleanee} ? $args{-cleanee} : scalar caller;
158              
159             my @also = map $subcast->($_), (
160             exists $args{-also}
161 2         8 ? (ref $args{-also} eq 'ARRAY' ? @{ $args{-also} } : $args{-also})
162 17 100       63 : ()
    100          
163             );
164              
165             my @except = map $subcast->($_), (
166             exists $args{-except}
167 1         4 ? (ref $args{-except} eq 'ARRAY' ? @{ $args{-except} } : $args{-except})
168 17 100       47 : ()
    100          
169             );
170              
171             on_scope_end {
172 17     17   2254 my $subs = namespace::clean->get_functions($cleanee);
173 17         1188 my $method_check = _method_check($cleanee);
174              
175             my @clean = grep {
176 17         67 my $method = $_;
  85         265  
177 6         14 ! first { $runtest->($_, $method) } @except
178             and ( !$method_check->($method)
179 85 100 100     445 or first { $runtest->($_, $method) } @also)
  21         41  
180             } keys %$subs;
181              
182 17         95 namespace::clean->clean_subroutines($cleanee, @clean);
183 17         103 };
184             }
185              
186             sub _method_check {
187 17     17   39 my $package = shift;
188 17 50 33     77 if (
189             (defined &Class::MOP::class_of and my $meta = Class::MOP::class_of($package))
190             ) {
191 0         0 my %methods = map +($_ => 1), $meta->get_method_list;
192 0 0 0     0 $methods{meta} = 1
193             if $meta->isa('Moose::Meta::Role') && Moose->VERSION < 0.90;
194 0 0   0   0 return sub { $_[0] =~ /^\(/ || $methods{$_[0]} };
  0         0  
195             }
196             else {
197 17 50       244 my $does = $package->can('does') ? 'does'
    100          
198             : $package->can('DOES') ? 'DOES'
199             : undef;
200 17         4319 require Sub::Identify;
201             return sub {
202 82 100   82   226 return 1 if $_[0] =~ /^\(/;
203 9     9   7976 my $coderef = do { no strict 'refs'; \&{ $package . '::' . $_[0] } };
  9         21  
  9         1432  
  77         104  
  77         101  
  77         208  
204 77         180 my $code_stash = Sub::Identify::stash_name($coderef);
205 77 100       652 return 1 if $code_stash eq $package;
206 38 50       66 return 1 if $code_stash eq 'constant';
207             # TODO: consider if we really need this eval
208 38 100 66     76 return 1 if $does && eval { $package->$does($code_stash) };
  38         156  
209 34         569 return 0;
210 17         9044 };
211             }
212             }
213              
214             1;
215              
216             __END__