File Coverage

blib/lib/Data/Report/Base.pm
Criterion Covered Total %
statement 152 190 80.0
branch 49 84 58.3
condition 17 33 51.5
subroutine 33 42 78.5
pod 0 20 0.0
total 251 369 68.0


line stmt bran cond sub pod time code
1             # Data::Report::Base.pm -- Base class for reporters
2             # RCS Info : $Id: Base.pm,v 1.10 2006/06/08 13:11:05 jv Exp $
3             # Author : Johan Vromans
4             # Created On : Wed Dec 28 13:18:40 2005
5             # Last Modified By: Johan Vromans
6             # Last Modified On: Tue Jun 6 22:13:56 2006
7             # Update Count : 317
8             # Status : Unknown, Use with caution!
9              
10             package Data::Report::Base;
11              
12             =head1 NAME
13              
14             Data::Report::Base - Base class for reporter plugins
15              
16             =head1 SYNOPSIS
17              
18             This module implements that basic functionality common to all reporters.
19              
20             Its documentation still has to be written.
21              
22             =cut
23              
24 23     23   5013 use strict;
  23         43  
  23         732  
25 23     23   116 use warnings;
  23         42  
  23         566  
26 23     23   116 use Carp;
  23         34  
  23         75788  
27              
28             my $style_pat = qr/^[a-zA-Z]\w*$/;
29              
30             ################ User API ################
31              
32             sub new {
33 22     22 0 61 my ($class, $args) = @_;
34 22   33     221 $class = ref($class) || $class;
35              
36 22         77 my $type = delete($args->{type});
37 22   50     131 my $style = delete($args->{style}) || "default";
38 22         374 my $self = bless { _base_type => lc($type),
39             _base_fields => [],
40             _base_fdata => {},
41             _base_style => $style,
42             }, $class;
43              
44 22 50       216 $self->_checkname($style)
45             or croak("Invalid style name: \"$style\"");
46 22         98 foreach my $arg ( keys(%$args) ) {
47 15         40 my $val = delete($args->{$arg});
48 15 50       217 if ( my $c = $self->can("set_$arg") ) {
49 15         77 $c->($self, $val);
50             }
51             else {
52 0         0 croak("Unhandled attribute: \"$arg\"");
53             }
54             }
55              
56             # Return object.
57 22         129 $self;
58             }
59              
60             sub start {
61 25     25 0 54 my $self = shift;
62 25         96 $self->_argcheck(0);
63 25 50       144 croak("No layout specified") unless $self->{_base_fdata};
64 25 50       103 croak("Reporter already started") if $self->{_base_started};
65              
66 25         89 $self->{_base_needpre} = 1;
67 25         61 $self->{_base_needhdr} = 1;
68 25         52 $self->{_base_needskip} = 0;
69              
70 25 50       168 $self->set_output(\*STDOUT) unless $self->{_base_out};
71 25 50       120 $self->set_style("default") unless $self->{_base_style};
72 25 50       801 $self->set_topheading($self->can("_top_heading"))
73             unless $self->{_base_topheading};
74 25 100       328 $self->set_heading($self->can("_std_heading"))
75             unless $self->{_base_heading};
76 25 100       245 $self->set_stylist($self->can("_std_stylist"))
77             unless $self->{_base_stylist};
78 25   50 0   100 $self->{_base_close} ||= sub {};
  0         0  
79              
80 25         51 $self->{_base_started} = 1;
81 25         87 $self->{_base_used} = 0;
82             }
83              
84             sub add {
85 61     61 0 92 my ($self, $data) = @_;
86 61 50       180 croak("Reporter not started") unless $self->{_base_started};
87              
88 61         269 while ( my($k,$v) = each(%$data) ) {
89 233 50       487 next if $k =~ /^_/;
90 233 50       1057 croak("Invalid field: \"$k\"\n")
91             unless defined $self->{_base_fdata}->{$k};
92             }
93              
94             }
95              
96             sub finish {
97 25     25 0 53 my $self = shift;
98 25         74 $self->_argcheck(0);
99 25 50       127 croak("Reporter not started") unless $self->{_base_started};
100 25         77 $self->{_base_started} = 0;
101             }
102              
103             sub close {
104 20     20 0 182 my $self = shift;
105 20         104 $self->_argcheck(0);
106 20 50       72 croak("Reporter is not finished") if $self->{_base_started};
107 20         76 $self->{_base_close}->();
108             }
109              
110             ################ Attributes ################
111              
112             #### Type
113              
114 2     2 0 16 sub get_type { shift->{_base_type} }
115              
116             #### Style
117              
118             sub set_style {
119 0     0 0 0 my ($self, $style) = @_;
120 0         0 $self->_argcheck(1);
121 0 0       0 $self->_checkname($style)
122             or croak("Invalid style name: \"$style\"");
123 0         0 $self->{_base_style} = $style;
124             }
125              
126             sub get_style {
127 3     3 0 58 my $self = shift;
128 3         8 $self->_argcheck(0);
129 3         19 $self->{_base_style};
130             }
131              
132             #### Layout
133              
134             sub set_layout {
135 22     22 0 6627 my ($self, $layout) = @_;
136 22         91 $self->_argcheck(1);
137 22         53 foreach my $col ( @$layout ) {
138 93 50       321 if ( $col->{name} ) {
139 93 50       221 $self->_checkname($col->{name})
140             or croak("Invalid column name: \"$col->{name}\"");
141 93   33     1198 my $a = { name => $col->{name},
      33        
      100        
      33        
142             title => $col->{title} || ucfirst(lc(_T($a->{name}))),
143             width => $col->{width} || length($a->{title}),
144             align => $col->{align} || "<",
145             style => $col->{style} || $col->{name},
146             truncate => $col->{truncate},
147             };
148 93 50       316 $self->_checkname($a->{style})
149             or croak("Invalid column style: \"$a->{style}\"");
150 93         426 $self->{_base_fdata}->{$a->{name}} = $a;
151 93         113 push(@{$self->{_base_fields}}, $a);
  93         255  
152             }
153             else {
154 0         0 croak("Missing column name in layout\n");
155             }
156             }
157              
158             # Return object.
159 22         74 $self;
160             }
161              
162             #### Fields (order of)
163              
164             sub set_fields {
165 2     2 0 18 my ($self, $f) = @_;
166 2         25 $self->_argcheck(1);
167              
168 2         3 my @nf; # new order of fields
169              
170 2         5 foreach my $fld ( @$f ) {
171 8         13 my $a = $self->{_base_fdata}->{$fld};
172 8 50       19 croak("Unknown field: \"$fld\"\n")
173             unless defined($a);
174 8         13 push(@nf, $a);
175             }
176 2         5 $self->{_base_fields} = \@nf;
177              
178             # PBP: Return nothing sensible.
179 2         6 return;
180             }
181              
182             sub get_fields {
183 0     0 0 0 my $self = shift;
184 0         0 $self->_argcheck(0);
185 0         0 [ map { $_->{name} } @{$self->{_base_fields}} ];
  0         0  
  0         0  
186             }
187              
188             #### Width (set one or more)
189              
190             sub set_width {
191 1     1 0 9 my ($self, $w) = @_;
192              
193 1         5 while ( my($fld,$width) = each(%$w) ) {
194 2 50       5 croak("Unknown field: \"$fld\"\n")
195             unless defined($self->{_base_fdata}->{$fld});
196 2         3 my $ow = $self->{_base_fdata}->{$fld}->{width};
197 2 50       12 if ( $width =~ /^\+(\d+)$/ ) {
    100          
    50          
    50          
198 0         0 $ow += $1;
199             }
200             elsif ( $width =~ /^-(\d+)$/ ) {
201 1         3 $ow -= $1;
202             }
203             elsif ( $width =~ /^(\d+)\%$/ ) {
204 0         0 $ow *= $1;
205 0         0 $ow = int($ow/100);
206             }
207             elsif ( $width =~ /^\d+$/ ) {
208 1         2 $ow = $width;
209             }
210             else {
211 0         0 croak("Invalid width specification \"$width\" for field \"$fld\"\n");
212             }
213 2         8 $self->{_base_fdata}->{$fld}->{width} = $ow;
214             }
215              
216             # PBP: Return nothing sensible.
217 1         2 return;
218             }
219              
220             #### Width (get all)
221              
222             sub get_widths {
223 0     0 0 0 my $self = shift;
224 0         0 $self->_argcheck(0);
225 0         0 { map { $_ => $self->{_base_fdata}->{$_}->{width} } $self->get_fields }
  0         0  
  0         0  
226             }
227              
228             #### Output
229              
230             sub set_output {
231 25     25 0 20091 my ($self, $out) = @_;
232 25         91 $self->_argcheck(1);
233 25     19   144 $self->{_base_close} = sub {};
  19         44  
234 25 100       122 if ( ref($out) ) {
235 24 100       142 if ( UNIVERSAL::isa($out, 'SCALAR') ) {
    50          
236 23     224   130 $self->{_base_out} = sub { $$out .= join("", @_) };
  224         650  
237             }
238             elsif ( UNIVERSAL::isa($out, 'ARRAY') ) {
239             $self->{_base_out} = sub {
240 2     2   10 push(@$out, map { +"$_\n" } split(/\n/, $_)) foreach @_;
  3         13  
241 1         7 };
242             }
243             else {
244 0     0   0 $self->{_base_out} = sub { print {$out} (@_) };
  0         0  
  0         0  
245 0 0   0   0 $self->{_base_close} = sub { CORE::close($out) or croak("Close: $!") };
  0         0  
246             }
247             }
248             else {
249 1 50       197 open(my $fd, ">", $out)
250             or croak("Cannot create \"$out\": $!");
251 1     2   6 $self->{_base_out} = sub { print {$fd} (@_) };
  2         3  
  2         16  
252 1 50   1   7 $self->{_base_close} = sub { CORE::close($fd) or croak("Close \"$out\": $!") };
  1         74  
253             }
254             }
255              
256             #### Stylist
257              
258             sub set_stylist {
259 25     25 0 136 my ($self, $stylist_code) = @_;
260 25         70 $self->_argcheck(1);
261 25 50 66     178 croak("Stylist must be a function (code ref)")
262             if $stylist_code && !UNIVERSAL::isa($stylist_code, 'CODE');
263 25         101 $self->{_base_stylist} = $stylist_code;
264             }
265              
266             sub get_stylist {
267 5     5 0 50 my ($self) = @_;
268 5         32 $self->_argcheck(0);
269 5         36 $self->{_base_stylist};
270             }
271              
272             #### Heading generator
273              
274             sub set_heading {
275 22     22 0 52 my ($self, $heading_code) = @_;
276 22         77 $self->_argcheck(1);
277 22 50 33     232 croak("Header must be a function (code ref)")
278             if $heading_code && !UNIVERSAL::isa($heading_code, 'CODE');
279 22         65 $self->{_base_heading} = $heading_code;
280             }
281              
282             sub get_heading {
283 24     24 0 54 my ($self) = @_;
284 24         110 $self->_argcheck(0);
285 24         118 $self->{_base_heading};
286             }
287              
288             sub set_topheading {
289 25     25 0 64 my ($self, $heading_code) = @_;
290 25         68 $self->_argcheck(1);
291 25 50 66     139 croak("Header must be a function (code ref)")
292             if $heading_code && !UNIVERSAL::isa($heading_code, 'CODE');
293 25         66 $self->{_base_topheading} = $heading_code;
294             }
295              
296             sub get_topheading {
297 25     25 0 367 my ($self) = @_;
298 25         81 $self->_argcheck(0);
299 25 100   19   272 $self->{_base_topheading} || sub {};
  19         31  
300             }
301              
302             ################ Friend methods ################
303              
304             sub _argcheck {
305 1255     1255   1793 my ($pkg, $exp) = @_;
306 1255         1287 my ($package, $filename, $line, $subroutine) = do { package DB; caller(1) };
  1255         22569  
307 1255         2847 my $got = scalar(@DB::args)-1;
308 1255 50       3513 return if $exp == $got;
309 0   0     0 $got ||= "none";
310 0         0 $Carp::CarpLevel++;
311 0 0       0 Carp::croak($subroutine." requires ".
    0          
312             ( $exp == 0 ? "no arguments" :
313             $exp == 1 ? " 1 argument" :
314             " $exp arguments" ).
315             " ($got supplied)");
316             }
317              
318             sub _get_fields {
319 90     90   138 my $self = shift;
320 90         187 $self->_argcheck(0);
321 90         291 $self->{_base_fields};
322             }
323              
324             sub _get_fdata {
325 204     204   232 my $self = shift;
326 204         369 $self->_argcheck(0);
327 204         1068 $self->{_base_fdata};
328             }
329              
330             sub _print {
331 228     228   1387 my $self = shift;
332 228         597 $self->{_base_out}->(@_);
333 228         787 $self->{_base_used}++;
334             }
335              
336             sub _started {
337 0     0   0 my $self = shift;
338 0         0 $self->_argcheck(0);
339 0         0 $self->{_base_started};
340             }
341              
342             sub _getstyle {
343 385     385   563 my ($self, $row, $cell) = @_;
344 385 100       984 $self->_argcheck(defined $cell ? 2 : 1);
345 385         559 my $stylist = $self->{_base_stylist};
346 385 100       874 return unless $stylist;
347              
348 289 100       667 return $stylist->($self, $row) unless $cell;
349              
350 258   100     570 my $a = $stylist->($self, "*", $cell) || {};
351 258   100     2629 my $b = $stylist->($self, $row, $cell) || {};
352 258         3044 return { %$a, %$b };
353             }
354              
355             sub _checkhdr {
356 60     60   95 my $self = shift;
357 60         127 $self->_argcheck(0);
358 60 100       217 if ( $self->{_base_needhdr} ) {
359 22         48 $self->{_base_needhdr} = 0;
360 22 100       198 $self->_pageskip if $self->can("_pageskip");
361 22         130 $self->get_topheading->($self);
362 22         221 $self->get_heading->($self);
363             }
364             }
365              
366             sub _needhdr {
367 0     0   0 my $self = shift;
368 0         0 $self->_argcheck(1);
369 0         0 $self->{_base_needhdr} = shift;
370             }
371             sub _does_needhdr {
372 0     0   0 my $self = shift;
373 0         0 $self->_argcheck(0);
374 0         0 $self->{_base_needhdr};
375             }
376              
377             sub _checkname {
378 220     220   284 my $self = shift;
379 220         556 $self->_argcheck(1);
380 220         1535 shift =~ $style_pat;
381             }
382              
383             1;
384              
385             =head1 AUTHOR
386              
387             Johan Vromans, C<< >>
388              
389             =head1 BUGS
390              
391             Please report any bugs or feature requests to
392             C, or through the web interface at
393             L.
394             I will be notified, and then you'll automatically be notified of progress on
395             your bug as I make changes.
396              
397             =head1 COPYRIGHT & LICENSE
398              
399             Copyright 2006 Squirrel Consultancy, all rights reserved.
400              
401             This program is free software; you can redistribute it and/or modify it
402             under the same terms as Perl itself.
403              
404             =cut