File Coverage

blib/lib/Perl/Metrics/Plugin.pm
Criterion Covered Total %
statement 66 84 78.5
branch 14 26 53.8
condition 4 15 26.6
subroutine 13 13 100.0
pod 5 5 100.0
total 102 143 71.3


line stmt bran cond sub pod time code
1             package Perl::Metrics::Plugin;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Perl::Metrics::Plugin - Base class for Perl::Metrics Plugins
8              
9             =head1 SYNOPSIS
10              
11             # Implement a simple metrics package which counts up the
12             # use of each type of magic variable.
13             package Perl::Metrics::Plugin::Magic;
14            
15             use base 'Perl::Metrics::Plugin';
16            
17             # Creates the metric 'all_magic'.
18             # The total number of magic variables.
19             sub metric_all_magic {
20             my ($self, $Document) = @_;
21             return scalar grep { $_->isa('PPI::Token::Magic') }
22             $Document->tokens;
23             }
24            
25             # The number of $_ "scalar_it" magic vars
26             sub metric_scalar_it {
27             my ($self, $Document) = @_;
28             return scalar grep { $_->content eq '$_' }
29             grep { $_->isa('PPI::Token::Magic') }
30             $Document->tokens;
31             }
32            
33             # ... and so on, and so forth.
34            
35             1;
36              
37             =head1 DESCRIPTION
38              
39             The L system does not in and of itself generate any actual
40             metrics data, it merely acts as a processing and storage engine.
41              
42             The generation of the actual metrics data is done via metrics packages,
43             which as implemented as C sub-classes.
44              
45             =head2 Implementing Your Own Metrics Package
46              
47             Implementing a metrics package is pretty easy.
48              
49             First, create a Perl::Metrics::Plugin::Something package, inheriting
50             from C.
51              
52             The create a subroutine for each metric, named metric_$name.
53              
54             For each subroutine, you will be passed the plugin object itself, and the
55             L object to generate the metric for.
56              
57             Return the metric value from the subroutine. And add as many metric_
58             methods as you wish. Methods not matching the pattern /^metric_(.+)$/
59             will be ignored, and you may use them for whatever support methods you
60             wish.
61              
62             =head1 METHODS
63              
64             =cut
65              
66 6     6   35 use strict;
  6         12  
  6         233  
67 6     6   33 use Carp ();
  6         16  
  6         88  
68 6     6   5831 use Class::Inspector ();
  6         24844  
  6         157  
69 6         483 use Params::Util '_IDENTIFIER',
70 6     6   822 '_INSTANCE';
  6         5197  
71              
72 6     6   36 use vars qw{$VERSION};
  6         14  
  6         285  
73             BEGIN {
74 6     6   5387 $VERSION = '0.09';
75             }
76              
77              
78              
79              
80              
81             #####################################################################
82             # Constructor
83              
84             =pod
85              
86             =head2 new
87              
88             The C constructor is quite trivial at this point, and is provided
89             merely as a convenience. You don't really need to think about this.
90              
91             =cut
92              
93             sub new {
94 2 50   2 1 682 my $class = ref $_[0] ? ref shift : shift;
95 2         53 my $self = bless {}, $class;
96 2         16 $self;
97             }
98              
99             =pod
100              
101             =head2 class
102              
103             A convenience method to get the class for the plugin object,
104             to avoid having to use ref directly (and making the intent of
105             any code a little clearer).
106              
107             =cut
108              
109 14 50   14 1 717 sub class { ref $_[0] || $_[0] }
110              
111              
112              
113              
114              
115             #####################################################################
116             # Perl::Metrics::Plugin API
117              
118             =pod
119              
120             =head2 metrics
121              
122             The C method provides the list of metrics that are provided
123             by the metrics package. By default, this list is automatically
124             generated for you scanning for C methods that reside
125             in the immediate package namespace.
126              
127             Returns a reference to a C where the keys are the metric names,
128             and the values are the "version" of the metric (for versioned metrics),
129             or C if the metric is not versioned.
130              
131             =cut
132              
133             sub metrics {
134 7     7 1 1420 my $self = shift;
135 7 100       83 $self->{_metrics} or
136             $self->{_metrics} = $self->_metrics;
137             }
138              
139             sub _metrics {
140 2     2   49 my $self = shift;
141 2         9 my $class = ref $self;
142 2 50       22 my $funcs = Class::Inspector->functions($class)
143             or Carp::croak("Failed to get method list for '$class'");
144 4         32 my %metrics = map { $_ => undef }
  4         159  
145 4         18 grep { _IDENTIFIER($_) }
146 2         321 grep { s/^metric_//s }
147             @$funcs;
148 2         22 \%metrics;
149             }
150              
151             sub _metric {
152 8     8   26 my ($self, $Document, $name) = @_;
153 8         110 my $method = "metric_$name";
154 8 50       99 $self->can($method) or Carp::croak("Bad metric name '$name'");
155 8         50 scalar($self->$method($Document));
156             }
157              
158             =pod
159              
160             =head2 process_index
161              
162             The C method will cause the metrics plugin to scan every
163             single file entry in the database, and run any an all metrics required to
164             bring to the database up to complete coverage for that plugin.
165              
166             This process may take some time for large indexes.
167              
168             =cut
169              
170             sub process_index {
171 2     2 1 475 my $self = shift;
172 2         37 my @files = Perl::Metrics::File->retrieve_all;
173 2         4670 @files = sort { $a->path cmp $b->path } @files;
  6         525  
174 2         863 while ( my $file = shift @files ) {
175 6         606 Perl::Metrics->_trace("Processing $file... ");
176 6 50       51 if ( $self->process_file($file) ) {
177 6         1292 Perl::Metrics->_trace("done.\n");
178             } else {
179 0         0 Perl::Metrics->_trace("error.\n");
180             }
181             }
182 2         54 1;
183             }
184              
185             =pod
186              
187             =head2 process_file $File
188              
189             The C method takes as argument a single
190             L and run any and all metrics required
191             to bring that file up to complete coverage for the plugin.
192              
193             =cut
194              
195             sub process_file {
196 6     6 1 29 my $self = shift;
197 6 50       102 my $file = _INSTANCE(shift, 'Perl::Metrics::File')
198             or Carp::croak("Did not pass a Perl::Metrics::File to process_file");
199              
200             # Has the file been removed since the last run
201 6 50       453 unless ( -f $file->path ) {
202             # Delete the file entry
203 0         0 $file->delete;
204 0         0 return 1;
205             }
206              
207             # Get the metric list for the plugin, and the
208             # database Metric data for this file.
209 6         667 my %metrics = %{$self->metrics}; # Copy so we can destroy
  6         41  
210 6         37 my @objects = $file->metrics(
211             'package' => $self->class,
212             );
213              
214             # Deal with the existing metrics objects that do not
215             # require the Document in order to be processed.
216 6         16510 my @todo = ();
217 6         28 foreach my $object ( @objects ) {
218 4         23 my $name = $object->name;
219              
220             # Remove any redundant metrics
221 4 50       478 if ( ! exists $metrics{$name} ) {
222 0         0 $object->delete;
223 0         0 delete $metrics{$name};
224 0         0 next;
225             }
226              
227             # If the metric is unversioned, we don't need to rerun
228 4 50 33     33 if ( ! defined $metrics{$name} and
229             ! defined $object->version
230             ) {
231 4         519 delete $metrics{$name};
232 4         14 next;
233             }
234              
235             # Must be versioned. If plugin equals stored version,
236             # then no need to rerun the metric.
237 0 0 0     0 if ( defined $metrics{$name} and
      0        
238             defined $object->version and
239             $object->version == $metrics{$name}
240             ) {
241 0         0 delete $metrics{$name};
242 0         0 next;
243             }
244              
245             # To do in the next pass
246 0         0 push @todo, $object;
247             }
248              
249             # Shortcut return now if nothing left to do
250 6 100 66     61 unless ( @todo or keys %metrics ) {
251 2         11 return 1;
252             }
253              
254             # Any further metrics will need the document
255 4         11 my $Document = eval { $file->Document };
  4         27  
256 4 50 33     42 if ( $@ or ! $Document ) {
257             # The document has gone unparsable. If this
258             # is due to a PPI upgrade breaking something, we
259             # need to flush out any existing metrics for the
260             # document, then skip on to the next file
261 0         0 $file->metrics->delete_all;
262 0         0 return 0;
263             }
264              
265             # Now we have the document, update the remaining metrics
266 4         14 foreach my $object ( @todo ) {
267 0         0 my $name = $object->name;
268              
269             # Versions differ, or it has changed from defined to
270             # not, or back the front.
271 0         0 $object->version($metrics{$name});
272 0         0 my $value = $self->_metric($Document, $name);
273 0         0 $object->value($value);
274 0         0 $object->update;
275 0         0 delete $metrics{$name};
276             }
277              
278             # With the existing ones out the way, generate the new ones
279 4         30 foreach my $name ( sort keys %metrics ) {
280 8         65293 my $value = $self->_metric($Document, $name);
281 8         431 Perl::Metrics::Metric->insert( {
282             hex_id => $file->hex_id,
283             package => $self->class,
284             name => $name,
285             version => $metrics{$name},
286             value => $value,
287             } );
288             }
289              
290 4         87290 1;
291             }
292              
293             1;
294              
295             =pod
296              
297             =head1 SUPPORT
298              
299             Bugs should be reported via the CPAN bug tracker at
300              
301             L
302              
303             For other issues, contact the author.
304              
305             =head1 AUTHOR
306              
307             Adam Kennedy Eadamk@cpan.orgE
308              
309             =head1 SEE ALSO
310              
311             L, L
312              
313             =head1 COPYRIGHT
314              
315             Copyright 2005 - 2008 Adam Kennedy.
316              
317             This program is free software; you can redistribute
318             it and/or modify it under the same terms as Perl itself.
319              
320             The full text of the license can be found in the
321             LICENSE file included with this module.
322              
323             =cut