From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

use strict;
$Text::SimpleTable::AutoWidth::VERSION = '0.09';
use Moo;
# ABSTRACT: Text::SimpleTable::AutoWidth - Simple eyecandy ASCII tables with auto-width selection
has 'fixed_width' => ( is => 'rw', default => 0 ); # isa => 'Int'
has 'max_width' => ( is => 'rw', default => 0 ); # isa => 'Int'
has 'captions' => ( is => 'rw' ); # isa => 'ArrayRef[Str]'
has 'rows' => ( is => 'rw' ); # isa => 'ArrayRef[ArrayRef[Str]]'
our $WIDTH_LIMIT = 200; # default maximum width
sub row {
my ( $self, @texts ) = @_;
if ( $self->rows ) {
push( @{ $self->rows }, [@texts] );
}
else {
$self->rows( [ [@texts] ] );
}
return $self;
}
sub draw {
my $self = shift;
# count of columns will be same as count of captions, or same
# as count of columns in first row, if there is no captions
my $columns =
( $self->captions && @{ $self->captions } )
|| ( $self->rows && @{ $self->rows->[0] } )
|| 0;
return unless $columns;
# table will not be wider than limits
my $limit =
$self->max_width ? $self->max_width
: $self->fixed_width ? $self->fixed_width
: $WIDTH_LIMIT;
# by default, each column should have at least 2 symbols:
# one informative and one for '-' (if we'll need to wrap)
my @max_width = (2) x $columns;
# calculate max width of each column
for my $row ( ( $self->captions ? $self->captions : () ), @{ $self->rows } ) {
my @row_width = map { length } @$row;
$#row_width = $columns - 1 if $#row_width >= $columns;
# find new width
# we will do this in two passes
my @new_width = @max_width;
# first pass:
# find new width for all columns, that we can
# make wider without need to wrap anything
for my $idx ( 0 .. $#row_width ) {
if ( $max_width[$idx] < $row_width[$idx] ) {
$new_width[$idx] = $row_width[$idx];
# check for limits
my $total = $columns + 1 # for each '|'
+ $columns * 2 # for spaces around each value
+ List::Util::reduce { $a + $b } @new_width;
# restore old value, if new value will lead to wrap
$new_width[$idx] = $max_width[$idx]
if $total > $limit;
}
}
# second pass:
# find new width for all columns, that we can
# make wider and need to wrap something
for my $idx ( 0 .. $#row_width ) {
if ( $new_width[$idx] < $row_width[$idx] ) {
my $total = $columns + 1 # for each '|'
+ $columns * 2 # for spaces around each value
+ List::Util::reduce { $a + $b } @new_width;
$new_width[$idx] += $limit - $total;
last;
}
}
# save new result
@max_width = @new_width;
# check for limits
my $total = $columns + 1 # for each '|'
+ $columns * 2 # for spaces around each value
+ List::Util::reduce { $a + $b } @max_width;
last if $total >= $limit;
}
# check for fixed_width
if ( $self->fixed_width ) {
my $total = $columns + 1 # for each '|'
+ $columns * 2 # for spaces around each value
+ List::Util::reduce { $a + $b } @max_width;
$max_width[-1] += $self->fixed_width - $total
unless $total == $self->fixed_width;
}
# prepare drawer
my @params = @max_width;
if ( $self->captions ) {
my $idx = 0;
for (@params) {
$_ = [ $_, $self->captions->[ $idx++ ] ];
}
}
my $tab = Text::SimpleTable->new(@params);
# put rows into drawer
$tab->row(@$_) for @{ $self->rows };
return $tab->draw();
}
__PACKAGE__->meta->make_immutable();
1; # End of Text::SimpleTable::AutoWidth
__END__
=pod
=encoding UTF-8
=head1 NAME
Text::SimpleTable::AutoWidth - Text::SimpleTable::AutoWidth - Simple eyecandy ASCII tables with auto-width selection
=head1 VERSION
version 0.09
=head1 SYNOPSIS
use Text::SimpleTable::AutoWidth;
my $t1 = Text::SimpleTable::AutoWidth->new();
$t1->row( 'foobarbaz', 'yadayadayada' );
print $t1->draw;
.-----------+--------------.
| foobarbaz | yadayadayada |
'-----------+--------------'
my $t2 = Text::SimpleTable::AutoWidth->new();
$t2->captions( 'Foo', 'Bar' );
$t2->row( 'foobarbaz', 'yadayadayada' );
$t2->row( 'barbarbarbarbar', 'yada' );
print $t2->draw;
.-----------------+--------------.
| Foo | Bar |
+-----------------+--------------+
| foobarbaz | yadayadayada |
| barbarbarbarbar | yada |
'-----------------+--------------'
=head1 DESCRIPTION
Simple eyecandy ASCII tables with auto-selection columns width,
as seen in L<Catalyst>.
=head1 METHODS
=head2 new(@attrs)
Inherited constructor from Moo.
You can set following attributes:
=head3 fixed_width
Set fixed width for resulting table. By default it's 0,
that's mean "don't fix width", so width of result table
will depend on input data.
Be warned, that fixed_width will include not only width of your data,
but also all surronding characters, like spaces across values,
table drawings (like '|') and hypen (if wrapping is needed).
=head3 max_width
Set maximum width for resulting table. By default it's 0,
that's mean "use default value". Default value is stored in
$Text::SimpleTable::AutoWidth::WIDTH_LIMIT, and can be changed
at any moment. Default value for WIDTH_LIMIT is 200.
Be warned, that max_width will include not only width of your data,
but also all surronding characters, like spaces across values,
table drawings (like '|') and hypen (if wrapping is needed).
NB: if you set fixed_width and max_width at same time, then you'll
get table with fixed width, but not wider than max_width characters.
=head3 captions
ArrayRef[Str] for captions in resulting table.
=head3 rows
ArrayRef[ArrayRef[Str]] for values in each row.
You can use next method to add individual rows into table.
=head2 row(@texts)
Add new row to table. Return $self, so you can write something like this:
print Text::SimpleTable::AutoWidth
->new( max_width => 55, captions => [qw/ Name Age /] )
->row( 'Mother', 59 )
->row( 'Dad', 58 )
->row( 'me', 32 )
->draw();
=head2 draw()
Draw table. Really, just calculate column width, and then call Text::SimpleTable->draw().
=head1 GIT REPOSITORY
=head1 SEE ALSO
L<Text::SimpleTable>, L<Moo>, L<Catalyst>
=head1 AUTHOR
Oleg Kostyuk, C<< <cub#cpan.org> >>
=head1 COPYRIGHT & LICENSE
Copyright by Oleg Kostyuk.
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.
=head1 AUTHOR
Oleg Kostyuk <cub.uanic@gmail.com>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2015 by Oleg Kostyuk.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut