Initial Commit
This commit is contained in:
527
database/perl/vendor/lib/Template/Iterator.pm
vendored
Normal file
527
database/perl/vendor/lib/Template/Iterator.pm
vendored
Normal file
@@ -0,0 +1,527 @@
|
||||
#============================================================= -*-Perl-*-
|
||||
#
|
||||
# Template::Iterator
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Module defining an iterator class which is used by the FOREACH
|
||||
# directive for iterating through data sets. This may be
|
||||
# sub-classed to define more specific iterator types.
|
||||
#
|
||||
# AUTHOR
|
||||
# Andy Wardley <abw@wardley.org>
|
||||
#
|
||||
# COPYRIGHT
|
||||
# Copyright (C) 1996-2007 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.
|
||||
#
|
||||
#============================================================================
|
||||
|
||||
package Template::Iterator;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use base 'Template::Base';
|
||||
use Template::Constants;
|
||||
use Template::Exception;
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
use constant ODD => 'odd';
|
||||
use constant EVEN => 'even';
|
||||
|
||||
our $VERSION = '3.009';
|
||||
our $DEBUG = 0 unless defined $DEBUG;
|
||||
our $AUTOLOAD;
|
||||
|
||||
#========================================================================
|
||||
# ----- CLASS METHODS -----
|
||||
#========================================================================
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# new(\@target, \%options)
|
||||
#
|
||||
# Constructor method which creates and returns a reference to a new
|
||||
# Template::Iterator object. A reference to the target data (array
|
||||
# or hash) may be passed for the object to iterate through.
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $data = shift || [ ];
|
||||
my $params = shift || { };
|
||||
|
||||
if (ref $data eq 'HASH') {
|
||||
# map a hash into a list of { key => ???, value => ??? } hashes,
|
||||
# one for each key, sorted by keys
|
||||
$data = [ map { { key => $_, value => $data->{ $_ } } }
|
||||
sort keys %$data ];
|
||||
}
|
||||
elsif (blessed($data) && $data->can('as_list')) {
|
||||
$data = $data->as_list();
|
||||
}
|
||||
elsif (ref $data ne 'ARRAY') {
|
||||
# coerce any non-list data into an array reference
|
||||
$data = [ $data ] ;
|
||||
}
|
||||
|
||||
bless {
|
||||
_DATA => $data,
|
||||
_ERROR => '',
|
||||
}, $class;
|
||||
}
|
||||
|
||||
|
||||
#========================================================================
|
||||
# ----- PUBLIC OBJECT METHODS -----
|
||||
#========================================================================
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# get_first()
|
||||
#
|
||||
# Initialises the object for iterating through the target data set. The
|
||||
# first record is returned, if defined, along with the STATUS_OK value.
|
||||
# If there is no target data, or the data is an empty set, then undef
|
||||
# is returned with the STATUS_DONE value.
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
sub get_first {
|
||||
my $self = shift;
|
||||
my $data = $self->{ _DATA };
|
||||
|
||||
$self->{ _DATASET } = $self->{ _DATA };
|
||||
my $size = scalar @$data;
|
||||
my $index = 0;
|
||||
|
||||
return (undef, Template::Constants::STATUS_DONE) unless $size;
|
||||
|
||||
# initialise various counters, flags, etc.
|
||||
@$self{ qw( SIZE MAX INDEX COUNT FIRST LAST ) }
|
||||
= ( $size, $size - 1, $index, 1, 1, $size > 1 ? 0 : 1, undef );
|
||||
@$self{ qw( PREV NEXT ) } = ( undef, $self->{ _DATASET }->[ $index + 1 ]);
|
||||
|
||||
return $self->{ _DATASET }->[ $index ];
|
||||
}
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# get_next()
|
||||
#
|
||||
# Called repeatedly to access successive elements in the data set.
|
||||
# Should only be called after calling get_first() or a warning will
|
||||
# be raised and (undef, STATUS_DONE) returned.
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
sub get_next {
|
||||
my ( $max, $index ) = @{ $_[0] }{qw( MAX INDEX )};
|
||||
|
||||
# warn about incorrect usage
|
||||
if ( !defined $index ) {
|
||||
my ( $pack, $file, $line ) = caller();
|
||||
warn("iterator get_next() called before get_first() at $file line $line\n");
|
||||
return ( undef, Template::Constants::STATUS_DONE ); ## RETURN ##
|
||||
}
|
||||
|
||||
# if there's still some data to go...
|
||||
elsif ( $index >= $max ) {
|
||||
return ( undef, Template::Constants::STATUS_DONE ); ## RETURN ##
|
||||
}
|
||||
|
||||
my $self = shift;
|
||||
my $dataset = $self->{_DATASET};
|
||||
|
||||
$index++;
|
||||
|
||||
# update counters and flags
|
||||
@$self{qw( INDEX COUNT FIRST LAST PREV NEXT )} = (
|
||||
$index, # INDEX
|
||||
$index + 1, # COUNT
|
||||
0, # FIRST
|
||||
$index == $max ? 1 : 0, # LAST
|
||||
@$dataset[ $index - 1, $index + 1 ] # PREV, NEXT
|
||||
);
|
||||
|
||||
return $dataset->[ $index ]; ## RETURN ##
|
||||
}
|
||||
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# get_all()
|
||||
#
|
||||
# Method which returns all remaining items in the iterator as a Perl list
|
||||
# reference. May be called at any time in the life-cycle of the iterator.
|
||||
# The get_first() method will be called automatically if necessary, and
|
||||
# then subsequent get_next() calls are made, storing each returned
|
||||
# result until the list is exhausted.
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
sub get_all {
|
||||
my $self = shift;
|
||||
my ($max, $index) = @$self{ qw( MAX INDEX ) };
|
||||
my @data;
|
||||
|
||||
# handle cases where get_first() has yet to be called.
|
||||
unless (defined $index) {
|
||||
my ($first, $status) = $self->get_first;
|
||||
|
||||
# refresh $max and $index, after get_first updates MAX and INDEX
|
||||
($max, $index) = @$self{ qw( MAX INDEX ) };
|
||||
|
||||
# empty lists are handled here.
|
||||
if ($status && $status == Template::Constants::STATUS_DONE) {
|
||||
return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
|
||||
}
|
||||
|
||||
push @data, $first;
|
||||
|
||||
## if there's nothing left in the iterator, return the single value.
|
||||
unless ($index < $max) {
|
||||
return \@data;
|
||||
}
|
||||
}
|
||||
|
||||
# if there's still some data to go...
|
||||
if ($index < $max) {
|
||||
$index++;
|
||||
push @data, @{ $self->{ _DATASET } } [ $index..$max ];
|
||||
|
||||
# update counters and flags
|
||||
@$self{ qw( INDEX COUNT FIRST LAST ) }
|
||||
= ( $max, $max + 1, 0, 1 );
|
||||
|
||||
return \@data; ## RETURN ##
|
||||
}
|
||||
else {
|
||||
return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
|
||||
}
|
||||
}
|
||||
|
||||
sub odd {
|
||||
shift->{ COUNT } % 2 ? 1 : 0
|
||||
}
|
||||
|
||||
sub even {
|
||||
shift->{ COUNT } % 2 ? 0 : 1
|
||||
}
|
||||
|
||||
sub parity {
|
||||
shift->{ COUNT } % 2 ? ODD : EVEN;
|
||||
}
|
||||
|
||||
sub index {
|
||||
return $_[0]->{INDEX};
|
||||
}
|
||||
|
||||
sub count {
|
||||
return $_[0]->{COUNT};
|
||||
}
|
||||
|
||||
sub number { # This is here for backward compatibility per sub AUTOLOAD
|
||||
return $_[0]->{COUNT};
|
||||
}
|
||||
|
||||
sub first {
|
||||
return $_[0]->{FIRST};
|
||||
}
|
||||
|
||||
sub last {
|
||||
return $_[0]->{LAST};
|
||||
}
|
||||
|
||||
sub size {
|
||||
return $_[0]->{SIZE};
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# AUTOLOAD
|
||||
#
|
||||
# Provides access to internal fields (e.g. prev, next, etc)
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
my $item = $AUTOLOAD;
|
||||
$item =~ s/.*:://;
|
||||
return if $item eq 'DESTROY';
|
||||
|
||||
# alias NUMBER to COUNT for backwards compatibility
|
||||
$item = 'COUNT' if CORE::index(uc $item,'NUMBER') > -1;
|
||||
|
||||
return $self->{ uc $item };
|
||||
}
|
||||
|
||||
|
||||
#========================================================================
|
||||
# ----- PRIVATE DEBUG METHODS -----
|
||||
#========================================================================
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# _dump()
|
||||
#
|
||||
# Debug method which returns a string detailing the internal state of
|
||||
# the iterator object.
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
sub _dump {
|
||||
my $self = shift;
|
||||
join('',
|
||||
" Data: ", $self->{ _DATA }, "\n",
|
||||
" Index: ", $self->{ INDEX }, "\n",
|
||||
"Number: ", $self->{ NUMBER }, "\n",
|
||||
" Max: ", $self->{ MAX }, "\n",
|
||||
" Size: ", $self->{ SIZE }, "\n",
|
||||
" First: ", $self->{ FIRST }, "\n",
|
||||
" Last: ", $self->{ LAST }, "\n",
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Template::Iterator - Data iterator used by the FOREACH directive
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $iter = Template::Iterator->new(\@data, \%options);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The C<Template::Iterator> module defines a generic data iterator for use
|
||||
by the C<FOREACH> directive.
|
||||
|
||||
It may be used as the base class for custom iterators.
|
||||
|
||||
=head1 PUBLIC METHODS
|
||||
|
||||
=head2 new($data)
|
||||
|
||||
Constructor method. A reference to a list of values is passed as the
|
||||
first parameter. Subsequent calls to L<get_first()> and L<get_next()> calls
|
||||
will return each element from the list.
|
||||
|
||||
my $iter = Template::Iterator->new([ 'foo', 'bar', 'baz' ]);
|
||||
|
||||
The constructor will also accept a reference to a hash array and will
|
||||
expand it into a list in which each entry is a hash array containing
|
||||
a 'C<key>' and 'C<value>' item, sorted according to the hash keys.
|
||||
|
||||
my $iter = Template::Iterator->new({
|
||||
foo => 'Foo Item',
|
||||
bar => 'Bar Item',
|
||||
});
|
||||
|
||||
This is equivalent to:
|
||||
|
||||
my $iter = Template::Iterator->new([
|
||||
{ key => 'bar', value => 'Bar Item' },
|
||||
{ key => 'foo', value => 'Foo Item' },
|
||||
]);
|
||||
|
||||
When passed a single item which is not an array reference, the constructor
|
||||
will automatically create a list containing that single item.
|
||||
|
||||
my $iter = Template::Iterator->new('foo');
|
||||
|
||||
This is equivalent to:
|
||||
|
||||
my $iter = Template::Iterator->new([ 'foo' ]);
|
||||
|
||||
Note that a single item which is an object based on a blessed ARRAY
|
||||
references will NOT be treated as an array and will be folded into
|
||||
a list containing that one object reference.
|
||||
|
||||
my $list = bless [ 'foo', 'bar' ], 'MyListClass';
|
||||
my $iter = Template::Iterator->new($list);
|
||||
|
||||
equivalent to:
|
||||
|
||||
my $iter = Template::Iterator->new([ $list ]);
|
||||
|
||||
If the object provides an C<as_list()> method then the L<Template::Iterator>
|
||||
constructor will call that method to return the list of data. For example:
|
||||
|
||||
package MyListObject;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
bless [ @_ ], $class;
|
||||
}
|
||||
|
||||
package main;
|
||||
|
||||
my $list = MyListObject->new('foo', 'bar');
|
||||
my $iter = Template::Iterator->new($list);
|
||||
|
||||
This is then functionally equivalent to:
|
||||
|
||||
my $iter = Template::Iterator->new([ $list ]);
|
||||
|
||||
The iterator will return only one item, a reference to the C<MyListObject>
|
||||
object, C<$list>.
|
||||
|
||||
By adding an C<as_list()> method to the C<MyListObject> class, we can force
|
||||
the C<Template::Iterator> constructor to treat the object as a list and
|
||||
use the data contained within.
|
||||
|
||||
package MyListObject;
|
||||
|
||||
...
|
||||
|
||||
sub as_list {
|
||||
my $self = shift;
|
||||
return $self;
|
||||
}
|
||||
|
||||
package main;
|
||||
|
||||
my $list = MyListObject->new('foo', 'bar');
|
||||
my $iter = Template::Iterator->new($list);
|
||||
|
||||
The iterator will now return the two items, 'C<foo>' and 'C<bar>', which the
|
||||
C<MyObjectList> encapsulates.
|
||||
|
||||
=head2 get_first()
|
||||
|
||||
Returns a C<($value, $error)> pair for the first item in the iterator set.
|
||||
The C<$error> returned may be zero or undefined to indicate a valid datum
|
||||
was successfully returned. Returns an error of C<STATUS_DONE> if the list
|
||||
is empty.
|
||||
|
||||
=head2 get_next()
|
||||
|
||||
Returns a C<($value, $error)> pair for the next item in the iterator set.
|
||||
Returns an error of C<STATUS_DONE> if all items in the list have been
|
||||
visited.
|
||||
|
||||
=head2 get_all()
|
||||
|
||||
Returns a C<(\@values, $error)> pair for all remaining items in the iterator
|
||||
set. Returns an error of C<STATUS_DONE> if all items in the list have been
|
||||
visited.
|
||||
|
||||
=head2 size()
|
||||
|
||||
Returns the size of the data set or undef if unknown.
|
||||
|
||||
=head2 max()
|
||||
|
||||
Returns the maximum index number (i.e. the index of the last element)
|
||||
which is equivalent to L<size()> - C<1>.
|
||||
|
||||
=head2 index()
|
||||
|
||||
Returns the current index number which is in the range C<0> to L<max()>.
|
||||
|
||||
=head2 count()
|
||||
|
||||
Returns the current iteration count in the range C<1> to L<size()>. This is
|
||||
equivalent to L<index()> + C<1>.
|
||||
|
||||
=head2 first()
|
||||
|
||||
Returns a boolean value to indicate if the iterator is currently on
|
||||
the first iteration of the set.
|
||||
|
||||
=head2 last()
|
||||
|
||||
Returns a boolean value to indicate if the iterator is currently on
|
||||
the last iteration of the set.
|
||||
|
||||
=head2 prev()
|
||||
|
||||
Returns the previous item in the data set, or C<undef> if the iterator is
|
||||
on the first item.
|
||||
|
||||
=head2 next()
|
||||
|
||||
Returns the next item in the data set or C<undef> if the iterator is on the
|
||||
last item.
|
||||
|
||||
=head2 number()
|
||||
|
||||
This is an alias to 'count' to provide backward compatibility.
|
||||
View L<count>.
|
||||
|
||||
=head2 parity()
|
||||
|
||||
Returns the text string C<even> or C<odd> to indicate the parity of the
|
||||
current iteration count (starting at 1). This is typically used to create
|
||||
striped I<zebra tables>.
|
||||
|
||||
<table>
|
||||
[% FOREACH name IN ['Arthur', 'Ford', 'Trillian'] -%]
|
||||
<tr class="[% loop.parity %]">
|
||||
<td>[% name %]</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
|
||||
This will produce the following output:
|
||||
|
||||
<table>
|
||||
<tr class="odd">
|
||||
<td>Arthur</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Ford</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>Trillian</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
You can then style the C<tr.odd> and C<tr.even> elements using CSS:
|
||||
|
||||
tr.odd td {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
tr.even td {
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
=head2 odd()
|
||||
|
||||
Returns a boolean (0/1) value to indicate if the current iterator count
|
||||
(starting at 1) is an odd number. In other words, this will return a true
|
||||
value for the first iterator, the third, fifth, and so on.
|
||||
|
||||
=head2 even()
|
||||
|
||||
Returns a boolean (0/1) value to indicate if the current iterator count
|
||||
(starting at 1) is an even number. In other words, this will return a true
|
||||
value for the second iteration, the fourth, sixth, and so on.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (C) 1996-2007 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.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Template>
|
||||
|
||||
=cut
|
||||
|
||||
# Local Variables:
|
||||
# mode: perl
|
||||
# perl-indent-level: 4
|
||||
# indent-tabs-mode: nil
|
||||
# End:
|
||||
#
|
||||
# vim: expandtab shiftwidth=4:
|
||||
Reference in New Issue
Block a user