blib/lib/Text/SpanningTable.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 126 | 129 | 97.6 |
branch | 53 | 64 | 82.8 |
condition | 9 | 16 | 56.2 |
subroutine | 11 | 11 | 100.0 |
pod | 8 | 8 | 100.0 |
total | 207 | 228 | 90.7 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Text::SpanningTable; | ||||||
2 | BEGIN { | ||||||
3 | 2 | 2 | 4125064 | $Text::SpanningTable::VERSION = '0.2'; | |||
4 | } | ||||||
5 | |||||||
6 | 2 | 2 | 23 | use warnings; | |||
2 | 5 | ||||||
2 | 61 | ||||||
7 | 2 | 2 | 12 | use strict; | |||
2 | 5 | ||||||
2 | 5395 | ||||||
8 | |||||||
9 | # ABSTRACT: ASCII tables with support for column spanning. | ||||||
10 | |||||||
11 | # this hash-ref holds the characters used to print the table decorations. | ||||||
12 | our $C = { | ||||||
13 | top => { # the top border, i.e. hr('top') | ||||||
14 | left => '.-', | ||||||
15 | border => '-', | ||||||
16 | sep => '-+-', | ||||||
17 | right => '-.', | ||||||
18 | }, | ||||||
19 | middle => { # simple horizontal rule, i.e. hr('middle') or hr() | ||||||
20 | left => '+-', | ||||||
21 | border => '-', | ||||||
22 | sep => '-+-', | ||||||
23 | right => '-+', | ||||||
24 | }, | ||||||
25 | dhr => { # double horizontal rule, i.e. hr('dhr') or dhr() | ||||||
26 | left => '+=', | ||||||
27 | border => '=', | ||||||
28 | sep => '=+=', | ||||||
29 | right => '=+', | ||||||
30 | }, | ||||||
31 | bottom => { # bottom border, i.e. hr('bottom') | ||||||
32 | left => "'-", | ||||||
33 | border => '-', | ||||||
34 | sep => '-+-', | ||||||
35 | right => "-'", | ||||||
36 | }, | ||||||
37 | row => { # row decoration | ||||||
38 | left => '| ', | ||||||
39 | sep => ' | ', | ||||||
40 | right => ' |', | ||||||
41 | }, | ||||||
42 | }; | ||||||
43 | |||||||
44 | =head1 NAME | ||||||
45 | |||||||
46 | Text::SpanningTable - ASCII tables with support for column spanning. | ||||||
47 | |||||||
48 | =head1 VERSION | ||||||
49 | |||||||
50 | version 0.2 | ||||||
51 | |||||||
52 | =head1 SYNOPSIS | ||||||
53 | |||||||
54 | use Text::SpanningTable; | ||||||
55 | |||||||
56 | # create a table object with four columns of varying widths | ||||||
57 | my $t = Text::SpanningTable->new(10, 20, 15, 25); | ||||||
58 | |||||||
59 | # enable auto-newline adding | ||||||
60 | $t->newlines(1); | ||||||
61 | |||||||
62 | # print a top border | ||||||
63 | print $t->hr('top'); | ||||||
64 | |||||||
65 | # print a row (with header information) | ||||||
66 | print $t->row('Column 1', 'Column 2', 'Column 3', 'Column 4'); | ||||||
67 | |||||||
68 | # print a double horizontal rule | ||||||
69 | print $t->dhr; # also $t->hr('dhr'); | ||||||
70 | |||||||
71 | # print a row of data | ||||||
72 | print $t->row('one', 'two', 'three', 'four'); | ||||||
73 | |||||||
74 | # print a horizontal rule | ||||||
75 | print $t->hr; | ||||||
76 | |||||||
77 | # print another row, with one column that spans all four columns | ||||||
78 | print $t->row([4, 'Creedance Clearwater Revival']); | ||||||
79 | |||||||
80 | # print a horizontal rule | ||||||
81 | print $t->hr; | ||||||
82 | |||||||
83 | # print a row with the first column normally and another column | ||||||
84 | # spanning the remaining three columns | ||||||
85 | print $t->row( | ||||||
86 | 'normal column', | ||||||
87 | [3, 'this column spans three columns and also wraps to the next line.'] | ||||||
88 | ); | ||||||
89 | |||||||
90 | # finally, print the bottom border | ||||||
91 | print $t->hr('bottom'); | ||||||
92 | |||||||
93 | # the output from all these commands is: | ||||||
94 | .----------+------------------+-------------+-----------------------. | ||||||
95 | | Column 1 | Column 2 | Column 3 | Column 4 | | ||||||
96 | +==========+==================+=============+=======================+ | ||||||
97 | | one | two | three | four | | ||||||
98 | +----------+------------------+-------------+-----------------------+ | ||||||
99 | | Creedance Clearwater Revival | | ||||||
100 | +----------+------------------+-------------+-----------------------+ | ||||||
101 | | normal | this column spans three columns and also wraps to the | | ||||||
102 | | | next line. | | ||||||
103 | '----------+------------------+-------------+-----------------------' | ||||||
104 | |||||||
105 | =head1 DESCRIPTION | ||||||
106 | |||||||
107 | C |
||||||
108 | with support for column spanning. It is meant to be used with monospace | ||||||
109 | fonts such as common in terminals, and thus is useful for logging purposes. | ||||||
110 | |||||||
111 | This module is inspired by L |
||||||
112 | the same output (except that C |
||||||
113 | spanning), but with a few key differences: | ||||||
114 | |||||||
115 | =over | ||||||
116 | |||||||
117 | =item * In C |
||||||
118 | C |
||||||
119 | your table (or do whatever you want with the output) as it is being built. | ||||||
120 | If you don't need to have your tables in "real-time", you can just save the | ||||||
121 | output in a variable, but for convenience and compatibility with | ||||||
122 | C |
||||||
123 | actually an alias for the C | ||||||
124 | output. | ||||||
125 | |||||||
126 | =item * C |
||||||
127 | the table by itself. Due to C |
||||||
128 | this functionality is not provided, and you have to take care of that yourself. | ||||||
129 | |||||||
130 | =item * C |
||||||
131 | when creating the table object. This module doesn't have that functionality, | ||||||
132 | you have to create header rows (or footer rows) yourself and how you see | ||||||
133 | fit. | ||||||
134 | |||||||
135 | =item * C |
||||||
136 | (called 'dhr' for 'double horizontal rule') that can be used for header | ||||||
137 | and footer rows (or whatever you see fit). | ||||||
138 | |||||||
139 | =item * C |
||||||
140 | function that can be automatically invoked on the module's output when | ||||||
141 | calling C or C |
||||||
142 | |||||||
143 | =item * In C |
||||||
144 | are the widths of the data they can accommodate, i.e. without the borders | ||||||
145 | and padding. In C |
||||||
146 | the borders and padding. If you are familiar with the CSS and the box model, | ||||||
147 | then columns in C |
||||||
148 | while in C |
||||||
149 | So take into account that the width of the column's data will be four | ||||||
150 | characters less than defined. | ||||||
151 | |||||||
152 | =back | ||||||
153 | |||||||
154 | Like C |
||||||
155 | the same width as defined, i.e. they will not stretch to accommodate the | ||||||
156 | data passed to the cells. If a cell's data is too big, it will be wrapped | ||||||
157 | (with possible word-breaking using the '-' character), thus resulting in | ||||||
158 | more lines of text. | ||||||
159 | |||||||
160 | =head1 METHODS | ||||||
161 | |||||||
162 | =head2 new( [@column_widths] ) | ||||||
163 | |||||||
164 | Creates a new instance of C |
||||||
165 | provided widths. If you don't provide any column widths, the table will | ||||||
166 | have one column with a width of 100 characters. | ||||||
167 | |||||||
168 | =cut | ||||||
169 | |||||||
170 | sub new { | ||||||
171 | 3 | 3 | 1 | 501 | my ($class, @cols) = @_; | ||
172 | |||||||
173 | 3 | 5 | my $width; # total width of the table | ||||
174 | |||||||
175 | # default widths | ||||||
176 | 3 | 100 | 100 | 14 | @cols = (100) unless @cols and scalar @cols; | ||
177 | |||||||
178 | 3 | 7 | foreach (@cols) { | ||||
179 | 8 | 12 | $width += $_; | ||||
180 | } | ||||||
181 | |||||||
182 | 3 | 21 | return bless { | ||||
183 | cols => \@cols, | ||||||
184 | width => $width, | ||||||
185 | newlines => 0, | ||||||
186 | output => [], | ||||||
187 | }, $class; | ||||||
188 | } | ||||||
189 | |||||||
190 | =head2 newlines( [$boolean] ) | ||||||
191 | |||||||
192 | By default, trailing newlines will NOT be added automatically to the output generated | ||||||
193 | by this module (for example, when printing a horizontal rule, a newline | ||||||
194 | character will not be added). Pass a boolean value to this method to | ||||||
195 | enable/disable automatic newline creation. Returns the current value of | ||||||
196 | this attribute (after changing it if a boolean value has been passed). | ||||||
197 | |||||||
198 | =cut | ||||||
199 | |||||||
200 | sub newlines { | ||||||
201 | 55 | 100 | 55 | 1 | 356 | if (defined $_[1]) { | |
202 | 1 | 4 | $_[0]->{newlines} = $_[1]; | ||||
203 | } | ||||||
204 | |||||||
205 | 55 | 226 | return $_[0]->{newlines}; | ||||
206 | } | ||||||
207 | |||||||
208 | =head2 exec( \&sub, [@args] ) | ||||||
209 | |||||||
210 | Define a callback function to be invoked whenever calling C |
||||||
211 | or C |
||||||
212 | or a reference to a subroutine, and a list of parameters/arguments you | ||||||
213 | wish this subroutine to have (C<@args> above). When called, the subroutine | ||||||
214 | will receive, as arguments, the generated output, and C<@args>. | ||||||
215 | |||||||
216 | So, for example, you can do: | ||||||
217 | |||||||
218 | $t->exec(sub { my ($output, $log) = @_; $log->info($output); }, $log); | ||||||
219 | |||||||
220 | This would result in C<< $log->info($output) >> being invoken whenever | ||||||
221 | calling C or C |
||||||
222 | these methods generated. See more info at the C |
||||||
223 | below. | ||||||
224 | |||||||
225 | =cut | ||||||
226 | |||||||
227 | sub exec { | ||||||
228 | 1 | 1 | 1 | 9 | my $self = shift; | ||
229 | |||||||
230 | 1 | 3 | $self->{exec} = shift; | ||||
231 | 1 | 50 | 7 | $self->{args} = \@_ if scalar @_; | |||
232 | } | ||||||
233 | |||||||
234 | =head2 hr( ['top'|'middle'|'bottom'|'dhr'] ) | ||||||
235 | |||||||
236 | Generates a horizontal rule of a certain type. Unless a specific type is | ||||||
237 | provided, 'middle' we be used. 'top' generates a top border for the table, | ||||||
238 | 'bottom' generates a bottom border, and 'dhr' is the same as 'middle', but | ||||||
239 | generates a 'double horizontal rule' that is more pronounced and thus can | ||||||
240 | be used for headers and footers. | ||||||
241 | |||||||
242 | This method will always result in one line of text. | ||||||
243 | |||||||
244 | =cut | ||||||
245 | |||||||
246 | sub hr { | ||||||
247 | 13 | 13 | 1 | 738 | my ($self, $type) = @_; | ||
248 | |||||||
249 | # generate a simple horizontal rule by default | ||||||
250 | 13 | 100 | 34 | $type ||= 'middle'; | |||
251 | |||||||
252 | # start with the left decoration | ||||||
253 | 13 | 30 | my $output = $C->{$type}->{left}; | ||||
254 | |||||||
255 | # print a border for every column in the table, with separator | ||||||
256 | # decorations between them | ||||||
257 | 13 | 21 | for (my $i = 0; $i < scalar @{$self->{cols}}; $i++) { | ||||
56 | 130 | ||||||
258 | 43 | 55 | my $width = $self->{cols}->[$i] - 4; | ||||
259 | 43 | 80 | $output .= $C->{$type}->{border} x$width; | ||||
260 | |||||||
261 | # print a separator unless this is the last column | ||||||
262 | 43 | 100 | 35 | $output .= $C->{$type}->{sep} unless $i == (scalar @{$self->{cols}} - 1); | |||
43 | 140 | ||||||
263 | } | ||||||
264 | |||||||
265 | # right decoration | ||||||
266 | 13 | 25 | $output .= $C->{$type}->{right}; | ||||
267 | |||||||
268 | # push this to the output buffer | ||||||
269 | 13 | 14 | push(@{$self->{output}}, $output); | ||||
13 | 33 | ||||||
270 | |||||||
271 | # are we adding newlines? | ||||||
272 | 13 | 100 | 28 | $output .= "\n" if $self->newlines; | |||
273 | |||||||
274 | # if a callback function is defined, invoke it | ||||||
275 | 13 | 100 | 32 | if ($self->{exec}) { | |||
276 | 8 | 11 | my @args = ($output); | ||||
277 | 8 | 50 | 18 | unshift(@args, @{$self->{args}}) if $self->{args}; | |||
8 | 14 | ||||||
278 | 8 | 18 | $self->{exec}->(@args); | ||||
279 | } | ||||||
280 | |||||||
281 | 13 | 57 | return $output; | ||||
282 | } | ||||||
283 | |||||||
284 | =head2 dhr() | ||||||
285 | |||||||
286 | Convenience method that simply calls C . |
||||||
287 | |||||||
288 | =cut | ||||||
289 | |||||||
290 | sub dhr { | ||||||
291 | 1 | 1 | 1 | 6 | shift->hr('dhr'); | ||
292 | } | ||||||
293 | |||||||
294 | =head2 row( @column_data ) | ||||||
295 | |||||||
296 | Generates a new row from an array holding the data for the row's columns. | ||||||
297 | At a maximum, the number of items in the C<@column_data> array will be | ||||||
298 | the number of columns defined when creating the object. At a minimum, it | ||||||
299 | will have one item. If the passed data doesn't fill the entire row, the | ||||||
300 | rest of the columns will be printed blank (so it is not structurally | ||||||
301 | incorrect to pass insufficient data). | ||||||
302 | |||||||
303 | When a column doesn't span, simply push a scalar to the array. When it | ||||||
304 | does span, push an array-ref with two items, the first being the number | ||||||
305 | of columns to span, the second being the scalar data to print. Passing an | ||||||
306 | array-ref with 1 for the first item is the same as just passing the scalar | ||||||
307 | data (as the column will simply span itself). | ||||||
308 | |||||||
309 | So, for example, if the table has nine columns, the following is a valid | ||||||
310 | value for C<@column_data>: | ||||||
311 | |||||||
312 | ( 'one', [2, 'two and three'], 'four', [5, 'five through nine'] ) | ||||||
313 | |||||||
314 | The following is also valid: | ||||||
315 | |||||||
316 | ( 'one', [5, 'two through six'] ) | ||||||
317 | |||||||
318 | Columns seven through nine in the above example will be blank, so it's the | ||||||
319 | same as passing: | ||||||
320 | |||||||
321 | ( 'one', [5, 'two through six'], ' ', ' ', ' ' ) | ||||||
322 | |||||||
323 | If a column's data is longer than its width, the data will wrapped | ||||||
324 | and broken, which will result in the row being constructed from more than one | ||||||
325 | lines of text. Thus, as opposed to the C method, this method has |
||||||
326 | two options for a return value: in list context, it will return all the | ||||||
327 | lines constructing the row (with or without newlines at the end of each | ||||||
328 | string as per what was defined with the C |
||||||
329 | context, however, it will return the row as a string containing newline | ||||||
330 | characters that separate the lines of text (once again, a trailing newline | ||||||
331 | will be added to this string only if a true value was passed to C |
||||||
332 | |||||||
333 | If a callback function has been defined, it will not be invoked with the | ||||||
334 | complete output of this row (i.e. with all the lines of text that has | ||||||
335 | resulted), but instead will be called once per each line of text. This is | ||||||
336 | what makes the callback function so useful, as it helps you cope with | ||||||
337 | problems resulting from all the newline characters separating these lines. | ||||||
338 | When the callback function is called on each line of text, the line will | ||||||
339 | only contain the newline character at its end if C |
||||||
340 | set to true. | ||||||
341 | |||||||
342 | =cut | ||||||
343 | |||||||
344 | sub row { | ||||||
345 | 11 | 11 | 1 | 39 | my ($self, @data) = @_; | ||
346 | |||||||
347 | 11 | 14 | my @rows; # will hold a matrix of the table | ||||
348 | |||||||
349 | 11 | 12 | my $done = 0; # how many columns have we generated yet? | ||||
350 | |||||||
351 | # go over all columns provided | ||||||
352 | 11 | 28 | for (my $i = 0; $i < scalar @data; $i++) { | ||||
353 | # is this a spanning column? what is the width of it? | ||||||
354 | 24 | 24 | my $width = 0; | ||||
355 | |||||||
356 | 24 | 24 | my $text = ''; # will hold column's text | ||||
357 | |||||||
358 | 24 | 100 | 42 | if (ref $data[$i] eq 'ARRAY') { | |||
359 | # this is a spanning column | ||||||
360 | 6 | 50 | 15 | $text .= $data[$i]->[1] if $data[$i]->[1]; | |||
361 | |||||||
362 | 6 | 14 | foreach (0 .. $data[$i]->[0] - 1) { | ||||
363 | # $data[$i]->[0] is the number of columns this column spans | ||||||
364 | 17 | 29 | $width += $self->{cols}->[$done + $_]; | ||||
365 | } | ||||||
366 | |||||||
367 | # subtract the number of columns this column spans | ||||||
368 | # minus 1, because two adjacent columns share the | ||||||
369 | # same separating border | ||||||
370 | 6 | 10 | $width -= $data[$i]->[0] - 1; | ||||
371 | |||||||
372 | # increase $done with the number of columns we have | ||||||
373 | # just parsed | ||||||
374 | 6 | 8 | $done += $data[$i]->[0]; | ||||
375 | } else { | ||||||
376 | # no spanning | ||||||
377 | 18 | 50 | 40 | $text .= $data[$i] if $data[$i]; | |||
378 | 18 | 25 | $width = $self->{cols}->[$done]; | ||||
379 | 18 | 20 | $done++; | ||||
380 | } | ||||||
381 | |||||||
382 | # make sure the column's data is at least 4 characters long | ||||||
383 | # (because we're subtracting four from every column to make | ||||||
384 | # room for the borders and separators) | ||||||
385 | 24 | 100 | 51 | $text .= ' 'x(4 - length($text)) if length($text) < 4; | |||
386 | |||||||
387 | # subtract four from the width, for the column's decorations | ||||||
388 | 24 | 25 | $width -= 4; | ||||
389 | |||||||
390 | # if the column's text is longer than the available width, | ||||||
391 | # we need to wrap it. | ||||||
392 | 24 | 29 | my $new_string = ''; # will hold parsed text | ||||
393 | 24 | 100 | 38 | if (length($text) > $width) { | |||
394 | 11 | 66 | 47 | while (length($text) && length($text) > $width) { | |||
395 | # if the $width'th character of the string | ||||||
396 | # is a whitespace, just break it with a | ||||||
397 | # new line. | ||||||
398 | |||||||
399 | # else if the $width'th - 1 character of the string | ||||||
400 | # is a whitespace, this is probably the start | ||||||
401 | # of a word, so add a whitespace and a newline. | ||||||
402 | |||||||
403 | # else if the $width'th + 1 character is a whitespace, | ||||||
404 | # it is probably the end of a word, so just | ||||||
405 | # break it with a newline. | ||||||
406 | |||||||
407 | # else we're in the middle of a word, so | ||||||
408 | # we need to break it with '-'. | ||||||
409 | |||||||
410 | |||||||
411 | 43 | 100 | 201 | if (substr($text, $width - 1, 1) =~ m/^\s$/) { | |||
100 | |||||||
100 | |||||||
412 | 2 | 13 | $new_string .= substr($text, 0, $width, '') . "\n"; | ||||
413 | } elsif (substr($text, $width - 2, 1) =~ m/^\s$/) { | ||||||
414 | 8 | 39 | $new_string .= substr($text, 0, $width - 1, '') . " \n"; | ||||
415 | } elsif (substr($text, $width, 1) =~ m/^\s$/) { | ||||||
416 | 7 | 31 | $new_string .= substr($text, 0, $width, '') . "\n"; | ||||
417 | } else { | ||||||
418 | 26 | 124 | $new_string .= substr($text, 0, $width - 1, '') . "-\n"; | ||||
419 | } | ||||||
420 | } | ||||||
421 | 11 | 50 | 22 | $new_string .= $text if length($text); | |||
422 | } else { | ||||||
423 | 13 | 15 | $new_string = $text; | ||||
424 | } | ||||||
425 | |||||||
426 | # if this row's data was split into more than one lines, | ||||||
427 | # we need to store these lines appropriately in our table's | ||||||
428 | # matrix (@rows). | ||||||
429 | 24 | 64 | my @fake_rows = split(/\n/, $new_string); | ||||
430 | 24 | 58 | for (my $j = 0; $j < scalar @fake_rows; $j++) { | ||||
431 | 67 | 100 | 274 | $rows[$j]->[$i] = ref $data[$i] eq 'ARRAY' ? [$data[$i]->[0], $fake_rows[$j]] : $fake_rows[$j]; | |||
432 | } | ||||||
433 | } | ||||||
434 | |||||||
435 | # suppose one column's data was wrapped into more than one lines | ||||||
436 | # of text. this means the matrix won't have data for all these | ||||||
437 | # lines in other columns that did not wrap (or wrapped less), so | ||||||
438 | # let's go over the matrix and fill missing cells with whitespace. | ||||||
439 | 11 | 29 | for (my $i = 1; $i < scalar @rows; $i++) { | ||||
440 | 34 | 36 | for (my $j = 0; $j < scalar @{$self->{cols}}; $j++) { | ||||
157 | 328 | ||||||
441 | 123 | 100 | 204 | next if $rows[$i]->[$j]; | |||
442 | |||||||
443 | 80 | 100 | 157 | if (ref $rows[$i - 1]->[$j] eq 'ARRAY') { | |||
444 | 17 | 21 | my $width = length($rows[$i - 1]->[$j]->[1]); | ||||
445 | 17 | 53 | $rows[$i]->[$j] = [$rows[$i - 1]->[$j]->[0], ' 'x$width]; | ||||
446 | } | ||||||
447 | } | ||||||
448 | } | ||||||
449 | |||||||
450 | # okay, now we go over the matrix and actually generate the | ||||||
451 | # decorated output | ||||||
452 | 11 | 11 | my @output; | ||||
453 | 11 | 24 | for (my $i = 0; $i < scalar @rows; $i++) { | ||||
454 | 45 | 61 | my $output = $C->{row}->{left}; | ||||
455 | |||||||
456 | 45 | 55 | my $push = 0; # how many columns have we generated already? | ||||
457 | |||||||
458 | # print the columns | ||||||
459 | 45 | 47 | for (my $j = 0; $j < scalar @{$rows[$i]}; $j++) { | ||||
150 | 280 | ||||||
460 | 105 | 99 | my $width = 0; | ||||
461 | 105 | 99 | my $text; | ||||
462 | |||||||
463 | 105 | 100 | 182 | if (ref $rows[$i]->[$j] eq 'ARRAY') { | |||
464 | # a spanning column, calculate width and | ||||||
465 | # get the text | ||||||
466 | 27 | 32 | $text = $rows[$i]->[$j]->[1]; | ||||
467 | 27 | 46 | foreach (0 .. $rows[$i]->[$j]->[0] - 1) { | ||||
468 | 71 | 137 | $width += $self->{cols}->[$push + $_]; | ||||
469 | } | ||||||
470 | 27 | 40 | $width -= $rows[$i]->[$j]->[0] - 1; | ||||
471 | } else { | ||||||
472 | # normal column | ||||||
473 | 78 | 91 | $text = $rows[$i]->[$j]; | ||||
474 | 78 | 115 | $width = $self->{cols}->[$push]; | ||||
475 | } | ||||||
476 | 105 | 173 | $width -= 4; | ||||
477 | |||||||
478 | # is there any text for this column? if not just | ||||||
479 | # generate whitespace | ||||||
480 | 105 | 100 | 66 | 412 | $output .= $text && length($text) ? $text . ' 'x($width - length($text)) : ' 'x$width; | ||
481 | |||||||
482 | # increase the number of columns we just processed | ||||||
483 | 105 | 100 | 252 | $push += ref $rows[$i]->[$j] eq 'ARRAY' ? $rows[$i]->[$j]->[0] : 1; | |||
484 | |||||||
485 | # print a separator, unless this is the last column | ||||||
486 | 105 | 100 | 91 | $output .= $C->{row}->{sep} unless $push == (scalar @{$self->{cols}}); | |||
105 | 308 | ||||||
487 | } | ||||||
488 | |||||||
489 | # have we processed all columns? (i.e. has the user provided | ||||||
490 | # data for all the columns?) if not, generate empty columns | ||||||
491 | 45 | 41 | my $left = scalar @{$self->{cols}} - $push; | ||||
45 | 70 | ||||||
492 | |||||||
493 | 45 | 100 | 72 | if ($left) { | |||
494 | 8 | 433 | for (my $k = 1; $k <= $left; $k++) { | ||||
495 | 12 | 18 | my $width = $self->{cols}->[$push++] - 4; | ||||
496 | 12 | 19 | $output .= ' 'x$width; | ||||
497 | 12 | 100 | 35 | $output .= $C->{row}->{sep} unless $k == $left; | |||
498 | } | ||||||
499 | } | ||||||
500 | |||||||
501 | 45 | 66 | $output .= $C->{row}->{right}; | ||||
502 | |||||||
503 | 45 | 119 | push(@output, $output); | ||||
504 | } | ||||||
505 | |||||||
506 | # save output in the object | ||||||
507 | 11 | 11 | push(@{$self->{output}}, @output); | ||||
11 | 30 | ||||||
508 | |||||||
509 | # invoke callback function, if any | ||||||
510 | 11 | 100 | 26 | if ($self->{exec}) { | |||
511 | 7 | 8 | my @args; | ||||
512 | 7 | 50 | 15 | push(@args, @{$self->{args}}) if $self->{args}; | |||
7 | 11 | ||||||
513 | 7 | 8 | foreach (@output) { | ||||
514 | 28 | 50 | 33 | 51 | $_ .= "\n" if $self->newlines && !m/\n$/; | ||
515 | 28 | 42 | push(@args, $_); | ||||
516 | 28 | 71 | $self->{exec}->(@args); | ||||
517 | 28 | 140 | pop @args; | ||||
518 | } | ||||||
519 | } | ||||||
520 | |||||||
521 | # is the user expecting an array? | ||||||
522 | 11 | 50 | 24 | if (wantarray) { | |||
523 | 0 | 0 | foreach (@output) { | ||||
524 | 0 | 0 | 0 | 0 | $_ .= "\n" if $self->newlines && !m/\n$/; | ||
525 | } | ||||||
526 | 0 | 0 | return @output; | ||||
527 | } else { | ||||||
528 | 11 | 29 | my $output = join("\n", @output); | ||||
529 | 11 | 100 | 20 | $output .= "\n" if $self->newlines; | |||
530 | |||||||
531 | 11 | 63 | return $output; | ||||
532 | } | ||||||
533 | } | ||||||
534 | |||||||
535 | =head2 output() | ||||||
536 | |||||||
537 | =head2 draw() | ||||||
538 | |||||||
539 | Returns the entire output generated for the table up to the point of calling | ||||||
540 | this method. It should be stressed that this method does not "finalize" | ||||||
541 | the table by adding top and bottom borders or anything at all. Decoration | ||||||
542 | is done "real-time" and if you don't add top and bottom borders yourself | ||||||
543 | (with C and C , respectively), this method will |
||||||
544 | not do that for you. Returned output will or will not contain newlines as | ||||||
545 | per the value defined with C |
||||||
546 | |||||||
547 | Both the above methods do the same, C |
||||||
548 | compatibility with L |
||||||
549 | |||||||
550 | =cut | ||||||
551 | |||||||
552 | sub output { | ||||||
553 | 2 | 2 | 1 | 7 | my $self = shift; | ||
554 | |||||||
555 | 2 | 3 | my $output = join("\n", @{$self->{output}}); | ||||
2 | 7 | ||||||
556 | 2 | 50 | 6 | $output .= "\n" if $self->newlines; | |||
557 | |||||||
558 | 2 | 8 | return $output; | ||||
559 | } | ||||||
560 | |||||||
561 | sub draw { | ||||||
562 | 1 | 1 | 1 | 3 | shift->output; | ||
563 | } | ||||||
564 | |||||||
565 | =head1 AUTHOR | ||||||
566 | |||||||
567 | Ido Perlmuter, C<< |
||||||
568 | |||||||
569 | =head1 BUGS | ||||||
570 | |||||||
571 | Please report any bugs or feature requests to C |
||||||
572 | the web interface at L |
||||||
573 | automatically be notified of progress on your bug as I make changes. | ||||||
574 | |||||||
575 | =head1 SUPPORT | ||||||
576 | |||||||
577 | You can find documentation for this module with the perldoc command. | ||||||
578 | |||||||
579 | perldoc Text::SpanningTable | ||||||
580 | |||||||
581 | You can also look for information at: | ||||||
582 | |||||||
583 | =over 4 | ||||||
584 | |||||||
585 | =item * RT: CPAN's request tracker | ||||||
586 | |||||||
587 | L |
||||||
588 | |||||||
589 | =item * AnnoCPAN: Annotated CPAN documentation | ||||||
590 | |||||||
591 | L |
||||||
592 | |||||||
593 | =item * CPAN Ratings | ||||||
594 | |||||||
595 | L |
||||||
596 | |||||||
597 | =item * Search CPAN | ||||||
598 | |||||||
599 | L |
||||||
600 | |||||||
601 | =back | ||||||
602 | |||||||
603 | =head1 ACKNOWLEDGEMENTS | ||||||
604 | |||||||
605 | Sebastian Riedel and Marcus Ramberg, authors of L |
||||||
606 | provided the inspiration of this module. | ||||||
607 | |||||||
608 | =head1 LICENSE AND COPYRIGHT | ||||||
609 | |||||||
610 | Copyright 2010 Ido Perlmuter. | ||||||
611 | |||||||
612 | This program is free software; you can redistribute it and/or modify it | ||||||
613 | under the terms of either: the GNU General Public License as published | ||||||
614 | by the Free Software Foundation; or the Artistic License. | ||||||
615 | |||||||
616 | See http://dev.perl.org/licenses/ for more information. | ||||||
617 | |||||||
618 | =cut | ||||||
619 | |||||||
620 | 1; |