diff options
Diffstat (limited to 'lib/Template/Plugin/Table.pm')
| -rw-r--r-- | lib/Template/Plugin/Table.pm | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/lib/Template/Plugin/Table.pm b/lib/Template/Plugin/Table.pm new file mode 100644 index 0000000..c1fd79a --- /dev/null +++ b/lib/Template/Plugin/Table.pm @@ -0,0 +1,464 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Table +# +# DESCRIPTION +# +# Plugin to order a linear data set into a virtual 2-dimensional table +# from which row and column permutations can be fetched. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 2000 Andy Wardley. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: Table.pm,v 2.64 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Table; + +require 5.004; + +use strict; +use vars qw( @ISA $VERSION $AUTOLOAD ); +use base qw( Template::Plugin ); +use Template::Plugin; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); + + +#------------------------------------------------------------------------ +# new($context, \@data, \%args) +# +# This constructor method initialises the object to iterate through +# the data set passed by reference to a list as the first parameter. +# It calculates the shape of the permutation table based on the ROWS +# or COLS parameters specified in the $args hash reference. The +# OVERLAP parameter may be provided to specify the number of common +# items that should be shared between subseqent columns. +#------------------------------------------------------------------------ + +sub new { + my ($class, $context, $data, $params) = @_; + my ($size, $rows, $cols, $coloff, $overlap, $error); + + # if the data item is a reference to a Template::Iterator object, + # or subclass thereof, we call its get_all() method to extract all + # the data it contains + if (UNIVERSAL::isa($data, 'Template::Iterator')) { + ($data, $error) = $data->get_all(); + return $class->error("iterator failed to provide data for table: ", + $error) + if $error; + } + + return $class->error('invalid table data, expecting a list') + unless ref $data eq 'ARRAY'; + + $params ||= { }; + return $class->error('invalid table parameters, expecting a hash') + unless ref $params eq 'HASH'; + + # ensure keys are folded to upper case + @$params{ map { uc } keys %$params } = values %$params; + + $size = scalar @$data; + $overlap = $params->{ OVERLAP } || 0; + + # calculate number of columns based on a specified number of rows + if ($rows = $params->{ ROWS }) { + if ($size < $rows) { + $rows = $size; # pad? + $cols = 1; + $coloff = 0; + } + else { + $coloff = $rows - $overlap; + $cols = int ($size / $coloff) + + ($size % $coloff > $overlap ? 1 : 0) + } + } + # calculate number of rows based on a specified number of columns + elsif ($cols = $params->{ COLS }) { + if ($size < $cols) { + $cols = $size; + $rows = 1; + $coloff = 1; + } + else { + $coloff = int ($size / $cols) + + ($size % $cols > $overlap ? 1 : 0); + $rows = $coloff + $overlap; + } + } + else { + $rows = $size; + $cols = 1; + $coloff = 0; + } + + bless { + _DATA => $data, + _SIZE => $size, + _NROWS => $rows, + _NCOLS => $cols, + _COLOFF => $coloff, + _OVERLAP => $overlap, + _PAD => defined $params->{ PAD } ? $params->{ PAD } : 1, + }, $class; +} + + +#------------------------------------------------------------------------ +# row($n) +# +# Returns a reference to a list containing the items in the row whose +# number is specified by parameter. If the row number is undefined, +# it calls rows() to return a list of all rows. +#------------------------------------------------------------------------ + +sub row { + my ($self, $row) = @_; + my ($data, $cols, $offset, $size, $pad) + = @$self{ qw( _DATA _NCOLS _COLOFF _SIZE _PAD) }; + my @set; + + # return all rows if row number not specified + return $self->rows() + unless defined $row; + + return () if $row >= $self->{ _NROWS } || $row < 0; + + my $index = $row; + + for (my $c = 0; $c < $cols; $c++) { + push(@set, $index < $size + ? $data->[$index] + : ($pad ? undef : ())); + $index += $offset; + } + return \@set; +} + + +#------------------------------------------------------------------------ +# col($n) +# +# Returns a reference to a list containing the items in the column whose +# number is specified by parameter. If the column number is undefined, +# it calls cols() to return a list of all columns. +#------------------------------------------------------------------------ + +sub col { + my ($self, $col) = @_; + my ($data, $size) = @$self{ qw( _DATA _SIZE ) }; + my ($start, $end); + my $blanks = 0; + + # return all cols if row number not specified + return $self->cols() + unless defined $col; + + return () if $col >= $self->{ _NCOLS } || $col < 0; + + $start = $self->{ _COLOFF } * $col; + $end = $start + $self->{ _NROWS } - 1; + $end = $start if $end < $start; + if ($end >= $size) { + $blanks = ($end - $size) + 1; + $end = $size - 1; + } + return () if $start >= $size; + return [ @$data[$start..$end], + $self->{ _PAD } ? ((undef) x $blanks) : () ]; +} + + +#------------------------------------------------------------------------ +# rows() +# +# Returns all rows as a reference to a list of rows. +#------------------------------------------------------------------------ + +sub rows { + my $self = shift; + return [ map { $self->row($_) } (0..$self->{ _NROWS }-1) ]; +} + + +#------------------------------------------------------------------------ +# cols() +# +# Returns all rows as a reference to a list of rows. +#------------------------------------------------------------------------ + +sub cols { + my $self = shift; + return [ map { $self->col($_) } (0..$self->{ _NCOLS }-1) ]; +} + + +#------------------------------------------------------------------------ +# AUTOLOAD +# +# Provides read access to various internal data members. +#------------------------------------------------------------------------ + +sub AUTOLOAD { + my $self = shift; + my $item = $AUTOLOAD; + $item =~ s/.*:://; + return if $item eq 'DESTROY'; + + if ($item =~ /^data|size|nrows|ncols|overlap|pad$/) { + return $self->{ $item }; + } + else { + return (undef, "no such table method: $item"); + } +} + + + +1; + +__END__ + + +#------------------------------------------------------------------------ +# IMPORTANT NOTE +# This documentation is generated automatically from source +# templates. Any changes you make here may be lost. +# +# The 'docsrc' documentation source bundle is available for download +# from http://www.template-toolkit.org/docs.html and contains all +# the source templates, XML files, scripts, etc., from which the +# documentation for the Template Toolkit is built. +#------------------------------------------------------------------------ + +=head1 NAME + +Template::Plugin::Table - Plugin to present data in a table + +=head1 SYNOPSIS + + [% USE table(list, rows=n, cols=n, overlap=n, pad=0) %] + + [% FOREACH item = table.row(n) %] + [% item %] + [% END %] + + [% FOREACH item = table.col(n) %] + [% item %] + [% END %] + + [% FOREACH row = table.rows %] + [% FOREACH item = row %] + [% item %] + [% END %] + [% END %] + + [% FOREACH col = table.cols %] + [% col.first %] - [% col.last %] ([% col.size %] entries) + [% END %] + +=head1 DESCRIPTION + +The Table plugin allows you to format a list of data items into a +virtual table. When you create a Table plugin via the USE directive, +simply pass a list reference as the first parameter and then specify +a fixed number of rows or columns. + + [% USE Table(list, rows=5) %] + [% USE table(list, cols=5) %] + +The 'Table' plugin name can also be specified in lower case as shown +in the second example above. You can also specify an alternative variable +name for the plugin as per regular Template Toolkit syntax. + + [% USE mydata = table(list, rows=5) %] + +The plugin then presents a table based view on the data set. The data +isn't actually reorganised in any way but is available via the row(), +col(), rows() and cols() as if formatted into a simple two dimensional +table of n rows x n columns. Thus, if our sample 'alphabet' list +contained the letters 'a' to 'z', the above USE directives would +create plugins that represented the following views of the alphabet. + + [% USE table(alphabet, ... %] + + rows=5 cols=5 + a f k p u z a g m s y + b g l q v b h n t z + c h m r w c i o u + d i n s x d j p v + e j o t y e k q w + f l r x + +We can request a particular row or column using the row() and col() +methods. + + [% USE table(alphabet, rows=5) %] + [% FOREACH item = table.row(0) %] + # [% item %] set to each of [ a f k p u z ] in turn + [% END %] + + [% FOREACH item = table.col(2) %] + # [% item %] set to each of [ m n o p q r ] in turn + [% END %] + +Data in rows is returned from left to right, columns from top to +bottom. The first row/column is 0. By default, rows or columns that +contain empty values will be padded with the undefined value to fill +it to the same size as all other rows or columns. For example, the +last row (row 4) in the first example would contain the values [ e j o +t y undef ]. The Template Toolkit will safely accept these undefined +values and print a empty string. You can also use the IF directive to +test if the value is set. + + [% FOREACH item = table.row(4) %] + [% IF item %] + Item: [% item %] + [% END %] + [% END %] + +You can explicitly disable the 'pad' option when creating the plugin to +returned shortened rows/columns where the data is empty. + + [% USE table(alphabet, cols=5, pad=0) %] + [% FOREACH item = table.col(4) %] + # [% item %] set to each of 'y z' + [% END %] + +The rows() method returns all rows/columns in the table as a reference +to a list of rows (themselves list references). The row() methods +when called without any arguments calls rows() to return all rows in +the table. + +Ditto for cols() and col(). + + [% USE table(alphabet, cols=5) %] + [% FOREACH row = table.rows %] + [% FOREACH item = row %] + [% item %] + [% END %] + [% END %] + +The Template Toolkit provides the first(), last() and size() methods +that can be called on list references to return the first/last entry +or the number of entried. The following example shows how we might +use this to provide an alphabetical index split into 3 even parts. + + [% USE table(alphabet, cols=3, pad=0) %] + [% FOREACH group = table.col %] + [ [% group.first %] - [% group.last %] ([% group.size %] letters) ] + [% END %] + +This produces the following output: + + [ a - i (9 letters) ] + [ j - r (9 letters) ] + [ s - z (8 letters) ] + +We can also use the general purpose join() list method which joins +the items of the list using the connecting string specified. + + [% USE table(alphabet, cols=5) %] + [% FOREACH row = table.rows %] + [% row.join(' - ') %] + [% END %] + +Data in the table is ordered downwards rather than across but can easily +be transformed on output. For example, to format our data in 5 columns +with data ordered across rather than down, we specify 'rows=5' to order +the data as such: + + a f . . + b g . + c h + d i + e j + +and then iterate down through each column (a-e, f-j, etc.) printing +the data across. + + a b c d e + f g h i j + . . + . + +Example code to do so would be much like the following: + + [% USE table(alphabet, rows=3) %] + [% FOREACH cols = table.cols %] + [% FOREACH item = cols %] + [% item %] + [% END %] + [% END %] + + a b c + d e f + g h i + j . . + . + +In addition to a list reference, the Table plugin constructor may be +passed a reference to a Template::Iterator object or subclass thereof. +The get_all() method is first called on the iterator to return all +remaining items. These are then available via the usual Table interface. + + [% USE DBI(dsn,user,pass) -%] + + # query() returns an iterator + [% results = DBI.query('SELECT * FROM alphabet ORDER BY letter') %] + + # pass into Table plugin + [% USE table(results, rows=8 overlap=1 pad=0) -%] + + [% FOREACH row = table.cols -%] + [% row.first.letter %] - [% row.last.letter %]: + [% row.join(', ') %] + [% END %] + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.64, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + Copyright (C) 1996-2004 Andy Wardley. All Rights Reserved. + Copyright (C) 1998-2002 Canon Research Centre Europe Ltd. + +This module is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=head1 SEE ALSO + +L<Template::Plugin|Template::Plugin> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: |
