File Coverage

blib/lib/Devel/TraceDeps/Scan.pm
Criterion Covered Total %
statement 85 87 97.7
branch 19 30 63.3
condition 9 16 56.2
subroutine 15 15 100.0
pod 7 7 100.0
total 135 155 87.1


line stmt bran cond sub pod time code
1             package Devel::TraceDeps::Scan;
2             $VERSION = v0.0.3;
3              
4 2     2   45182 use warnings;
  2         6  
  2         76  
5 2     2   12 use strict;
  2         5  
  2         75  
6 2     2   12 use Carp;
  2         4  
  2         204  
7              
8              
9 2     2   2072 use Class::Accessor::Classy;
  2         10520  
  2         21  
10             with 'new';
11 2     2   450 no Class::Accessor::Classy;
  2         4  
  2         12  
12              
13             =head1 NAME
14              
15             Devel::TraceDeps::Scan - frontend and data access
16              
17             =head1 SYNOPSIS
18              
19             my $scan = Devel::TraceDeps::Scan->load($filehandle);
20              
21             =cut
22              
23             =head1 Acquisition
24              
25              
26             =head2 scan
27              
28             my $scan = Devel::TraceDeps::Scan->scan(file => $filename, %opts);
29              
30             =cut
31              
32             sub scan {
33 5     5 1 10721 my $me = shift;
34 5         100 my (%opts) = @_;
35              
36 5         12 my @cmd;
37 5 100       29 if(my $file = $opts{file}) {
    50          
38 3         9 @cmd = ($file);
39             }
40             elsif(my $code = $opts{code}) {
41 2         8 @cmd = ('-e' => $code);
42             }
43             else {
44 0         0 croak("must have something (code|file) to scan");
45             }
46              
47             # bah IPC::Cmd gives me invalid free or something
48 5 50       28955 open(my $fh, '-|', $^X, '-MDevel::TraceDeps', @cmd) or
49             croak("cannot run @cmd $!");
50              
51 5         287 my $self = $me->load($fh);
52 5         870 return($self);
53             } # end subroutine scan definition
54             ########################################################################
55              
56             =head1 Retrieval
57              
58             =head2 load
59              
60             C<$source> may be a filename, or a reference to an open filehandle or
61             string.
62              
63             my $scan = Devel::TraceDeps::Scan->load($source);
64              
65             =cut
66              
67             sub load {
68 6     6 1 109 my $package = shift;
69 6         26 my ($source) = @_;
70              
71 6 50       264 my $self = ref($package) ? $package : $package->new;
72              
73 6         171 my $fh;
74 6 50       65 if(my $r = ref($source)) {
75 6 100 66     77 if($r eq 'GLOB' or
  1   66     8  
76             $source =~ m/=GLOB\(0x[0-9a-f]+\)$/ or
77             eval {overload::Method($source, '<>')}
78             ) {
79 5         12 $fh = $source;
80             }
81             else {
82 1 50   1   65 open($fh, '<', $source) or die "open string failed $!";
  1         11  
  1         3  
  1         10  
83             }
84             }
85             else {
86 0 0       0 open($fh, '<', $source) or die "open file '$source' failed $!";
87             }
88              
89 6   50     2339 $self->{order} ||= [];
90 6   50     2893 $self->{store} ||= {};
91              
92 6         13 my $pack;
93             my $current;
94 6         47796 while(my $line = <$fh>) {
95 202         273 chomp($line);
96 202         846 my ($mod, $rest) = split(/ /, $line, 2);
97             #warn "$mod|$rest\n";
98 202 100       605 if(length($mod)) {
99 13 50       61 push(@{$self->{order}}, $mod) unless($self->{store}{$mod});
  13         55  
100 13         33 $current = '';
101 13         124 $pack = $mod;
102             }
103             else {
104 189 100       411 if($rest eq '-----') {
105 37         349 $current = Devel::TraceDeps::Scan::Item->new(by => $pack);
106 37         396 push(@{$self->{store}{$pack}}, $current);
  37         187  
107 37         132 next;
108             }
109 152         361 my ($key, $val) = split(/: /, $rest, 2);
110             # pretend every .pm was loaded with the :: form
111 152 100 66     655 $val =~ s#/+#::#g if($key eq 'req' and $val =~ s/\.pm$//);
112 152 50       651 $current or croak("out-of-sequence in $pack");
113 152         1014 $current->{$key} = $val;
114             }
115             }
116 6         185 return($self);
117             } # end subroutine load definition
118             ########################################################################
119              
120             =head1 Querying the Data
121              
122             =head2 callers
123              
124             The list of all packages which called use(), require(), or do().
125              
126             my @callers = $scan->callers;
127              
128             =cut
129              
130             sub callers {
131 9     9 1 447 my $self = shift;
132 9         188 return(@{$self->{order}});
  9         77  
133             } # end subroutine callers definition
134             ########################################################################
135              
136             =head2 items
137              
138             Return all of the use/require/do events.
139              
140             my @items = $scan->items;
141              
142             =cut
143              
144             sub items {
145 8     8 1 50 my $self = shift;
146 8         38 return(map({@{$self->{store}{$_}}} $self->callers));
  7         17  
  7         256  
147             } # end subroutine items definition
148             ########################################################################
149              
150             =head2 items_for
151              
152             Return all of the use/require/do events for a given package.
153              
154             my @items_for = $scan->items_for($caller);
155              
156             =cut
157              
158             sub items_for {
159 1     1 1 15 my $self = shift;
160 1         6 my ($pack) = @_;
161              
162 1 50       7 my $array = $self->{store}{$pack} or return();
163 1         5 return(@$array);
164             } # end subroutine items_for definition
165             ########################################################################
166              
167             =head2 required
168              
169             A unique list of use/require/do items.
170              
171             my @required = $scan->required;
172              
173             =cut
174              
175             sub required {
176 2     2 1 3784 my $self = shift;
177              
178 2         4 my @out;
179             my %seen;
180 2         7 foreach my $item ($self->items) {
181 4   33     119 my $key = $item->req || $item->did;
182 4 50       30 $seen{$key} and next;
183 4         10 $seen{$key} = 1;
184 4         10 push(@out, $item);
185             }
186 2         12 return(@out);
187             } # end subroutine required definition
188             ########################################################################
189              
190             =head2 loaded
191              
192             Everything from required() which did not fail to load.
193              
194             my @loaded = $scan->loaded;
195              
196             =cut
197              
198             sub loaded {
199 1     1 1 2 my $self = shift;
200 1         3 return(grep({not $_->fail} $self->required));
  2         61  
201             } # end subroutine loaded definition
202             ########################################################################
203              
204              
205             =head1 Item objects
206              
207             C objects are returned by several of the
208             above methods. The have the following attributes:
209              
210             =over
211              
212             =item by
213              
214             The package which required this item (caller).
215              
216             =item trace
217              
218             A string of sequence numbers indicating the tree of this call.
219              
220             =item file
221              
222             Filename from caller().
223              
224             =item line
225              
226             Line number from caller().
227              
228             =item req
229              
230             The require()d module filename (as found in C -- e.g. 'use
231             Foo::Bar' would appear as 'Foo/Bar.pm'.)
232              
233             This will typically be a relative path (within @INC), but might be
234             absolute or ever relative to './' (depending on how use/require/do was
235             called.
236              
237             =item ver
238              
239             Item is a requirement for a minimum perl version. If 'req' is empty,
240             look here.
241              
242             =item did
243              
244             The argument to do("filename").
245              
246             =item fail
247              
248             True if the require() failed. This probably means that the file does
249             not exist (e.g. C or C)
250              
251             =item err
252              
253             Any $@ or $! found.
254              
255             =back
256              
257             =cut
258              
259             {
260             package Devel::TraceDeps::Scan::Item;
261 2     2   2692 use Class::Accessor::Classy;
  2         4  
  2         9  
262             with 'new';
263             ro qw(
264             by
265             trace
266             req
267             ver
268             did
269             file
270             line
271             fail
272             err
273             );
274 2     2   400 no Class::Accessor::Classy;
  2         8  
  2         7  
275             }
276              
277             =head1 AUTHOR
278              
279             Eric Wilhelm @
280              
281             http://scratchcomputing.com/
282              
283             =head1 BUGS
284              
285             If you found this module on CPAN, please report any bugs or feature
286             requests through the web interface at L. I will be
287             notified, and then you'll automatically be notified of progress on your
288             bug as I make changes.
289              
290             If you pulled this development version from my /svn/, please contact me
291             directly.
292              
293             =head1 COPYRIGHT
294              
295             Copyright (C) 2008 Eric L. Wilhelm, All Rights Reserved.
296              
297             =head1 NO WARRANTY
298              
299             Absolutely, positively NO WARRANTY, neither express or implied, is
300             offered with this software. You use this software at your own risk. In
301             case of loss, no person or entity owes you anything whatsoever. You
302             have been warned.
303              
304             =head1 LICENSE
305              
306             This program is free software; you can redistribute it and/or modify it
307             under the same terms as Perl itself.
308              
309             =cut
310              
311             # vi:ts=2:sw=2:et:sta
312             1;