File Coverage

blib/lib/Data/Report/Base.pm
Criterion Covered Total %
statement 150 187 80.2
branch 49 84 58.3
condition 19 35 54.2
subroutine 33 42 78.5
pod 0 20 0.0
total 251 368 68.2


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