package Template::Plugin::ASCIITable; use base 'Template::Plugin'; use Text::ASCIITable; use vars qw($VERSION $AUTOLOAD); $VERSION='0.2'; =head1 NAME Template::Plugin::ASCIITable =head1 SYNOPSIS [% USE ASCIITable %] blah [% ASCIITable.cols('a', 'b', 'c'); ASCIITable.rows([1,2,3],['one','two','three']); ASCIITable.draw() %] =head1 DESCRIPTION This module allows you to use L in your templates. A plugin object will be instantiated with the directive: [% USE ASCIITable %] You can pass a number of parameters to the constructor, for example: [% USE ASCIITable(cols => ['a','b','c'], show=>'rowline') %] See L for details. To obtain the table, you invoke the C method, to which you can pass the same parameters: [% ASCIITable.draw(rows=>[[1,2,3],['one','two','three']]) %] =head1 METHODS =head2 C This is the plugin construtor. You should never call it directly: use the C directive. =cut sub new { my ($class,$context,$params)=@_; my $self=bless {hide=>{rowline=>1}},$class; $self->handleParms(%$params) if $params; return $self; } =head2 C This method invokes L to obtain the textual representation of the table, and returns it. You can pass various parameters to it, see L. =cut sub draw { my ($self,@rest)=@_; $self->handleParms(@rest) if @rest; my $t=Text::ASCIITable->new({$self->_globals()}); $t->setCols($self->_colnames()); for my $c ($self->_colprops()) { $t->alignCol($c->{name},$c->{align}) if $c->{align}; $t->alignColName($c->{name},$c->{nalign}) if $c->{nalign}; $t->setColWidth($c->{name},$c->{width},$c->{widen}) if ($c->{width}||-1)>0; } for my $r ($self->_rows()) { $t->addRow($r) } my $ret=$t->draw($self->_style()); my ($l,$r)=$self->_squeeze(); $ret=~s/^.{$l}//mg if $l; $ret=~s/.{$r}$//mg if $r; $ret=~s/\n$//; return $ret; } my %paramSubs=( hide => \&hide, show => \&show, cols => \&cols, errors => \&errors, reporterrors => \&errors, allow => \&allow, deny => \&deny, alignheadrow => \&alignHeadRow, rows => \&rows, style => \&style, ); =head1 Parameters These parameters can be set in three ways: =over 4 =item * passing them to the constructor =item * passing them to the C call =item * setting them with individual method calls =back All three forms are case-insensitive: you can do [% USE t=ASCIITable(allow=>'HTML') %] [% t.Allow('html') %] [% t.draw(ALLOW=>'HtMl') %] =cut sub handleParms { my ($self,%params)=@_; for my $k (keys %params) { if (exists $paramSubs{lc($k)}) { $paramSubs{lc($k)}->($self,$params{$k}); } else { _die("no such parameter $k"); } } return; } =head2 C This parameter accepts a list of names of features to hide. The recognized names are: =over 4 =item C The very first decoration line of the table, before the column names. =item C The line containing the names of the columns. =item C The decoration line separating the column names from the data lines. =item C The decoration line separating one data line from the next. I. =item C The very last decoration line, after all the data. =back Note: C will cause I features to be hidden. See L. =cut sub hide { my ($self,@what)=@_; if (@what==1 and ref($what[0]) eq 'ARRAY') { @what=@{$what[0]}; } @{$self->{hide}}{grep {m{(headrow)|((head|first|last|row)line)}} map {lc $_} @what}=(); return; } =head2 C This parameter does the opposite of C: sets the given features to be shown. Note: C will cause I features to be shown. See L. =cut sub show { my ($self,@what)=@_; if (@what==1 and ref($what[0]) eq 'ARRAY') { @what=@{$what[0]}; } delete @{$self->{hide}}{grep {m{(head(row|line))|((first|last|row)line)}} map {lc $_} @what}; return; } =head2 C Setting this to a true value will set the C option (see L). =cut sub errors { my ($self,$val)=@_; $self->{errors}=$val; return; } =head2 C This parameter accepts a list of markup names to allow inside the cells. This in needed to let C calculate the correct column widths. The recognized markups are: =over 4 =item C This will allow you to use ANSI escape sequences for things like colors or baldface. This usually works only if you output to a compliant terminal. =item C This will allow you to use HTML tags. No check is performed on the well-formedness of any such tag. =back Note: C will cause I markups to be recognized. See L. =cut sub allow { my ($self,@what)=@_; if (@what==1 and ref($what[0]) eq 'ARRAY') { @what=@{$what[0]}; } @{$self->{allow}}{grep {m{ansi|html}} map {lc $_} @what}=(); return; } =head2 C This parameter does the opposite of C: ignores the given markups inside cells, counting them as data. Note: C will cause I markups to be ignored. See L. =cut sub deny { my ($self,@what)=@_; if (@what==1 and ref($what[0]) eq 'ARRAY') { @what=@{$what[0]}; } delete @{$self->{allow}}{grep {m{ansi|html}} map {lc $_} @what}; return; } =head2 C Sets the alignment for the column names. Can be one of: =over 4 =item * left =item * right =item * center =item * auto =back =cut sub alignHeadRow { my ($self,$val)=@_; $self->{headrow}=$val; return; } sub _handleCols { my ($self,@colspec)=@_; if (@colspec==1 and ref($colspec[0]) eq 'ARRAY') { @colspec=@{$colspec[0]}; } my %colpos=(); my @cols=(); for (@colspec) { if (ref $_) { push @cols,{name=>$_->[0], align=>$_->[1], width=>$_->[2], widen=>$_->[3], nalign=>$_->[4], }; $colpos{$_->[0]}=$#cols; } else { push @cols,{name=>$_}; $colpos{$_}=$#cols; } } return (\@cols,\%colpos) } =head2 C This parameter sets the names, and optionally some properties, of all the columns in the table. To add columns to an existing table, use the L parameter (usually as a method). This parameter accepts a list of column specifications. Each specification can be: =over 4 =item a single string that becomes the name of the column, and width and alignment default to 'auto' =item an array reference that contains, in order: =over 8 =item * the name =item * the alignment for the data, one of 'left', 'right', 'center', 'auto' (see L) =item * the maximum width of the column, in characters (see L) =item * whether to force the width of the column, a boolean (see L, the last parameter) =item * the name alignment (see L) =back =back Note: this sets I. C will result in a table with I column. See L. =cut sub cols { my $self=shift; ($self->{cols},$self->{colpos})=$self->_handleCols(@_); return; } =head2 C This parameter works like C, but adds the given columns to the existing ones, instead of replacing them. So C will result in a table with 3 columns. =cut sub addCols { my $self=shift; my ($cols,$colpos)=$self->_handleCols(@_); push @{$self->{cols}},@$cols; @{$self->{colpos}}{keys %$colpos}=values %$colpos; return; } =head2 C This parameter accepts a list of array references, one per row. Each row must have one scalar element per each column that will be produced. Note that, because of the way this plugin works, you can do something like: [% USE t=ASCIITable(cols=>['a','b']); t.rows([1,2,3],[4,5,6]); t.addCols('c'); t.draw() %] And it will print a 3x2 table. Note: this sets I. C will result in a table with I row. See L. =cut sub rows { my ($self,@rows)=@_; if (@rows==1 and ref($rows[0]) eq 'ARRAY') { @rows=@{$rows[0]}; } $self->{rows}=[@rows]; return; } =head2 C This parameter works like C, but adds the given rows to the existing ones, instead of replacing them. So C will result in a table with 3 rows. =cut sub addRows { my ($self,@row)=@_; if (@row==1 and ref($row[0]) eq 'ARRAY') { @row=@{$row[0]}; } push @{$self->{rows}},[@row]; return; } my %styles=( 'default'=> {lines=>undef, globals=>{hide=>'rowline'}, }, 'rest-simple'=> {lines=>[ ['<','>','=',' '], ['<','>',' '], ['<','>','=',' '], ['<','>',' '], ['<','>','=',' '], ['<','>','-',' '], 1,1, ], globals=>{hide=>'rowline'}, }, 'rest-grid'=> {lines=>[ ['+','+','-','+'], ['|','|','|'], ['+','+','=','+'], ['|','|','|'], ['+','+','-','+'], ['+','+','-','+'], 0,0, ], globals=>{show=>'rowline'}, }, ); =head2 C