File Coverage

blib/lib/namespace/autoclean.pm
Criterion Covered Total %
statement 61 76 80.2
branch 28 44 63.6
condition 7 15 46.6
subroutine 13 14 92.8
pod n/a
total 109 149 73.1


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