File Coverage

blib/lib/Text/SimpleTable/AutoWidth.pm
Criterion Covered Total %
statement 59 61 96.7
branch 24 26 92.3
condition 1 2 50.0
subroutine 9 11 81.8
pod 2 2 100.0
total 95 102 93.1


line stmt bran cond sub pod time code
1 2     2   1168 use strict;
  2         2  
  2         70  
2 2     2   7 use warnings;
  2         3  
  2         119  
3              
4             package Text::SimpleTable::AutoWidth;
5             $Text::SimpleTable::AutoWidth::VERSION = '0.09';
6 2     2   1090 use Moo;
  2         23738  
  2         13  
7              
8             # ABSTRACT: Text::SimpleTable::AutoWidth - Simple eyecandy ASCII tables with auto-width selection
9              
10              
11             has 'fixed_width' => ( is => 'rw', default => 0 ); # isa => 'Int'
12             has 'max_width' => ( is => 'rw', default => 0 ); # isa => 'Int'
13              
14             has 'captions' => ( is => 'rw' ); # isa => 'ArrayRef[Str]'
15             has 'rows' => ( is => 'rw' ); # isa => 'ArrayRef[ArrayRef[Str]]'
16              
17             our $WIDTH_LIMIT = 200; # default maximum width
18              
19              
20             sub row {
21 6     6 1 2720 my ( $self, @texts ) = @_;
22              
23 6 100       19 if ( $self->rows ) {
24 2         3 push( @{ $self->rows }, [@texts] );
  2         4  
25             }
26             else {
27 4         9 $self->rows( [ [@texts] ] );
28             }
29              
30 6         12 return $self;
31             }
32              
33              
34 2     2   2815 use List::Util;
  2         3  
  2         127  
35 2     2   919 use Text::SimpleTable;
  2         3097  
  2         1009  
36              
37             sub draw {
38 4     4 1 12 my $self = shift;
39              
40             # count of columns will be same as count of captions, or same
41             # as count of columns in first row, if there is no captions
42             my $columns =
43             ( $self->captions && @{ $self->captions } )
44 4   50     24 || ( $self->rows && @{ $self->rows->[0] } )
45             || 0;
46              
47 4 50       7 return unless $columns;
48              
49             # table will not be wider than limits
50 4 100       17 my $limit =
    100          
51             $self->max_width ? $self->max_width
52             : $self->fixed_width ? $self->fixed_width
53             : $WIDTH_LIMIT;
54              
55             # by default, each column should have at least 2 symbols:
56             # one informative and one for '-' (if we'll need to wrap)
57 4         7 my @max_width = (2) x $columns;
58              
59             # calculate max width of each column
60 4 100       7 for my $row ( ( $self->captions ? $self->captions : () ), @{ $self->rows } ) {
  4         10  
61 7         9 my @row_width = map { length } @$row;
  15         23  
62 7 100       16 $#row_width = $columns - 1 if $#row_width >= $columns;
63              
64             # find new width
65             # we will do this in two passes
66 7         8 my @new_width = @max_width;
67              
68             # first pass:
69             # find new width for all columns, that we can
70             # make wider without need to wrap anything
71 7         10 for my $idx ( 0 .. $#row_width ) {
72 14 100       25 if ( $max_width[$idx] < $row_width[$idx] ) {
73 12         11 $new_width[$idx] = $row_width[$idx];
74              
75             # check for limits
76             my $total = $columns + 1 # for each '|'
77             + $columns * 2 # for spaces around each value
78 12     16   56 + List::Util::reduce { $a + $b } @new_width;
  16         15  
79              
80             # restore old value, if new value will lead to wrap
81 12 100       41 $new_width[$idx] = $max_width[$idx]
82             if $total > $limit;
83             }
84             }
85              
86             # second pass:
87             # find new width for all columns, that we can
88             # make wider and need to wrap something
89 7         12 for my $idx ( 0 .. $#row_width ) {
90 14 100       26 if ( $new_width[$idx] < $row_width[$idx] ) {
91             my $total = $columns + 1 # for each '|'
92             + $columns * 2 # for spaces around each value
93 1     0   5 + List::Util::reduce { $a + $b } @new_width;
  0         0  
94              
95 1         3 $new_width[$idx] += $limit - $total;
96 1         2 last;
97             }
98             }
99              
100             # save new result
101 7         9 @max_width = @new_width;
102              
103             # check for limits
104             my $total = $columns + 1 # for each '|'
105             + $columns * 2 # for spaces around each value
106 7     7   25 + List::Util::reduce { $a + $b } @max_width;
  7         7  
107              
108 7 100       23 last if $total >= $limit;
109             }
110              
111             # check for fixed_width
112 4 100       11 if ( $self->fixed_width ) {
113             my $total = $columns + 1 # for each '|'
114             + $columns * 2 # for spaces around each value
115 1     0   4 + List::Util::reduce { $a + $b } @max_width;
  0         0  
116              
117 1 50       6 $max_width[-1] += $self->fixed_width - $total
118             unless $total == $self->fixed_width;
119             }
120              
121             # prepare drawer
122 4         5 my @params = @max_width;
123              
124 4 100       9 if ( $self->captions ) {
125 1         1 my $idx = 0;
126 1         2 for (@params) {
127 3         7 $_ = [ $_, $self->captions->[ $idx++ ] ];
128             }
129             }
130 4         34 my $tab = Text::SimpleTable->new(@params);
131              
132             # put rows into drawer
133 4         130 $tab->row(@$_) for @{ $self->rows };
  4         20  
134              
135 4         325 return $tab->draw();
136             }
137              
138             __PACKAGE__->meta->make_immutable();
139              
140              
141             1; # End of Text::SimpleTable::AutoWidth
142              
143             __END__