File Coverage

blib/lib/namespace/functions.pm
Criterion Covered Total %
statement 41 41 100.0
branch 4 8 50.0
condition 2 4 50.0
subroutine 6 6 100.0
pod n/a
total 53 59 89.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -c
2              
3             package namespace::functions;
4              
5             =head1 NAME
6              
7             namespace::functions - Keep package's namespace clean
8              
9             =head1 SYNOPSIS
10              
11             package My::Class;
12              
13             # import some function
14             use Scalar::Util 'looks_like_number';
15              
16             # collect the names of all previously created functions
17             use namespace::functions;
18              
19             # our function uses imported function
20             sub is_num {
21             my ($self, $val) = @_;
22             return looks_like_number("$val");
23             }
24              
25             # delete all previously collected functions
26             no namespace::functions;
27              
28             # our package doesn't provide imported function anymore!
29              
30             =head1 DESCRIPTION
31              
32             This pragma allows to collect all functions existing in package's
33             namespace and finally delete them.
34              
35             The problem is that any function which is imported to your
36             package, stays a part of public API of this package. I.e.:
37              
38             package My::PollutedClass;
39             use Scalar::Util 'looks_like_number';
40              
41             sub is_num {
42             my ($val) = @_;
43             return looks_like_number("$val");
44             }
45              
46             package main;
47             print My::PollutedClass->can('looks_like_number'); # true
48              
49             Deleting imported function from package's stash is a solution, because the
50             function will be not available at run-time:
51              
52             delete {\%My::PoorSolutionClass::}->{looks_like_number};
53              
54             The C<namespace::functions> collects the function names and finally deletes
55             them from package's namespace.
56              
57             =for readme stop
58              
59             =cut
60              
61              
62 2     2   8966 use 5.006;
  2         8  
  2         77  
63              
64 2     2   12 use strict;
  2         3  
  2         71  
65 2     2   23 use warnings;
  2         4  
  2         157  
66              
67             our $VERSION = '0.0101';
68              
69              
70 2     2   12 use Symbol::Util ();
  2         4  
  2         10294  
71              
72              
73             # Store collected functions in this hash
74             my %Functions = ();
75              
76              
77             # Collect functions
78             sub import {
79 4     4   8663 my (undef, @args) = @_;
80 4         13 my $caller = caller;
81              
82 4         8 my %except = do {
83 4         7 my @except;
84 4         23 while (my $arg = shift @args) {
85 1 50       6 if ($arg eq '-except') {
86 1         2 my $what = shift @args;
87 1 50 50     13 push @except, (ref $what||'') eq 'ARRAY' ? @$what : $what;
88             };
89             };
90 4         10 map { $_ => 1 } @except;
  2         16  
91             };
92              
93 9         84 my @functions = grep { defined Symbol::Util::fetch_glob("${caller}::$_", 'CODE') }
  11         116  
94 4         21 grep { not $except{$_} }
95 4         6 keys %{ Symbol::Util::stash($caller) };
96              
97 4         51 $Functions{$caller} = \@functions;
98              
99 4         356 return;
100             };
101              
102              
103             # Delete functions
104             sub unimport {
105 3     3   25 my (undef, @args) = @_;
106 3         8 my $caller = caller;
107              
108 3         5 my @also;
109 3         12 while (my $arg = shift @args) {
110 1 50       5 if ($arg eq '-also') {
111 1         2 my $what = shift @args;
112 1 50 50     15 push @also, (ref $what||'') eq 'ARRAY' ? @$what : $what;
113             };
114             };
115              
116 4         31 my @functions = grep { defined Symbol::Util::fetch_glob("${caller}::$_", 'CODE') }
  3         7  
117 3         6 @{ $Functions{$caller} }, @also;
118              
119 3         37 foreach my $function (@functions) {
120 4         109 Symbol::Util::delete_sub("${caller}::$function");
121             };
122              
123 3         357 return;
124             };
125              
126              
127             1;
128              
129              
130             __END__
131              
132             =head1 IMPORTS
133              
134             =over
135              
136             =item use namespace::functions;
137              
138             Collects all functions from our namespace.
139              
140             =item use namespace::functions -except => 'func';
141              
142             =item use namespace::functions -except => ['func1', 'func2'];
143              
144             Collects all functions from our namespace without listed functions.
145              
146             =item no namespace::functions;
147              
148             Deletes all previously collected functions from our namespace.
149              
150             =item use namespace::functions -also => 'func';
151              
152             =item use namespace::functions -also => ['func1', 'func2'];
153              
154             Deletes all previously collected functions and also additional functions from
155             our namespace.
156              
157             =back
158              
159             =head1 OVERVIEW
160              
161             This pragma needs to be placed in the right order: after last C<use> which
162             imports some unwanted functions and before first C<sub>.
163              
164             package My::Example;
165              
166             use Carp 'confess';
167             use Scalar::Util 'blessed'
168              
169             use namespace::functions;
170              
171             sub some_method {
172             my ($self) = @_;
173             confess("Call as a method") if not blessed($self);
174             # ...
175             };
176              
177             no namespace::functions;
178              
179             You can check if your package is clean with L<Class::Inspector> module and
180             its methods: C<functions> and C<methods>.
181              
182             use My::Example;
183             use Class::Inspector;
184             use YAML;
185             print Dump ( {
186             functions => [Class::Inspector->functions("My::Example")],
187             methods => [Class::Inspector->methods("My::Example")],
188             } );
189              
190             =head2 Moose
191              
192             L<Moose> keywords can be unexported with C<no Moose> statement. Even that,
193             L<Moose> imports following functions: C<blessed>, C<confess>, C<meta>. The
194             C<meta> method is required by Moose framework and should be unchanged. The
195             others can be deleted safely.
196              
197             package My::MooseClass;
198              
199             use Moose;
200              
201             use namespace::functions -except => 'meta';
202              
203             sub my_method {
204             my ($self, $arg) = @_;
205             return blessed $self;
206             };
207              
208             no namespace::functions;
209              
210             # The My::MooseClass now provides "my_method" and "meta" only.
211              
212             =head1 SEE ALSO
213              
214             This module is inspired by L<namespace::clean> module but it doesn't require
215             compiled XS modules. It also doesn't work lexically so C<unimport> method
216             have to be called explicitly.
217              
218             See also: L<namespace::clean>, L<Class::Inspector>.
219              
220             =head1 BUGS
221              
222             If you find the bug or want to implement new features, please report it at
223             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=namespace-functions>
224              
225             =for readme continue
226              
227             =head1 AUTHOR
228              
229             Piotr Roszatycki <dexter@cpan.org>
230              
231             =head1 COPYRIGHT
232              
233             Copyright (C) 2009, 2011 by Piotr Roszatycki <dexter@cpan.org>.
234              
235             This program is free software; you can redistribute it and/or modify it
236             under the same terms as Perl itself.
237              
238             See L<http://www.perl.com/perl/misc/Artistic.html>
239