—use
5.006;
use
strict;
use
warnings;
package
TextTableTiny;
$TextTableTiny::VERSION
=
'0.02411'
;
# TRIAL
our
@EXPORT_OK
=
qw/ generate_table generate_markdown_table /
;
# ABSTRACT: makes simple tables from two-dimensional arrays, with limited templating options
our
$COLUMN_SEPARATOR
=
'|'
;
our
$ROW_SEPARATOR
=
'-'
;
our
$CORNER_MARKER
=
'+'
;
our
$HEADER_ROW_SEPARATOR
=
'='
;
our
$HEADER_CORNER_MARKER
=
'O'
;
sub
generate_table {
my
%params
=
@_
;
my
$rows
=
$params
{rows} or croak
"generate_table(): you must pass the 'rows' argument!"
;
# foreach col, get the biggest width
my
$widths
= _maxwidths(
$rows
);
my
$max_index
= _max_array_index(
$rows
);
# use that to get the field format and separators
my
$format
= _get_format(
$widths
);
my
$row_sep
= _get_row_separator(
$widths
);
my
$head_row_sep
= _get_header_row_separator(
$widths
);
# here we go...
my
@table
;
push
(
@table
,
$row_sep
)
unless
$params
{top_and_tail};
# if the first row's a header:
my
$data_begins
= 0;
if
(
$params
{header_row} ) {
my
$header_row
=
$rows
->[0];
$data_begins
++;
push
@table
,
sprintf
(
$format
,
map
{
defined
(
$header_row
->[
$_
]) ?
$header_row
->[
$_
] :
''
} (0..
$max_index
)
);
push
@table
,
$params
{separate_rows} ?
$head_row_sep
:
$row_sep
;
}
# then the data
my
$row_number
= 0;
my
$last_line_number
=
int
(
@$rows
);
$last_line_number
--
if
$params
{header_row};
foreach
my
$row
( @{
$rows
}[
$data_begins
..
$#$rows
] ) {
$row_number
++;
push
(
@table
,
sprintf
(
$format
,
map
{
defined
(
$row
->[
$_
]) ?
$row
->[
$_
] :
''
} (0..
$max_index
)
));
push
(
@table
,
$row_sep
)
if
$params
{separate_rows} && (!
$params
{top_and_tail} ||
$row_number
<
$last_line_number
);
}
# this will have already done the bottom if called explicitly
push
(
@table
,
$row_sep
)
unless
$params
{separate_rows} ||
$params
{top_and_tail};
return
join
(
"\n"
,
grep
{
$_
}
@table
);
}
sub
generate_markdown_table {
$CORNER_MARKER
=
'|'
;
$HEADER_ROW_SEPARATOR
=
'-'
;
$HEADER_CORNER_MARKER
=
'|'
;
my
@ARGS
= (
@_
);
unshift
@ARGS
, (
header_row
=> 1,
top_and_tail
=> 1 );
return
TextTableTiny::generate_table(
@ARGS
);
}
sub
_maxwidths {
my
$rows
=
shift
;
# what's the longest array in this list of arrays?
my
$max_index
= _max_array_index(
$rows
);
my
$widths
= [];
for
my
$i
(0..
$max_index
) {
# go through the $i-th element of each array, find the longest
my
$max
= List::Util::max(
map
{
defined
$$_
[
$i
] ?
length
(
$$_
[
$i
]) : 0}
@$rows
);
push
@$widths
,
$max
;
}
return
$widths
;
}
# return highest top-index from all rows in case they're different lengths
sub
_max_array_index {
my
$rows
=
shift
;
return
List::Util::max(
map
{
$#$_
}
@$rows
);
}
sub
_get_format {
my
$widths
=
shift
;
return
"$COLUMN_SEPARATOR "
.
join
(
" $COLUMN_SEPARATOR "
,
map
{
"%-${_}s"
}
@$widths
).
" $COLUMN_SEPARATOR"
;
}
sub
_get_row_separator {
my
$widths
=
shift
;
return
"$CORNER_MARKER$ROW_SEPARATOR"
.
join
(
"$ROW_SEPARATOR$CORNER_MARKER$ROW_SEPARATOR"
,
map
{
$ROW_SEPARATOR
x
$_
}
@$widths
).
"$ROW_SEPARATOR$CORNER_MARKER"
;
}
sub
_get_header_row_separator {
my
$widths
=
shift
;
return
"$HEADER_CORNER_MARKER$HEADER_ROW_SEPARATOR"
.
join
(
"$HEADER_ROW_SEPARATOR$HEADER_CORNER_MARKER$HEADER_ROW_SEPARATOR"
,
map
{
$HEADER_ROW_SEPARATOR
x
$_
}
@$widths
).
"$HEADER_ROW_SEPARATOR$HEADER_CORNER_MARKER"
;
}
# Back-compat: 'table' is an alias for 'generate_table', but isn't exported
*table
= \
&generate_table
;
1;
__END__
=pod
=head1 TextTableTiny
This is a temporary fork of Neil Bowen's module to support a method I added to set all of the flags for markdown compatible tables. While waiting on Neil to accept or reject the pull request I wanted Vote::Count to be work on other systems.
=head1 NAME
Text::Table::Tiny - simple text tables from 2D arrays, with limited templating options
=head1 SYNOPSIS
use Text::Table::Tiny 0.04 qw/ generate_table /;
my $rows = [
# header row
['Name', 'Rank', 'Serial'],
# rows
['alice', 'pvt', '123456'],
['bob', 'cpl', '98765321'],
['carol', 'brig gen', '8745'],
];
print generate_table(rows => $rows, header_row => 1);
=head1 DESCRIPTION
This module provides, C<generate_table>, which formats
a two-dimensional array of data as a text table.
A second function C<generate_markdown_table>, formats the table
as markdown and should not be passed any other formatting directives.
The example shown in the SYNOPSIS generates the following table:
+-------+----------+----------+
| Name | Rank | Serial |
+-------+----------+----------+
| alice | pvt | 123456 |
| bob | cpl | 98765321 |
| carol | brig gen | 8745 |
+-------+----------+----------+
B<NOTE>: the interface changed with version 0.04, so if you
use the C<generate_table()> function illustrated above,
then you need to require at least version 0.04 of this module,
as shown in the SYNOPSIS.
=head2 generate_table()
The C<generate_table> function understands three arguments,
which are passed as a hash.
=over 4
=item *
rows
Takes an array reference which should contain one or more rows
of data, where each row is an array reference.
=item *
header_row
If given a true value, the first row in the data will be interpreted
as a header row, and separated from the rest of the table with a ruled line.
=item *
separate_rows
If given a true value, a separator line will be drawn between every row in
the table,
and a thicker line will be used for the header separator.
=item *
top_and_tail
If given a true value, then the top and bottom border lines will be skipped.
This reduces the vertical height of the generated table.
=back
=head2 generate_markdown_table()
Calls C<generate_table()> with all of the settings and parameters
necessary to return a table that is valid for most markdown
interpreters.
You should not pass or set any other formatting options when using
C<generate_markdown_table>.
The first row in the data from rows => will be used as the header row.
=head2 EXAMPLES
If you just pass the data and no other options:
generate_table(rows => $rows);
You get minimal ruling:
+-------+----------+----------+
| Name | Rank | Serial |
| alice | pvt | 123456 |
| bob | cpl | 98765321 |
| carol | brig gen | 8745 |
+-------+----------+----------+
If you want lines between every row, and also want a separate header:
generate_table(rows => $rows, header_row => 1, separate_rows => 1);
You get the maximally ornate:
+-------+----------+----------+
| Name | Rank | Serial |
O=======O==========O==========O
| alice | pvt | 123456 |
+-------+----------+----------+
| bob | cpl | 98765321 |
+-------+----------+----------+
| carol | brig gen | 8745 |
+-------+----------+----------+
If you want your table in MarkDown compatible format:
generate_markdown_table( rows => $rows );
| Name | Rank | Serial | |
|-------|----------|----------|
| alice | pvt | 123456 |
| bob | cpl | 98765321 |
| carol | brig gen | 8745 |
=head1 FORMAT VARIABLES
You can set a number of package variables inside the C<Text::Table::Tiny> package
to configure the appearance of the table.
This interface is likely to be deprecated in the future,
and some other mechanism provided.
=over 4
=item *
$Text::Table::Tiny::COLUMN_SEPARATOR = '|';
=item *
$Text::Table::Tiny::ROW_SEPARATOR = '-';
=item *
$Text::Table::Tiny::CORNER_MARKER = '+';
=item *
$Text::Table::Tiny::HEADER_ROW_SEPARATOR = '=';
=item *
$Text::Table::Tiny::HEADER_CORNER_MARKER = 'O';
=back
=head1 PREVIOUS INTERFACE
Prior to version 0.04 this module provided a function called C<table()>,
which wasn't available for export. It took exactly the same arguments:
use Text::Table::Tiny;
my $rows = [ ... ];
print Text::Table::Tiny::table(rows => $rows, separate_rows => 1, header_row => 1);
For backwards compatibility this interface is still supported.
The C<table()> function isn't available for export though.
=head1 SEE ALSO
There are many modules for formatting text tables on CPAN.
A good number of them are listed in the
section of the documentation for L<Text::Table::Manifold>.
=head1 REPOSITORY
=head1 AUTHOR
Creighton Higgins <chiggins@chiggins.com>
Now maintained by Neil Bowers <neilb@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2012 by Creighton Higgins.
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