File Coverage

blib/lib/namespace/autoclean.pm
Criterion Covered Total %
statement 61 66 92.4
branch 27 34 79.4
condition 6 12 50.0
subroutine 12 13 92.3
pod n/a
total 106 125 84.8


line stmt bran cond sub pod time code
1 10     10   248921 use strict;
  10         25  
  10         290  
2 10     10   50 use warnings;
  10         18  
  10         640  
3              
4             package namespace::autoclean; # git description: 0.27-4-g47c7088
5             # ABSTRACT: Keep imports out of your namespace
6             # KEYWORDS: namespaces clean dirty imports exports subroutines methods development
7              
8             our $VERSION = '0.28';
9              
10 10     10   7705 use B::Hooks::EndOfScope 0.12;
  10         119354  
  10         59  
11 10     10   778 use List::Util qw( first );
  10         22  
  10         947  
12 10     10   7454 use namespace::clean 0.20;
  10         42707  
  10         61  
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 18     18   20599 my ($class, %args) = @_;
143              
144             my $subcast = sub {
145 10     10   25 my $i = shift;
146 10 100       55 return $i if ref $i eq 'CODE';
147 8 100       34 return sub { $_ =~ $i } if ref $i eq 'Regexp';
  10         79  
148 5         27 return sub { $_ eq $i };
  11         86  
149 18         77 };
150              
151             my $runtest = sub {
152 27     27   45 my ($code, $method_name) = @_;
153 27         42 local $_ = $method_name;
154 27         48 return $code->();
155 18         63 };
156              
157 18 100       87 my $cleanee = exists $args{-cleanee} ? $args{-cleanee} : scalar caller;
158              
159 7         16 my @also = map { $subcast->($_) } (
160             exists $args{-also}
161 2         5 ? (ref $args{-also} eq 'ARRAY' ? @{ $args{-also} } : $args{-also})
162 18 100       85 : ()
    100          
163             );
164              
165 3         16 my @except = map { $subcast->($_) } (
166             exists $args{-except}
167 1         3 ? (ref $args{-except} eq 'ARRAY' ? @{ $args{-except} } : $args{-except})
168 18 100       66 : ()
    100          
169             );
170              
171             on_scope_end {
172 18     18   1942 my $subs = namespace::clean->get_functions($cleanee);
173 18         830 my $method_check = _method_check($cleanee);
174              
175             my @clean = grep {
176 18         64 my $method = $_;
  87         256  
177 6         13 ! first { $runtest->($_, $method) } @except
178             and ( !$method_check->($method)
179 87 100 100     485 or first { $runtest->($_, $method) } @also)
  21         46  
180             } keys %$subs;
181              
182 18         120 namespace::clean->clean_subroutines($cleanee, @clean);
183 18         114 };
184             }
185              
186             sub _method_check {
187 18     18   33 my $package = shift;
188 18 50 33     92 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;
  0         0  
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 18 50       248 my $does = $package->can('does') ? 'does'
    100          
198             : $package->can('DOES') ? 'DOES'
199             : undef;
200 18         7173 require Sub::Identify;
201             return sub {
202 84 100   84   1422 return 1 if $_[0] =~ /^\(/;
203 10     10   8192 my $coderef = do { no strict 'refs'; \&{ $package . '::' . $_[0] } };
  10         21  
  10         1471  
  79         89  
  79         90  
  79         246  
204 79         212 my $code_stash = Sub::Identify::stash_name($coderef);
205 79 100       762 return 1 if $code_stash eq $package;
206 39 50       74 return 1 if $code_stash eq 'constant';
207             # TODO: consider if we really need this eval
208 39 100 66     99 return 1 if $does && eval { $package->$does($code_stash) };
  39         201  
209 35         623 return 0;
210 18         10432 };
211             }
212             }
213              
214             1;
215              
216             __END__