File Coverage

blib/lib/Devel/Leak/Module.pm
Criterion Covered Total %
statement 57 72 79.1
branch 9 24 37.5
condition 2 3 66.6
subroutine 16 18 88.8
pod 0 9 0.0
total 84 126 66.6


line stmt bran cond sub pod time code
1             package Devel::Leak::Module;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Devel::Leak::Module - Track loaded modules and namespaces
8              
9             =head1 SYNOPSIS
10              
11             # 1. Load all modules we believe are needed
12             require My::Everything;
13            
14             # 2. Set a checkpoint for all loaded modules/packages/namespaces
15             Devel::Leak::Module::checkpoint();
16            
17             # 3. Run code that should not result in loading any new code
18             My::foo();
19            
20             # 4. Confirm that no new code was loaded during
21             Devel::Leak::Module::print_new();
22              
23             =head1 DESCRIPTION
24              
25             B is a simple little convenience module for tracking
26             module, package and namespace creation.
27              
28             The synopsis code above describes pretty much the main way that it works.
29              
30             =head1 FUNCTIONS
31              
32             =cut
33              
34 2     2   25641 use 5.005;
  2         8  
  2         90  
35 2     2   11 use strict;
  2         4  
  2         78  
36 2     2   27 no strict 'refs';
  2         3  
  2         61  
37              
38 2     2   10 use vars qw{$VERSION};
  2         4  
  2         218  
39             BEGIN {
40 2     2   4 $VERSION = '0.02';
41              
42             # Force sort::hints to be populated early, to avoid a case where
43             # calling all_modules creates a new namespace.
44 2         4 my @foo = qw{ b c a };
45 2         14 my @bar = sort @foo;
46              
47             # If Scalar::Util and List::Util are around, load them.
48             # This prevents a problem when tests are run in the debugger.
49             # If they AREN'T available, we don't care
50 2         3 local $@;
51 2         160 eval "require Scalar::Util; require List::Util;";
52             }
53              
54              
55              
56              
57              
58             #####################################################################
59             # Main Functions
60              
61             my %NAMESPACES = ();
62             my %PACKAGES = ();
63             my %MODULES = ();
64              
65             sub checkpoint {
66 1     1 0 3360 %NAMESPACES = map { $_ => 1 } all_namespaces();
  182         351  
67 1         27 %PACKAGES = map { $_ => 1 } all_packages();
  126         253  
68 1         135 %MODULES = %INC;
69 1         15 return 1;
70             }
71              
72             # Print a summary of newly created things
73             sub print_new {
74 0 0   0 0 0 my %parts = map { $_ => 1 } (@_ ? @_ : qw{ namespace package module });
  0         0  
75              
76 0 0       0 if ( $parts{module} ) {
77 0         0 foreach my $module ( new_modules() ) {
78 0         0 print "Module: $module\n";
79             }
80             }
81 0 0       0 if ( $parts{package} ) {
82 0         0 foreach my $package ( new_packages() ) {
83 0         0 print "Package: $package\n";
84             }
85             }
86 0 0       0 if ( $parts{namespace} ) {
87 0         0 foreach my $namespace ( new_namespaces() ) {
88 0         0 print "Namespace: $namespace\n";
89             }
90             }
91              
92             }
93              
94             # Boolean true/false for if there are any new anything
95             sub any_new {
96 0 0   0 0 0 return 1 if new_namespaces();
97 0 0       0 return 1 if new_packages();
98 0 0       0 return 1 if new_modules();
99 0         0 return '';
100             }
101              
102             sub new_modules {
103 3     3 0 59 grep { ! $MODULES{$_} } all_modules();
  213         311  
104             }
105              
106             sub new_packages {
107 3     3 0 52 grep { ! $PACKAGES{$_} } all_packages();
  379         514  
108             }
109              
110             sub new_namespaces {
111 3     3 0 2578 grep { ! $NAMESPACES{$_} } all_namespaces();
  549         766  
112             }
113              
114              
115              
116              
117              
118             #####################################################################
119             # Capture Functions
120              
121             # Get the list of all modules
122             sub all_modules {
123 4     4 0 119 sort grep { $_ ne 'dumpvar.pl' } keys %INC;
  284         442  
124             }
125              
126             # Start with all the namespaces,
127             # limited to the ones that look like classes.
128             # Then check each namespace actually contains something.
129             sub all_packages {
130 5     5 0 22 grep { _OCCUPIED($_) } grep { _CLASS($_) } all_namespaces();
  908         1283  
  913         1258  
131             }
132              
133             sub all_namespaces {
134 10     10 0 21 my @names = ();
135 10         46 my @stack = grep { $_ ne 'main' } _names('main');
  592         800  
136 10         186 while ( @stack ) {
137 1826         2240 my $c = shift @stack;
138 1826         2253 push @names, $c;
139 1826         2711 unshift @stack, _namespaces($c);
140             }
141 10         355 return @names;
142             }
143              
144              
145              
146              
147              
148             #####################################################################
149             # Support Functions
150              
151             sub _names {
152 1838     1838   1688 return grep { s/::$// } sort keys %{$_[0] . '::'};
  34882         56944  
  1838         24352  
153             }
154              
155             sub _namespaces {
156 1827     1827   2707 return map { $_[0] . '::' . $_ } _names($_[0]);
  1245         3927  
157             }
158              
159              
160              
161              
162              
163             #####################################################################
164             # Embedded Functions
165              
166             # Params::Util::_CLASS
167             sub _CLASS ($) {
168 913 100 66 913   5552 (defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s) ? $_[0] : undef;
169             }
170              
171             # Class::Autouse::_namespace_occupied
172             sub _OCCUPIED ($) {
173             # Handle the most likely case
174 908 50   908   1494 my $class = shift or return undef;
175 908 100       781 return 1 if @{"${class}::ISA"};
  908         3392  
176              
177             # Get the list of glob names, ignoring namespaces
178 577         518 foreach ( keys %{"${class}::"} ) {
  577         2418  
179 1209 100       2200 next if substr($_, -2) eq '::';
180              
181             # Only check for methods, since that's all that's reliable
182 972 100       796 return 1 if defined *{"${class}::$_"}{CODE};
  972         3518  
183             }
184              
185 277         538 '';
186             }
187              
188             1;
189              
190             =pod
191              
192             =head1 SUPPORT
193              
194             Bugs should be always be reported via the CPAN bug tracker at
195              
196             L
197              
198             For other issues, or commercial enhancement or support, contact the author.
199              
200             =head1 AUTHORS
201              
202             Adam Kennedy Eadamk@cpan.orgE
203              
204             =head1 SEE ALSO
205              
206             L, L
207              
208             =head1 COPYRIGHT
209              
210             Copyright 2007 - 2012 Adam Kennedy.
211              
212             This program is free software; you can redistribute
213             it and/or modify it under the same terms as Perl itself.
214              
215             The full text of the license can be found in the
216             LICENSE file included with this module.
217              
218             =cut