diff options
| author | Andreas Brachold <vdr07@deltab.de> | 2007-08-13 18:41:27 +0000 |
|---|---|---|
| committer | Andreas Brachold <vdr07@deltab.de> | 2007-08-13 18:41:27 +0000 |
| commit | bcbf441e09fb502cf64924ff2530fa144bdf52c5 (patch) | |
| tree | f377707a2dac078db8cd0c7d7abfe69ac1006d71 /lib/Template | |
| download | xxv-bcbf441e09fb502cf64924ff2530fa144bdf52c5.tar.gz xxv-bcbf441e09fb502cf64924ff2530fa144bdf52c5.tar.bz2 | |
* Move files to trunk
Diffstat (limited to 'lib/Template')
85 files changed, 45026 insertions, 0 deletions
diff --git a/lib/Template/Base.pm b/lib/Template/Base.pm new file mode 100644 index 0000000..5f19d78 --- /dev/null +++ b/lib/Template/Base.pm @@ -0,0 +1,314 @@ +#============================================================= -*-perl-*- +# +# Template::Base +# +# DESCRIPTION +# Base class module implementing common functionality for various other +# Template Toolkit modules. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#------------------------------------------------------------------------ +# +# $Id: Base.pm,v 2.69 2004/01/13 16:19:09 abw Exp $ +# +#======================================================================== + +package Template::Base; + +require 5.004; + +use strict; +use vars qw( $VERSION ); +use Template::Constants; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.69 $ =~ /(\d+)\.(\d+)/); + + +#------------------------------------------------------------------------ +# new(\%params) +# +# General purpose constructor method which expects a hash reference of +# configuration parameters, or a list of name => value pairs which are +# folded into a hash. Blesses a hash into an object and calls its +# _init() method, passing the parameter hash reference. Returns a new +# object derived from Template::Base, or undef on error. +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my ($argnames, @args, $arg, $cfg); +# $class->error(''); # always clear package $ERROR var? + + { no strict qw( refs ); + $argnames = \@{"$class\::BASEARGS"} || [ ]; + } + + # shift off all mandatory args, returning error if undefined or null + foreach $arg (@$argnames) { + return $class->error("no $arg specified") + unless ($cfg = shift); + push(@args, $cfg); + } + + # fold all remaining args into a hash, or use provided hash ref +# local $" = ', '; +# print STDERR "args: [@_]\n"; + $cfg = defined $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ }; + + my $self = bless { + map { ($_ => shift @args) } @$argnames, + _ERROR => '', + DEBUG => 0, + }, $class; + + return $self->_init($cfg) ? $self : $class->error($self->error); +} + + +#------------------------------------------------------------------------ +# error() +# error($msg, ...) +# +# May be called as a class or object method to set or retrieve the +# package variable $ERROR (class method) or internal member +# $self->{ _ERROR } (object method). The presence of parameters indicates +# that the error value should be set. Undef is then returned. In the +# abscence of parameters, the current error value is returned. +#------------------------------------------------------------------------ + +sub error { + my $self = shift; + my $errvar; + + { + no strict qw( refs ); + $errvar = ref $self ? \$self->{ _ERROR } : \${"$self\::ERROR"}; + } + if (@_) { + $$errvar = ref($_[0]) ? shift : join('', @_); + return undef; + } + else { + return $$errvar; + } +} + + +#------------------------------------------------------------------------ +# _init() +# +# Initialisation method called by the new() constructor and passing a +# reference to a hash array containing any configuration items specified +# as constructor arguments. Should return $self on success or undef on +# error, via a call to the error() method to set the error message. +#------------------------------------------------------------------------ + +sub _init { + my ($self, $config) = @_; + return $self; +} + + +sub DEBUG { + my $self = shift; + print STDERR "DEBUG: ", @_; +} + +sub debug { + my $self = shift; + my $msg = join('', @_); + my ($pkg, $file, $line) = caller(); + + unless ($msg =~ /\n$/) { + $msg .= ($self->{ DEBUG } & Template::Constants::DEBUG_CALLER) + ? " at $file line $line\n" + : "\n"; + } + + print STDERR "[$pkg] $msg"; +} + + +#------------------------------------------------------------------------ +# module_version() +# +# Returns the current version number. +#------------------------------------------------------------------------ + +sub module_version { + my $self = shift; + my $class = ref $self || $self; + no strict 'refs'; + return ${"${class}::VERSION"}; +} + + +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::Base - Base class module implementing common functionality + +=head1 SYNOPSIS + + package My::Module; + use base qw( Template::Base ); + + sub _init { + my ($self, $config) = @_; + $self->{ doodah } = $config->{ doodah } + || return $self->error("No 'doodah' specified"); + return $self; + } + + package main; + + my $object = My::Module->new({ doodah => 'foobar' }) + || die My::Module->error(); + +=head1 DESCRIPTION + +Base class module which implements a constructor and error reporting +functionality for various Template Toolkit modules. + +=head1 PUBLIC METHODS + +=head2 new(\%config) + +Constructor method which accepts a reference to a hash array or a list +of C<name =E<gt> value> parameters which are folded into a hash. The +_init() method is then called, passing the configuration hash and should +return true/false to indicate success or failure. A new object reference +is returned, or undef on error. Any error message raised can be examined +via the error() class method or directly via the package variable ERROR +in the derived class. + + my $module = My::Module->new({ ... }) + || die My::Module->error(), "\n"; + + my $module = My::Module->new({ ... }) + || die "constructor error: $My::Module::ERROR\n"; + +=head2 error($msg, ...) + +May be called as an object method to get/set the internal _ERROR member +or as a class method to get/set the $ERROR variable in the derived class's +package. + + my $module = My::Module->new({ ... }) + || die My::Module->error(), "\n"; + + $module->do_something() + || die $module->error(), "\n"; + +When called with parameters (multiple params are concatenated), this +method will set the relevant variable and return undef. This is most +often used within object methods to report errors to the caller. + + package My::Module; + + sub foobar { + my $self = shift; + + # some other code... + + return $self->error('some kind of error...') + if $some_condition; + } + +=head2 debug($msg, ...) + +Generates a debugging message by concatenating all arguments +passed into a string and printing it to STDERR. A prefix is +added to indicate the module of the caller. + + package My::Module; + + sub foobar { + my $self = shift; + + $self->debug('called foobar()'); + + # some other code... + } + +When the foobar() method is called, the following message +is sent to STDERR: + + [My::Module] called foobar() + +Objects can set an internal DEBUG value which the debug() +method will examine. If this value sets the relevant bits +to indicate DEBUG_CALLER then the file and line number of +the caller will be appened to the message. + + use Template::Constants qw( :debug ); + + my $module = My::Module->new({ + DEBUG => DEBUG_SERVICE | DEBUG_CONTEXT | DEBUG_CALLER, + }); + + $module->foobar(); + +This generates an error message such as: + + [My::Module] called foobar() at My/Module.pm line 6 + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.69, 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|Template> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Config.pm b/lib/Template/Config.pm new file mode 100644 index 0000000..9a3f378 --- /dev/null +++ b/lib/Template/Config.pm @@ -0,0 +1,467 @@ +#============================================================= -*-perl-*- +# +# Template::Config +# +# DESCRIPTION +# Template Toolkit configuration module. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#------------------------------------------------------------------------ +# +# $Id: Config.pm,v 2.67 2004/01/13 16:19:10 abw Exp $ +# +#======================================================================== + +package Template::Config; + +require 5.004; + +use strict; +use base qw( Template::Base ); +use vars qw( $VERSION $DEBUG $ERROR $INSTDIR + $PARSER $PROVIDER $PLUGINS $FILTERS $ITERATOR + $LATEX_PATH $PDFLATEX_PATH $DVIPS_PATH + $STASH $SERVICE $CONTEXT $CONSTANTS @PRELOAD ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.67 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0 unless defined $DEBUG; +$ERROR = ''; +$CONTEXT = 'Template::Context'; +$FILTERS = 'Template::Filters'; +$ITERATOR = 'Template::Iterator'; +$PARSER = 'Template::Parser'; +$PLUGINS = 'Template::Plugins'; +$PROVIDER = 'Template::Provider'; +$SERVICE = 'Template::Service'; +$STASH = 'Template::Stash'; +$CONSTANTS = 'Template::Namespace::Constants'; + +@PRELOAD = ( $CONTEXT, $FILTERS, $ITERATOR, $PARSER, + $PLUGINS, $PROVIDER, $SERVICE, $STASH ); + +# the following is set at installation time by the Makefile.PL +$INSTDIR = ''; + +# LaTeX executable paths set at installation time by the Makefile.PL +# Empty strings cause the latex(pdf|dvi|ps) filters to throw an error. +$LATEX_PATH = ''; +$PDFLATEX_PATH = ''; +$DVIPS_PATH = ''; + +#======================================================================== +# --- CLASS METHODS --- +#======================================================================== + +#------------------------------------------------------------------------ +# preload($module, $module, ...) +# +# Preloads all the standard TT modules that are likely to be used, along +# with any other passed as arguments. +#------------------------------------------------------------------------ + +sub preload { + my $class = shift; + + foreach my $module (@PRELOAD, @_) { + $class->load($module) || return; + }; + return 1; +} + + +#------------------------------------------------------------------------ +# load($module) +# +# Load a module via require(). Any occurences of '::' in the module name +# are be converted to '/' and '.pm' is appended. Returns 1 on success +# or undef on error. Use $class->error() to examine the error string. +#------------------------------------------------------------------------ + +sub load { + my ($class, $module) = @_; + $module =~ s[::][/]g; + $module .= '.pm'; +# print STDERR "loading $module\n" +# if $DEBUG; + eval { + require $module; + }; + return $@ ? $class->error("failed to load $module: $@") : 1; +} + + +#------------------------------------------------------------------------ +# parser(\%params) +# +# Instantiate a new parser object of the class whose name is denoted by +# the package variable $PARSER (default: Template::Parser). Returns +# a reference to a newly instantiated parser object or undef on error. +# The class error() method can be called without arguments to examine +# the error message generated by this failure. +#------------------------------------------------------------------------ + +sub parser { + my $class = shift; + my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') + ? shift : { @_ }; + + return undef unless $class->load($PARSER); + return $PARSER->new($params) + || $class->error("failed to create parser: ", $PARSER->error); +} + + +#------------------------------------------------------------------------ +# provider(\%params) +# +# Instantiate a new template provider object (default: Template::Provider). +# Returns an object reference or undef on error, as above. +#------------------------------------------------------------------------ + +sub provider { + my $class = shift; + my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') + ? shift : { @_ }; + + return undef unless $class->load($PROVIDER); + return $PROVIDER->new($params) + || $class->error("failed to create template provider: ", + $PROVIDER->error); +} + + +#------------------------------------------------------------------------ +# plugins(\%params) +# +# Instantiate a new plugins provider object (default: Template::Plugins). +# Returns an object reference or undef on error, as above. +#------------------------------------------------------------------------ + +sub plugins { + my $class = shift; + my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') + ? shift : { @_ }; + + return undef unless $class->load($PLUGINS); + return $PLUGINS->new($params) + || $class->error("failed to create plugin provider: ", + $PLUGINS->error); +} + + +#------------------------------------------------------------------------ +# filters(\%params) +# +# Instantiate a new filters provider object (default: Template::Filters). +# Returns an object reference or undef on error, as above. +#------------------------------------------------------------------------ + +sub filters { + my $class = shift; + my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') + ? shift : { @_ }; + + return undef unless $class->load($FILTERS); + return $FILTERS->new($params) + || $class->error("failed to create filter provider: ", + $FILTERS->error); +} + + +#------------------------------------------------------------------------ +# iterator(\@list) +# +# Instantiate a new Template::Iterator object (default: Template::Iterator). +# Returns an object reference or undef on error, as above. +#------------------------------------------------------------------------ + +sub iterator { + my $class = shift; + my $list = shift; + + return undef unless $class->load($ITERATOR); + return $ITERATOR->new($list, @_) + || $class->error("failed to create iterator: ", $ITERATOR->error); +} + + +#------------------------------------------------------------------------ +# stash(\%vars) +# +# Instantiate a new template variable stash object (default: +# Template::Stash). Returns object or undef, as above. +#------------------------------------------------------------------------ + +sub stash { + my $class = shift; + my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') + ? shift : { @_ }; + + return undef unless $class->load($STASH); + return $STASH->new($params) + || $class->error("failed to create stash: ", $STASH->error); +} + + +#------------------------------------------------------------------------ +# context(\%params) +# +# Instantiate a new template context object (default: Template::Context). +# Returns object or undef, as above. +#------------------------------------------------------------------------ + +sub context { + my $class = shift; + my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') + ? shift : { @_ }; + + return undef unless $class->load($CONTEXT); + return $CONTEXT->new($params) + || $class->error("failed to create context: ", $CONTEXT->error); +} + + +#------------------------------------------------------------------------ +# service(\%params) +# +# Instantiate a new template context object (default: Template::Service). +# Returns object or undef, as above. +#------------------------------------------------------------------------ + +sub service { + my $class = shift; + my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') + ? shift : { @_ }; + + return undef unless $class->load($SERVICE); + return $SERVICE->new($params) + || $class->error("failed to create context: ", $SERVICE->error); +} + + +#------------------------------------------------------------------------ +# constants(\%params) +# +# Instantiate a new namespace handler for compile time constant folding +# (default: Template::Namespace::Constants). +# Returns object or undef, as above. +#------------------------------------------------------------------------ + +sub constants { + my $class = shift; + my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH') + ? shift : { @_ }; + + return undef unless $class->load($CONSTANTS); + return $CONSTANTS->new($params) + || $class->error("failed to create constants namespace: ", + $CONSTANTS->error); +} + + +#------------------------------------------------------------------------ +# instdir($dir) +# +# Returns the root installation directory appended with any local +# component directory passed as an argument. +#------------------------------------------------------------------------ + +sub instdir { + my ($class, $dir) = @_; + my $inst = $INSTDIR + || return $class->error("no installation directory"); + $inst =~ s[/$][]g; + $inst .= "/$dir" if $dir; + return $inst; +} + +#------------------------------------------------------------------------ +# latexpaths() +# +# Returns a reference to a three element array: +# [latex_path, pdf2latex_path, dvips_path] +# These values are determined by Makefile.PL at installation time +# and are used by the latex(pdf|dvi|ps) filters. +#------------------------------------------------------------------------ + +sub latexpaths { + return [$LATEX_PATH, $PDFLATEX_PATH, $DVIPS_PATH]; +} + +#======================================================================== +# This should probably be moved somewhere else in the long term, but for +# now it ensures that Template::TieString is available even if the +# Template::Directive module hasn't been loaded, as is the case when +# using compiled templates and Template::Parser hasn't yet been loaded +# on demand. +#======================================================================== + +#------------------------------------------------------------------------ +# simple package for tying $output variable to STDOUT, used by perl() +#------------------------------------------------------------------------ + +package Template::TieString; + +sub TIEHANDLE { + my ($class, $textref) = @_; + bless $textref, $class; +} +sub PRINT { + my $self = shift; + $$self .= join('', @_); +} + + + +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::Config - Factory module for instantiating other TT2 modules + +=head1 SYNOPSIS + + use Template::Config; + +=head1 DESCRIPTION + +This module implements various methods for loading and instantiating +other modules that comprise the Template Toolkit. It provides a consistent +way to create toolkit components and allows custom modules to be used in +place of the regular ones. + +Package variables such as $STASH, $SERVICE, $CONTEXT, etc., contain +the default module/package name for each component (Template::Stash, +Template::Service and Template::Context, respectively) and are used by +the various factory methods (stash(), service() and context()) to load +the appropriate module. Changing these package variables will cause +subsequent calls to the relevant factory method to load and instantiate +an object from the new class. + +=head1 PUBLIC METHODS + +=head2 load($module) + +Load a module via require(). Any occurences of '::' in the module name +are be converted to '/' and '.pm' is appended. Returns 1 on success +or undef on error. Use $class-E<gt>error() to examine the error string. + +=head2 preload() + +This method preloads all the other Template::* modules that are likely +to be used. It is called by the Template module when running under +mod_perl ($ENV{MOD_PERL} is set). + +=head2 parser(\%config) + +Instantiate a new parser object of the class whose name is denoted by +the package variable $PARSER (default: Template::Parser). Returns +a reference to a newly instantiated parser object or undef on error. + +=head2 provider(\%config) + +Instantiate a new template provider object (default: Template::Provider). +Returns an object reference or undef on error, as above. + +=head2 plugins(\%config) + +Instantiate a new plugins provider object (default: Template::Plugins). +Returns an object reference or undef on error, as above. + +=head2 filters(\%config) + +Instantiate a new filter provider object (default: Template::Filters). +Returns an object reference or undef on error, as above. + +=head2 stash(\%vars) + +Instantiate a new stash object (Template::Stash or Template::Stash::XS +depending on the default set at installation time) using the contents +of the optional hash array passed by parameter as initial variable +definitions. Returns an object reference or undef on error, as above. + +=head2 context(\%config) + +Instantiate a new template context object (default: Template::Context). +Returns an object reference or undef on error, as above. + +=head2 service(\%config) + +Instantiate a new template service object (default: Template::Service). +Returns an object reference or undef on error, as above. + +=head2 instdir($dir) + +Returns the root directory of the Template Toolkit installation under +which optional components are installed. Any relative directory specified +as an argument will be appended to the returned directory. + + # e.g. returns '/usr/local/tt2' + my $ttroot = Template::Config->instdir() + || die "$Template::Config::ERROR\n"; + + # e.g. returns '/usr/local/tt2/templates' + my $template = Template::Config->instdir('templates') + || die "$Template::Config::ERROR\n"; + +Returns undef and sets $Template::Config::ERROR appropriately if the +optional components of the Template Toolkit have not been installed. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.67, 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|Template> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Constants.pm b/lib/Template/Constants.pm new file mode 100644 index 0000000..b227467 --- /dev/null +++ b/lib/Template/Constants.pm @@ -0,0 +1,287 @@ +#============================================================= -*-Perl-*- +# +# Template::Constants.pm +# +# DESCRIPTION +# Definition of constants for the Template Toolkit. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Constants.pm,v 2.67 2004/01/13 16:19:10 abw Exp $ +# +#============================================================================ + +package Template::Constants; + +require 5.004; +require Exporter; + +use strict; +use vars qw( $VERSION @ISA @EXPORT_OK %EXPORT_TAGS ); +use vars qw( $DEBUG_OPTIONS @STATUS @ERROR @CHOMP @DEBUG); + +@ISA = qw( Exporter ); +$VERSION = sprintf("%d.%02d", q$Revision: 2.67 $ =~ /(\d+)\.(\d+)/); + + +#======================================================================== +# ----- EXPORTER ----- +#======================================================================== + +# STATUS constants returned by directives +use constant STATUS_OK => 0; # ok +use constant STATUS_RETURN => 1; # ok, block ended by RETURN +use constant STATUS_STOP => 2; # ok, stoppped by STOP +use constant STATUS_DONE => 3; # ok, iterator done +use constant STATUS_DECLINED => 4; # ok, declined to service request +use constant STATUS_ERROR => 255; # error condition + +# ERROR constants for indicating exception types +use constant ERROR_RETURN => 'return'; # return a status code +use constant ERROR_FILE => 'file'; # file error: I/O, parse, recursion +use constant ERROR_VIEW => 'view'; # view error +use constant ERROR_UNDEF => 'undef'; # undefined variable value used +use constant ERROR_PERL => 'perl'; # error in [% PERL %] block +use constant ERROR_FILTER => 'filter'; # filter error +use constant ERROR_PLUGIN => 'plugin'; # plugin error + +# CHOMP constants for PRE_CHOMP and POST_CHOMP +use constant CHOMP_NONE => 0; # do not remove whitespace +use constant CHOMP_ALL => 1; # remove whitespace +use constant CHOMP_COLLAPSE => 2; # collapse whitespace to a single space + +# DEBUG constants to enable various debugging options +use constant DEBUG_OFF => 0; # do nothing +use constant DEBUG_ON => 1; # basic debugging flag +use constant DEBUG_UNDEF => 2; # throw undef on undefined variables +use constant DEBUG_VARS => 4; # general variable debugging +use constant DEBUG_DIRS => 8; # directive debugging +use constant DEBUG_STASH => 16; # general stash debugging +use constant DEBUG_CONTEXT => 32; # context debugging +use constant DEBUG_PARSER => 64; # parser debugging +use constant DEBUG_PROVIDER => 128; # provider debugging +use constant DEBUG_PLUGINS => 256; # plugins debugging +use constant DEBUG_FILTERS => 512; # filters debugging +use constant DEBUG_SERVICE => 1024; # context debugging +use constant DEBUG_ALL => 2047; # everything + +# extra debugging flags +use constant DEBUG_CALLER => 4096; # add caller file/line +use constant DEBUG_FLAGS => 4096; # bitmask to extraxt flags + +$DEBUG_OPTIONS = { + &DEBUG_OFF => off => off => &DEBUG_OFF, + &DEBUG_ON => on => on => &DEBUG_ON, + &DEBUG_UNDEF => undef => undef => &DEBUG_UNDEF, + &DEBUG_VARS => vars => vars => &DEBUG_VARS, + &DEBUG_DIRS => dirs => dirs => &DEBUG_DIRS, + &DEBUG_STASH => stash => stash => &DEBUG_STASH, + &DEBUG_CONTEXT => context => context => &DEBUG_CONTEXT, + &DEBUG_PARSER => parser => parser => &DEBUG_PARSER, + &DEBUG_PROVIDER => provider => provider => &DEBUG_PROVIDER, + &DEBUG_PLUGINS => plugins => plugins => &DEBUG_PLUGINS, + &DEBUG_FILTERS => filters => filters => &DEBUG_FILTERS, + &DEBUG_SERVICE => service => service => &DEBUG_SERVICE, + &DEBUG_ALL => all => all => &DEBUG_ALL, + &DEBUG_CALLER => caller => caller => &DEBUG_CALLER, +}; + +@STATUS = qw( STATUS_OK STATUS_RETURN STATUS_STOP STATUS_DONE + STATUS_DECLINED STATUS_ERROR ); +@ERROR = qw( ERROR_FILE ERROR_VIEW ERROR_UNDEF ERROR_PERL + ERROR_RETURN ERROR_FILTER ERROR_PLUGIN ); +@CHOMP = qw( CHOMP_NONE CHOMP_ALL CHOMP_COLLAPSE ); +@DEBUG = qw( DEBUG_OFF DEBUG_ON DEBUG_UNDEF DEBUG_VARS + DEBUG_DIRS DEBUG_STASH DEBUG_CONTEXT DEBUG_PARSER + DEBUG_PROVIDER DEBUG_PLUGINS DEBUG_FILTERS DEBUG_SERVICE + DEBUG_ALL DEBUG_CALLER DEBUG_FLAGS ); + +@EXPORT_OK = ( @STATUS, @ERROR, @CHOMP, @DEBUG ); +%EXPORT_TAGS = ( + 'all' => [ @EXPORT_OK ], + 'status' => [ @STATUS ], + 'error' => [ @ERROR ], + 'chomp' => [ @CHOMP ], + 'debug' => [ @DEBUG ], +); + + +sub debug_flags { + my ($self, $debug) = @_; + my (@flags, $flag, $value); + $debug = $self unless defined($debug) || ref($self); + + if ($debug =~ /^\d+$/) { + foreach $flag (@DEBUG) { + next if $flag =~ /^DEBUG_(OFF|ALL|FLAGS)$/; + + # don't trash the original + my $copy = $flag; + $flag =~ s/^DEBUG_//; + $flag = lc $flag; + return $self->error("no value for flag: $flag") + unless defined($value = $DEBUG_OPTIONS->{ $flag }); + $flag = $value; + + if ($debug & $flag) { + $value = $DEBUG_OPTIONS->{ $flag }; + return $self->error("no value for flag: $flag") unless defined $value; + push(@flags, $value); + } + } + return wantarray ? @flags : join(', ', @flags); + } + else { + @flags = split(/\W+/, $debug); + $debug = 0; + foreach $flag (@flags) { + $value = $DEBUG_OPTIONS->{ $flag }; + return $self->error("unknown debug flag: $flag") unless defined $value; + $debug |= $value; + } + return $debug; + } +} + + +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::Constants - Defines constants for the Template Toolkit + +=head1 SYNOPSIS + + use Template::Constants qw( :status :error :all ); + +=head1 DESCRIPTION + +The Template::Constants modules defines, and optionally exports into the +caller's namespace, a number of constants used by the Template package. + +Constants may be used by specifying the Template::Constants package +explicitly: + + use Template::Constants; + + print Template::Constants::STATUS_DECLINED; + +Constants may be imported into the caller's namespace by naming them as +options to the C<use Template::Constants> statement: + + use Template::Constants qw( STATUS_DECLINED ); + + print STATUS_DECLINED; + +Alternatively, one of the following tagset identifiers may be specified +to import sets of constants; :status, :error, :all. + + use Template::Constants qw( :status ); + + print STATUS_DECLINED; + +See L<Exporter> for more information on exporting variables. + +=head1 EXPORTABLE TAG SETS + +The following tag sets and associated constants are defined: + + :status + STATUS_OK # no problem, continue + STATUS_RETURN # ended current block then continue (ok) + STATUS_STOP # controlled stop (ok) + STATUS_DONE # iterator is all done (ok) + STATUS_DECLINED # provider declined to service request (ok) + STATUS_ERROR # general error condition (not ok) + + :error + ERROR_RETURN # return a status code (e.g. 'stop') + ERROR_FILE # file error: I/O, parse, recursion + ERROR_UNDEF # undefined variable value used + ERROR_PERL # error in [% PERL %] block + ERROR_FILTER # filter error + ERROR_PLUGIN # plugin error + + :chomp # for PRE_CHOMP and POST_CHOMP + CHOMP_NONE # do not remove whitespace + CHOMP_ALL # remove whitespace + CHOMP_COLLAPSE # collapse whitespace to a single space + + :debug + DEBUG_OFF # do nothing + DEBUG_ON # basic debugging flag + DEBUG_UNDEF # throw undef on undefined variables + DEBUG_VARS # general variable debugging + DEBUG_DIRS # directive debugging + DEBUG_STASH # general stash debugging + DEBUG_CONTEXT # context debugging + DEBUG_PARSER # parser debugging + DEBUG_PROVIDER # provider debugging + DEBUG_PLUGINS # plugins debugging + DEBUG_FILTERS # filters debugging + DEBUG_SERVICE # context debugging + DEBUG_ALL # everything + DEBUG_CALLER # add caller file/line info + DEBUG_FLAGS # bitmap used internally + + :all All the above constants. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.67, 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|Template>, L<Exporter|Exporter> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Context.pm b/lib/Template/Context.pm new file mode 100644 index 0000000..cdc699e --- /dev/null +++ b/lib/Template/Context.pm @@ -0,0 +1,1570 @@ +#============================================================= -*-Perl-*- +# +# Template::Context +# +# DESCRIPTION +# Module defining a context in which a template document is processed. +# This is the runtime processing interface through which templates +# can access the functionality of the Template Toolkit. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +# REVISION +# $Id: Context.pm,v 2.89 2004/01/30 18:37:47 abw Exp $ +# +#============================================================================ + +package Template::Context; + +require 5.004; + +use strict; +use vars qw( $VERSION $DEBUG $AUTOLOAD $DEBUG_FORMAT ); +use base qw( Template::Base ); + +use Template::Base; +use Template::Config; +use Template::Constants; +use Template::Exception; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.89 $ =~ /(\d+)\.(\d+)/); +$DEBUG_FORMAT = "\n## \$file line \$line : [% \$text %] ##\n"; + + +#======================================================================== +# ----- PUBLIC METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# template($name) +# +# General purpose method to fetch a template and return it in compiled +# form. In the usual case, the $name parameter will be a simple string +# containing the name of a template (e.g. 'header'). It may also be +# a reference to Template::Document object (or sub-class) or a Perl +# sub-routine. These are considered to be compiled templates and are +# returned intact. Finally, it may be a reference to any other kind +# of valid input source accepted by Template::Provider (e.g. scalar +# ref, glob, IO handle, etc). +# +# Templates may be cached at one of 3 different levels. The internal +# BLOCKS member is a local cache which holds references to all +# template blocks used or imported via PROCESS since the context's +# reset() method was last called. This is checked first and if the +# template is not found, the method then walks down the BLOCKSTACK +# list. This contains references to the block definition tables in +# any enclosing Template::Documents that we're visiting (e.g. we've +# been called via an INCLUDE and we want to access a BLOCK defined in +# the template that INCLUDE'd us). If nothing is defined, then we +# iterate through the LOAD_TEMPLATES providers list as a 'chain of +# responsibility' (see Design Patterns) asking each object to fetch() +# the template if it can. +# +# Returns the compiled template. On error, undef is returned and +# the internal ERROR value (read via error()) is set to contain an +# error message of the form "$name: $error". +#------------------------------------------------------------------------ + +sub template { + my ($self, $name) = @_; + my ($prefix, $blocks, $defblocks, $provider, $template, $error); + my ($shortname, $blockname, $providers); + + $self->debug("template($name)") if $self->{ DEBUG }; + + # references to Template::Document (or sub-class) objects objects, or + # CODE references are assumed to be pre-compiled templates and are + # returned intact + return $name + if UNIVERSAL::isa($name, 'Template::Document') + || ref($name) eq 'CODE'; + + $shortname = $name; + + unless (ref $name) { + + $self->debug("looking for block [$name]") if $self->{ DEBUG }; + + # we first look in the BLOCKS hash for a BLOCK that may have + # been imported from a template (via PROCESS) + return $template + if ($template = $self->{ BLOCKS }->{ $name }); + + # then we iterate through the BLKSTACK list to see if any of the + # Template::Documents we're visiting define this BLOCK + foreach $blocks (@{ $self->{ BLKSTACK } }) { + return $template + if $blocks && ($template = $blocks->{ $name }); + } + + # now it's time to ask the providers, so we look to see if any + # prefix is specified to indicate the desired provider set. + if ($^O eq 'MSWin32') { + # let C:/foo through + $prefix = $1 if $shortname =~ s/^(\w{2,})://o; + } + else { + $prefix = $1 if $shortname =~ s/^(\w+)://; + } + + if (defined $prefix) { + $providers = $self->{ PREFIX_MAP }->{ $prefix } + || return $self->throw( Template::Constants::ERROR_FILE, + "no providers for template prefix '$prefix'"); + } + } + $providers = $self->{ PREFIX_MAP }->{ default } + || $self->{ LOAD_TEMPLATES } + unless $providers; + + + # Finally we try the regular template providers which will + # handle references to files, text, etc., as well as templates + # reference by name. If + + $blockname = ''; + while ($shortname) { + $self->debug("asking providers for [$shortname] [$blockname]") + if $self->{ DEBUG }; + + foreach my $provider (@$providers) { + ($template, $error) = $provider->fetch($shortname, $prefix); + if ($error) { + if ($error == Template::Constants::STATUS_ERROR) { + # $template contains exception object + if (UNIVERSAL::isa($template, 'Template::Exception') + && $template->type() eq Template::Constants::ERROR_FILE) { + $self->throw($template); + } + else { + $self->throw( Template::Constants::ERROR_FILE, $template ); + } + } + # DECLINE is ok, carry on + } + elsif (length $blockname) { + return $template + if $template = $template->blocks->{ $blockname }; + } + else { + return $template; + } + } + + last if ref $shortname || ! $self->{ EXPOSE_BLOCKS }; + $shortname =~ s{/([^/]+)$}{} || last; + $blockname = length $blockname ? "$1/$blockname" : $1; + } + + $self->throw(Template::Constants::ERROR_FILE, "$name: not found"); +} + + +#------------------------------------------------------------------------ +# plugin($name, \@args) +# +# Calls on each of the LOAD_PLUGINS providers in turn to fetch() (i.e. load +# and instantiate) a plugin of the specified name. Additional parameters +# passed are propagated to the new() constructor for the plugin. +# Returns a reference to a new plugin object or other reference. On +# error, undef is returned and the appropriate error message is set for +# subsequent retrieval via error(). +#------------------------------------------------------------------------ + +sub plugin { + my ($self, $name, $args) = @_; + my ($provider, $plugin, $error); + + $self->debug("plugin($name, ", defined $args ? @$args : '[ ]', ')') + if $self->{ DEBUG }; + + # request the named plugin from each of the LOAD_PLUGINS providers in turn + foreach my $provider (@{ $self->{ LOAD_PLUGINS } }) { + ($plugin, $error) = $provider->fetch($name, $args, $self); + return $plugin unless $error; + if ($error == Template::Constants::STATUS_ERROR) { + $self->throw($plugin) if ref $plugin; + $self->throw(Template::Constants::ERROR_PLUGIN, $plugin); + } + } + + $self->throw(Template::Constants::ERROR_PLUGIN, "$name: plugin not found"); +} + + +#------------------------------------------------------------------------ +# filter($name, \@args, $alias) +# +# Similar to plugin() above, but querying the LOAD_FILTERS providers to +# return filter instances. An alias may be provided which is used to +# save the returned filter in a local cache. +#------------------------------------------------------------------------ + +sub filter { + my ($self, $name, $args, $alias) = @_; + my ($provider, $filter, $error); + + $self->debug("filter($name, ", + defined $args ? @$args : '[ ]', + defined $alias ? $alias : '<no alias>', ')') + if $self->{ DEBUG }; + + # use any cached version of the filter if no params provided + return $filter + if ! $args && ! ref $name + && ($filter = $self->{ FILTER_CACHE }->{ $name }); + + # request the named filter from each of the FILTERS providers in turn + foreach my $provider (@{ $self->{ LOAD_FILTERS } }) { + ($filter, $error) = $provider->fetch($name, $args, $self); + last unless $error; + if ($error == Template::Constants::STATUS_ERROR) { + $self->throw($filter) if ref $filter; + $self->throw(Template::Constants::ERROR_FILTER, $filter); + } + # return $self->error($filter) + # if $error == &Template::Constants::STATUS_ERROR; + } + + return $self->error("$name: filter not found") + unless $filter; + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # commented out by abw on 19 Nov 2001 to fix problem with xmlstyle + # plugin which may re-define a filter by calling define_filter() + # multiple times. With the automatic aliasing/caching below, any + # new filter definition isn't seen. Don't think this will cause + # any problems as filters explicitly supplied with aliases will + # still work as expected. + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # alias defaults to name if undefined + # $alias = $name + # unless defined($alias) or ref($name) or $args; + + # cache FILTER if alias is valid + $self->{ FILTER_CACHE }->{ $alias } = $filter + if $alias; + + return $filter; +} + + +#------------------------------------------------------------------------ +# view(\%config) +# +# Create a new Template::View bound to this context. +#------------------------------------------------------------------------ + +sub view { + my $self = shift; + require Template::View; + return Template::View->new($self, @_) + || $self->throw(&Template::Constants::ERROR_VIEW, + $Template::View::ERROR); +} + + +#------------------------------------------------------------------------ +# process($template, \%params) [% PROCESS template var=val ... %] +# process($template, \%params, $local) [% INCLUDE template var=val ... %] +# +# Processes the template named or referenced by the first parameter. +# The optional second parameter may reference a hash array of variable +# definitions. These are set before the template is processed by +# calling update() on the stash. Note that, unless the third parameter +# is true, the context is not localised and these, and any other +# variables set in the template will retain their new values after this +# method returns. The third parameter is in place so that this method +# can handle INCLUDE calls: the stash will be localized. +# +# Returns the output of processing the template. Errors are thrown +# as Template::Exception objects via die(). +#------------------------------------------------------------------------ + +sub process { + my ($self, $template, $params, $localize) = @_; + my ($trim, $blocks) = @$self{ qw( TRIM BLOCKS ) }; + my (@compiled, $name, $compiled); + my ($stash, $component, $tblocks, $error, $tmpout); + my $output = ''; + + $template = [ $template ] unless ref $template eq 'ARRAY'; + + $self->debug("process([ ", join(', '), @$template, ' ], ', + defined $params ? $params : '<no params>', ', ', + $localize ? '<localized>' : '<unlocalized>', ')') + if $self->{ DEBUG }; + + # fetch compiled template for each name specified + foreach $name (@$template) { + push(@compiled, $self->template($name)); + } + + if ($localize) { + # localise the variable stash with any parameters passed + $stash = $self->{ STASH } = $self->{ STASH }->clone($params); + } else { + # update stash with any new parameters passed + $self->{ STASH }->update($params); + $stash = $self->{ STASH }; + } + + eval { + # save current component + eval { $component = $stash->get('component') }; + + foreach $name (@$template) { + $compiled = shift @compiled; + my $element = ref $compiled eq 'CODE' + ? { (name => (ref $name ? '' : $name), modtime => time()) } + : $compiled; + + if (UNIVERSAL::isa($component, 'Template::Document')) { + $element->{ caller } = $component->{ name }; + $element->{ callers } = $component->{ callers } || []; + push(@{$element->{ callers }}, $element->{ caller }); + } + + $stash->set('component', $element); + + unless ($localize) { + # merge any local blocks defined in the Template::Document + # into our local BLOCKS cache + @$blocks{ keys %$tblocks } = values %$tblocks + if UNIVERSAL::isa($compiled, 'Template::Document') + && ($tblocks = $compiled->blocks()); + } + + if (ref $compiled eq 'CODE') { + $tmpout = &$compiled($self); + } + elsif (ref $compiled) { + $tmpout = $compiled->process($self); + } + else { + $self->throw('file', + "invalid template reference: $compiled"); + } + + if ($trim) { + for ($tmpout) { + s/^\s+//; + s/\s+$//; + } + } + $output .= $tmpout; + } + $stash->set('component', $component); + }; + $error = $@; + + if ($localize) { + # ensure stash is delocalised before dying + $self->{ STASH } = $self->{ STASH }->declone(); + } + + $self->throw(ref $error + ? $error : (Template::Constants::ERROR_FILE, $error)) + if $error; + + return $output; +} + + +#------------------------------------------------------------------------ +# include($template, \%params) [% INCLUDE template var = val, ... %] +# +# Similar to process() above but processing the template in a local +# context. Any variables passed by reference to a hash as the second +# parameter will be set before the template is processed and then +# revert to their original values before the method returns. Similarly, +# any changes made to non-global variables within the template will +# persist only until the template is processed. +# +# Returns the output of processing the template. Errors are thrown +# as Template::Exception objects via die(). +#------------------------------------------------------------------------ + +sub include { + my ($self, $template, $params) = @_; + return $self->process($template, $params, 'localize me!'); +} + +#------------------------------------------------------------------------ +# insert($file) +# +# Insert the contents of a file without parsing. +#------------------------------------------------------------------------ + +sub insert { + my ($self, $file) = @_; + my ($prefix, $providers, $text, $error); + my $output = ''; + + my $files = ref $file eq 'ARRAY' ? $file : [ $file ]; + + $self->debug("insert([ ", join(', '), @$files, " ])") + if $self->{ DEBUG }; + + + FILE: foreach $file (@$files) { + my $name = $file; + + if ($^O eq 'MSWin32') { + # let C:/foo through + $prefix = $1 if $name =~ s/^(\w{2,})://o; + } + else { + $prefix = $1 if $name =~ s/^(\w+)://; + } + + if (defined $prefix) { + $providers = $self->{ PREFIX_MAP }->{ $prefix } + || return $self->throw(Template::Constants::ERROR_FILE, + "no providers for file prefix '$prefix'"); + } + else { + $providers = $self->{ PREFIX_MAP }->{ default } + || $self->{ LOAD_TEMPLATES }; + } + + foreach my $provider (@$providers) { + ($text, $error) = $provider->load($name, $prefix); + next FILE unless $error; + if ($error == Template::Constants::STATUS_ERROR) { + $self->throw($text) if ref $text; + $self->throw(Template::Constants::ERROR_FILE, $text); + } + } + $self->throw(Template::Constants::ERROR_FILE, "$file: not found"); + } + continue { + $output .= $text; + } + return $output; +} + + +#------------------------------------------------------------------------ +# throw($type, $info, \$output) [% THROW errtype "Error info" %] +# +# Throws a Template::Exception object by calling die(). This method +# may be passed a reference to an existing Template::Exception object; +# a single value containing an error message which is used to +# instantiate a Template::Exception of type 'undef'; or a pair of +# values representing the exception type and info from which a +# Template::Exception object is instantiated. e.g. +# +# $context->throw($exception); +# $context->throw("I'm sorry Dave, I can't do that"); +# $context->throw('denied', "I'm sorry Dave, I can't do that"); +# +# An optional third parameter can be supplied in the last case which +# is a reference to the current output buffer containing the results +# of processing the template up to the point at which the exception +# was thrown. The RETURN and STOP directives, for example, use this +# to propagate output back to the user, but it can safely be ignored +# in most cases. +# +# This method rides on a one-way ticket to die() oblivion. It does not +# return in any real sense of the word, but should get caught by a +# surrounding eval { } block (e.g. a BLOCK or TRY) and handled +# accordingly, or returned to the caller as an uncaught exception. +#------------------------------------------------------------------------ + +sub throw { + my ($self, $error, $info, $output) = @_; + local $" = ', '; + + # die! die! die! + if (UNIVERSAL::isa($error, 'Template::Exception')) { + die $error; + } + elsif (defined $info) { + die (Template::Exception->new($error, $info, $output)); + } + else { + $error ||= ''; + die (Template::Exception->new('undef', $error, $output)); + } + + # not reached +} + + +#------------------------------------------------------------------------ +# catch($error, \$output) +# +# Called by various directives after catching an error thrown via die() +# from within an eval { } block. The first parameter contains the errror +# which may be a sanitized reference to a Template::Exception object +# (such as that raised by the throw() method above, a plugin object, +# and so on) or an error message thrown via die from somewhere in user +# code. The latter are coerced into 'undef' Template::Exception objects. +# Like throw() above, a reference to a scalar may be passed as an +# additional parameter to represent the current output buffer +# localised within the eval block. As exceptions are thrown upwards +# and outwards from nested blocks, the catch() method reconstructs the +# correct output buffer from these fragments, storing it in the +# exception object for passing further onwards and upwards. +# +# Returns a reference to a Template::Exception object.. +#------------------------------------------------------------------------ + +sub catch { + my ($self, $error, $output) = @_; + + if (UNIVERSAL::isa($error, 'Template::Exception')) { + $error->text($output) if $output; + return $error; + } + else { + return Template::Exception->new('undef', $error, $output); + } +} + + +#------------------------------------------------------------------------ +# localise(\%params) +# delocalise() +# +# The localise() method creates a local copy of the current stash, +# allowing the existing state of variables to be saved and later +# restored via delocalise(). +# +# A reference to a hash array may be passed containing local variable +# definitions which should be added to the cloned namespace. These +# values persist until delocalisation. +#------------------------------------------------------------------------ + +sub localise { + my $self = shift; + $self->{ STASH } = $self->{ STASH }->clone(@_); +} + +sub delocalise { + my $self = shift; + $self->{ STASH } = $self->{ STASH }->declone(); +} + + +#------------------------------------------------------------------------ +# visit($document, $blocks) +# +# Each Template::Document calls the visit() method on the context +# before processing itself. It passes a reference to the hash array +# of named BLOCKs defined within the document, allowing them to be +# added to the internal BLKSTACK list which is subsequently used by +# template() to resolve templates. +# from a provider. +#------------------------------------------------------------------------ + +sub visit { + my ($self, $document, $blocks) = @_; + unshift(@{ $self->{ BLKSTACK } }, $blocks) +} + + +#------------------------------------------------------------------------ +# leave() +# +# The leave() method is called when the document has finished +# processing itself. This removes the entry from the BLKSTACK list +# that was added visit() above. For persistance of BLOCK definitions, +# the process() method (i.e. the PROCESS directive) does some extra +# magic to copy BLOCKs into a shared hash. +#------------------------------------------------------------------------ + +sub leave { + my $self = shift; + shift(@{ $self->{ BLKSTACK } }); +} + + +#------------------------------------------------------------------------ +# define_block($name, $block) +# +# Adds a new BLOCK definition to the local BLOCKS cache. $block may +# be specified as a reference to a sub-routine or Template::Document +# object or as text which is compiled into a template. Returns a true +# value (the $block reference or compiled block reference) if +# succesful or undef on failure. Call error() to retrieve the +# relevent error message (i.e. compilation failure). +#------------------------------------------------------------------------ + +sub define_block { + my ($self, $name, $block) = @_; + $block = $self->template(\$block) + || return undef + unless ref $block; + $self->{ BLOCKS }->{ $name } = $block; +} + + +#------------------------------------------------------------------------ +# define_filter($name, $filter, $is_dynamic) +# +# Adds a new FILTER definition to the local FILTER_CACHE. +#------------------------------------------------------------------------ + +sub define_filter { + my ($self, $name, $filter, $is_dynamic) = @_; + my ($result, $error); + $filter = [ $filter, 1 ] if $is_dynamic; + + foreach my $provider (@{ $self->{ LOAD_FILTERS } }) { + ($result, $error) = $provider->store($name, $filter); + return 1 unless $error; + $self->throw(&Template::Constants::ERROR_FILTER, $result) + if $error == &Template::Constants::STATUS_ERROR; + } + $self->throw(&Template::Constants::ERROR_FILTER, + "FILTER providers declined to store filter $name"); +} + + +#------------------------------------------------------------------------ +# reset() +# +# Reset the state of the internal BLOCKS hash to clear any BLOCK +# definitions imported via the PROCESS directive. Any original +# BLOCKS definitions passed to the constructor will be restored. +#------------------------------------------------------------------------ + +sub reset { + my ($self, $blocks) = @_; + $self->{ BLKSTACK } = [ ]; + $self->{ BLOCKS } = { %{ $self->{ INIT_BLOCKS } } }; +} + + +#------------------------------------------------------------------------ +# stash() +# +# Simple accessor methods to return the STASH values. This is likely +# to be called quite often so we provide a direct method rather than +# relying on the slower AUTOLOAD. +#------------------------------------------------------------------------ + +sub stash { + return $_[0]->{ STASH }; +} + + +#------------------------------------------------------------------------ +# define_vmethod($type, $name, \&sub) +# +# Passes $type, $name, and &sub on to stash->define_vmethod(). +#------------------------------------------------------------------------ +sub define_vmethod { + my $self = shift; + $self->stash->define_vmethod(@_); +} + + +#------------------------------------------------------------------------ +# debugging($command, @args, \%params) +# +# Method for controlling the debugging status of the context. The first +# argument can be 'on' or 'off' to enable/disable debugging, 'format' +# to define the format of the debug message, or 'msg' to generate a +# debugging message reporting the file, line, message text, etc., +# according to the current debug format. +#------------------------------------------------------------------------ + +sub debugging { + my $self = shift; + my $hash = ref $_[-1] eq 'HASH' ? pop : { }; + my @args = @_; + +# print "*** debug(@args)\n"; + if (@args) { + if ($args[0] =~ /^on|1$/i) { + $self->{ DEBUG_DIRS } = 1; + shift(@args); + } + elsif ($args[0] =~ /^off|0$/i) { + $self->{ DEBUG_DIRS } = 0; + shift(@args); + } + } + + if (@args) { + if ($args[0] =~ /^msg$/i) { + return unless $self->{ DEBUG_DIRS }; + my $format = $self->{ DEBUG_FORMAT }; + $format = $DEBUG_FORMAT unless defined $format; + $format =~ s/\$(\w+)/$hash->{ $1 }/ge; + return $format; + } + elsif ($args[0] =~ /^format$/i) { + $self->{ DEBUG_FORMAT } = $args[1]; + } + # else ignore + } + + return ''; +} + + +#------------------------------------------------------------------------ +# AUTOLOAD +# +# Provides pseudo-methods for read-only access to various internal +# members. For example, templates(), plugins(), filters(), +# eval_perl(), load_perl(), etc. These aren't called very often, or +# may never be called at all. +#------------------------------------------------------------------------ + +sub AUTOLOAD { + my $self = shift; + my $method = $AUTOLOAD; + my $result; + + $method =~ s/.*:://; + return if $method eq 'DESTROY'; + + warn "no such context method/member: $method\n" + unless defined ($result = $self->{ uc $method }); + + return $result; +} + + +#------------------------------------------------------------------------ +# DESTROY +# +# Stash may contain references back to the Context via macro closures, +# etc. This breaks the circular references. +#------------------------------------------------------------------------ + +sub DESTROY { + my $self = shift; + undef $self->{ STASH }; +} + + + +#======================================================================== +# -- PRIVATE METHODS -- +#======================================================================== + +#------------------------------------------------------------------------ +# _init(\%config) +# +# Initialisation method called by Template::Base::new() +#------------------------------------------------------------------------ + +sub _init { + my ($self, $config) = @_; + my ($name, $item, $method, $block, $blocks); + my @itemlut = ( + LOAD_TEMPLATES => 'provider', + LOAD_PLUGINS => 'plugins', + LOAD_FILTERS => 'filters' + ); + + # LOAD_TEMPLATE, LOAD_PLUGINS, LOAD_FILTERS - lists of providers + while (($name, $method) = splice(@itemlut, 0, 2)) { + $item = $config->{ $name } + || Template::Config->$method($config) + || return $self->error($Template::Config::ERROR); + $self->{ $name } = ref $item eq 'ARRAY' ? $item : [ $item ]; + } + + my $providers = $self->{ LOAD_TEMPLATES }; + my $prefix_map = $self->{ PREFIX_MAP } = $config->{ PREFIX_MAP } || { }; + while (my ($key, $val) = each %$prefix_map) { + $prefix_map->{ $key } = [ ref $val ? $val : + map { $providers->[$_] } + split(/\D+/, $val) ] + unless ref $val eq 'ARRAY'; +# print(STDERR "prefix $key => $val => [", +# join(', ', @{ $prefix_map->{ $key } }), "]\n"); + } + + # STASH + $self->{ STASH } = $config->{ STASH } || do { + my $predefs = $config->{ VARIABLES } + || $config->{ PRE_DEFINE } + || { }; + + # hack to get stash to know about debug mode + $predefs->{ _DEBUG } = ( ($config->{ DEBUG } || 0) + & &Template::Constants::DEBUG_UNDEF ) ? 1 : 0 + unless defined $predefs->{ _DEBUG }; + + Template::Config->stash($predefs) + || return $self->error($Template::Config::ERROR); + }; + + # compile any template BLOCKS specified as text + $blocks = $config->{ BLOCKS } || { }; + $self->{ INIT_BLOCKS } = $self->{ BLOCKS } = { + map { + $block = $blocks->{ $_ }; + $block = $self->template(\$block) + || return undef + unless ref $block; + ($_ => $block); + } + keys %$blocks + }; + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # RECURSION - flag indicating is recursion into templates is supported + # EVAL_PERL - flag indicating if PERL blocks should be processed + # TRIM - flag to remove leading and trailing whitespace from output + # BLKSTACK - list of hashes of BLOCKs defined in current template(s) + # CONFIG - original configuration hash + # EXPOSE_BLOCKS - make blocks visible as pseudo-files + # DEBUG_FORMAT - format for generating template runtime debugging messages + # DEBUG - format for generating template runtime debugging messages + + $self->{ RECURSION } = $config->{ RECURSION } || 0; + $self->{ EVAL_PERL } = $config->{ EVAL_PERL } || 0; + $self->{ TRIM } = $config->{ TRIM } || 0; + $self->{ BLKSTACK } = [ ]; + $self->{ CONFIG } = $config; + $self->{ EXPOSE_BLOCKS } = defined $config->{ EXPOSE_BLOCKS } + ? $config->{ EXPOSE_BLOCKS } + : 0; + + $self->{ DEBUG_FORMAT } = $config->{ DEBUG_FORMAT }; + $self->{ DEBUG_DIRS } = ($config->{ DEBUG } || 0) + & Template::Constants::DEBUG_DIRS; + $self->{ DEBUG } = defined $config->{ DEBUG } + ? $config->{ DEBUG } & ( Template::Constants::DEBUG_CONTEXT + | Template::Constants::DEBUG_FLAGS ) + : $DEBUG; + + return $self; +} + + +#------------------------------------------------------------------------ +# _dump() +# +# Debug method which returns a string representing the internal state +# of the context object. +#------------------------------------------------------------------------ + +sub _dump { + my $self = shift; + my $output = "[Template::Context] {\n"; + my $format = " %-16s => %s\n"; + my $key; + + foreach $key (qw( RECURSION EVAL_PERL TRIM )) { + $output .= sprintf($format, $key, $self->{ $key }); + } + foreach my $pname (qw( LOAD_TEMPLATES LOAD_PLUGINS LOAD_FILTERS )) { + my $provtext = "[\n"; + foreach my $prov (@{ $self->{ $pname } }) { + $provtext .= $prov->_dump(); +# $provtext .= ",\n"; + } + $provtext =~ s/\n/\n /g; + $provtext =~ s/\s+$//; + $provtext .= ",\n ]"; + $output .= sprintf($format, $pname, $provtext); + } + $output .= sprintf($format, STASH => $self->{ STASH }->_dump()); + $output .= '}'; + return $output; +} + + +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::Context - Runtime context in which templates are processed + +=head1 SYNOPSIS + + use Template::Context; + + # constructor + $context = Template::Context->new(\%config) + || die $Template::Context::ERROR; + + # fetch (load and compile) a template + $template = $context->template($template_name); + + # fetch (load and instantiate) a plugin object + $plugin = $context->plugin($name, \@args); + + # fetch (return or create) a filter subroutine + $filter = $context->filter($name, \@args, $alias); + + # process/include a template, errors are thrown via die() + $output = $context->process($template, \%vars); + $output = $context->include($template, \%vars); + + # raise an exception via die() + $context->throw($error_type, $error_message, \$output_buffer); + + # catch an exception, clean it up and fix output buffer + $exception = $context->catch($exception, \$output_buffer); + + # save/restore the stash to effect variable localisation + $new_stash = $context->localise(\%vars); + $old_stash = $context->delocalise(); + + # add new BLOCK or FILTER definitions + $context->define_block($name, $block); + $context->define_filter($name, \&filtersub, $is_dynamic); + + # reset context, clearing any imported BLOCK definitions + $context->reset(); + + # methods for accessing internal items + $stash = $context->stash(); + $tflag = $context->trim(); + $epflag = $context->eval_perl(); + $providers = $context->templates(); + $providers = $context->plugins(); + $providers = $context->filters(); + ... + +=head1 DESCRIPTION + +The Template::Context module defines an object class for representing +a runtime context in which templates are processed. It provides an +interface to the fundamental operations of the Template Toolkit +processing engine through which compiled templates (i.e. Perl code +constructed from the template source) can process templates, load +plugins and filters, raise exceptions and so on. + +A default Template::Context object is created by the Template module. +Any Template::Context options may be passed to the Template new() +constructor method and will be forwarded to the Template::Context +constructor. + + use Template; + + my $template = Template->new({ + TRIM => 1, + EVAL_PERL => 1, + BLOCKS => { + header => 'This is the header', + footer => 'This is the footer', + }, + }); + +Similarly, the Template::Context constructor will forward all configuration +parameters onto other default objects (e.g. Template::Provider, Template::Plugins, +Template::Filters, etc.) that it may need to instantiate. + + $context = Template::Context->new({ + INCLUDE_PATH => '/home/abw/templates', # provider option + TAG_STYLE => 'html', # parser option + }); + +A Template::Context object (or subclass/derivative) can be explicitly +instantiated and passed to the Template new() constructor method as +the CONTEXT item. + + use Template; + use Template::Context; + + my $context = Template::Context->new({ TRIM => 1 }); + my $template = Template->new({ CONTEXT => $context }); + +The Template module uses the Template::Config context() factory method +to create a default context object when required. The +$Template::Config::CONTEXT package variable may be set to specify an +alternate context module. This will be loaded automatically and its +new() constructor method called by the context() factory method when +a default context object is required. + + use Template; + + $Template::Config::CONTEXT = 'MyOrg::Template::Context'; + + my $template = Template->new({ + EVAL_PERL => 1, + EXTRA_MAGIC => 'red hot', # your extra config items + ... + }); + +=head1 METHODS + +=head2 new(\%params) + +The new() constructor method is called to instantiate a Template::Context +object. Configuration parameters may be specified as a HASH reference or +as a list of (name =E<gt> value) pairs. + + my $context = Template::Context->new({ + INCLUDE_PATH => 'header', + POST_PROCESS => 'footer', + }); + + my $context = Template::Context->new( EVAL_PERL => 1 ); + +The new() method returns a Template::Context object (or sub-class) or +undef on error. In the latter case, a relevant error message can be +retrieved by the error() class method or directly from the +$Template::Context::ERROR package variable. + + my $context = Template::Context->new(\%config) + || die Template::Context->error(); + + my $context = Template::Context->new(\%config) + || die $Template::Context::ERROR; + +The following configuration items may be specified. + +=over 4 + + +=item VARIABLES, PRE_DEFINE + +The VARIABLES option (or PRE_DEFINE - they're equivalent) can be used +to specify a hash array of template variables that should be used to +pre-initialise the stash when it is created. These items are ignored +if the STASH item is defined. + + my $context = Template::Context->new({ + VARIABLES => { + title => 'A Demo Page', + author => 'Joe Random Hacker', + version => 3.14, + }, + }; + +or + + my $context = Template::Context->new({ + PRE_DEFINE => { + title => 'A Demo Page', + author => 'Joe Random Hacker', + version => 3.14, + }, + }; + + + + + +=item BLOCKS + +The BLOCKS option can be used to pre-define a default set of template +blocks. These should be specified as a reference to a hash array +mapping template names to template text, subroutines or Template::Document +objects. + + my $context = Template::Context->new({ + BLOCKS => { + header => 'The Header. [% title %]', + footer => sub { return $some_output_text }, + another => Template::Document->new({ ... }), + }, + }); + + + + + +=item TRIM + +The TRIM option can be set to have any leading and trailing whitespace +automatically removed from the output of all template files and BLOCKs. + +By example, the following BLOCK definition + + [% BLOCK foo %] + Line 1 of foo + [% END %] + +will be processed is as "\nLine 1 of foo\n". When INCLUDEd, the surrounding +newlines will also be introduced. + + before + [% INCLUDE foo %] + after + +output: + before + + Line 1 of foo + + after + +With the TRIM option set to any true value, the leading and trailing +newlines (which count as whitespace) will be removed from the output +of the BLOCK. + + before + Line 1 of foo + after + +The TRIM option is disabled (0) by default. + + + + + + +=item EVAL_PERL + +This flag is used to indicate if PERL and/or RAWPERL blocks should be +evaluated. By default, it is disabled and any PERL or RAWPERL blocks +encountered will raise exceptions of type 'perl' with the message +'EVAL_PERL not set'. Note however that any RAWPERL blocks should +always contain valid Perl code, regardless of the EVAL_PERL flag. The +parser will fail to compile templates that contain invalid Perl code +in RAWPERL blocks and will throw a 'file' exception. + +When using compiled templates (see +L<COMPILE_EXT|Template::Manual::Config/Caching_and_Compiling_Options> and +L<COMPILE_DIR|Template::Manual::Config/Caching_and_Compiling_Options>), +the EVAL_PERL has an affect when the template is compiled, and again +when the templates is subsequently processed, possibly in a different +context to the one that compiled it. + +If the EVAL_PERL is set when a template is compiled, then all PERL and +RAWPERL blocks will be included in the compiled template. If the +EVAL_PERL option isn't set, then Perl code will be generated which +B<always> throws a 'perl' exception with the message 'EVAL_PERL not +set' B<whenever> the compiled template code is run. + +Thus, you must have EVAL_PERL set if you want your compiled templates +to include PERL and RAWPERL blocks. + +At some point in the future, using a different invocation of the +Template Toolkit, you may come to process such a pre-compiled +template. Assuming the EVAL_PERL option was set at the time the +template was compiled, then the output of any RAWPERL blocks will be +included in the compiled template and will get executed when the +template is processed. This will happen regardless of the runtime +EVAL_PERL status. + +Regular PERL blocks are a little more cautious, however. If the +EVAL_PERL flag isn't set for the I<current> context, that is, the +one which is trying to process it, then it will throw the familiar 'perl' +exception with the message, 'EVAL_PERL not set'. + +Thus you can compile templates to include PERL blocks, but optionally +disable them when you process them later. Note however that it is +possible for a PERL block to contain a Perl "BEGIN { # some code }" +block which will always get run regardless of the runtime EVAL_PERL +status. Thus, if you set EVAL_PERL when compiling templates, it is +assumed that you trust the templates to Do The Right Thing. Otherwise +you must accept the fact that there's no bulletproof way to prevent +any included code from trampling around in the living room of the +runtime environment, making a real nuisance of itself if it really +wants to. If you don't like the idea of such uninvited guests causing +a bother, then you can accept the default and keep EVAL_PERL disabled. + + + + + + + +=item RECURSION + +The template processor will raise a file exception if it detects +direct or indirect recursion into a template. Setting this option to +any true value will allow templates to include each other recursively. + + + +=item LOAD_TEMPLATES + +The LOAD_TEMPLATE option can be used to provide a reference to a list +of Template::Provider objects or sub-classes thereof which will take +responsibility for loading and compiling templates. + + my $context = Template::Context->new({ + LOAD_TEMPLATES => [ + MyOrg::Template::Provider->new({ ... }), + Template::Provider->new({ ... }), + ], + }); + +When a PROCESS, INCLUDE or WRAPPER directive is encountered, the named +template may refer to a locally defined BLOCK or a file relative to +the INCLUDE_PATH (or an absolute or relative path if the appropriate +ABSOLUTE or RELATIVE options are set). If a BLOCK definition can't be +found (see the Template::Context template() method for a discussion of +BLOCK locality) then each of the LOAD_TEMPLATES provider objects is +queried in turn via the fetch() method to see if it can supply the +required template. Each provider can return a compiled template, an +error, or decline to service the request in which case the +responsibility is passed to the next provider. If none of the +providers can service the request then a 'not found' error is +returned. The same basic provider mechanism is also used for the +INSERT directive but it bypasses any BLOCK definitions and doesn't +attempt is to parse or process the contents of the template file. + +This is an implementation of the 'Chain of Responsibility' +design pattern as described in +"Design Patterns", Erich Gamma, Richard Helm, Ralph Johnson, John +Vlissides), Addision-Wesley, ISBN 0-201-63361-2, page 223 +. + +If LOAD_TEMPLATES is undefined, a single default provider will be +instantiated using the current configuration parameters. For example, +the Template::Provider INCLUDE_PATH option can be specified in the Template::Context configuration and will be correctly passed to the provider's +constructor method. + + my $context = Template::Context->new({ + INCLUDE_PATH => '/here:/there', + }); + + + + + +=item LOAD_PLUGINS + +The LOAD_PLUGINS options can be used to specify a list of provider +objects (i.e. they implement the fetch() method) which are responsible +for loading and instantiating template plugin objects. The +Template::Content plugin() method queries each provider in turn in a +"Chain of Responsibility" as per the template() and filter() methods. + + my $context = Template::Context->new({ + LOAD_PLUGINS => [ + MyOrg::Template::Plugins->new({ ... }), + Template::Plugins->new({ ... }), + ], + }); + +By default, a single Template::Plugins object is created using the +current configuration hash. Configuration items destined for the +Template::Plugins constructor may be added to the Template::Context +constructor. + + my $context = Template::Context->new({ + PLUGIN_BASE => 'MyOrg::Template::Plugins', + LOAD_PERL => 1, + }); + + + + + +=item LOAD_FILTERS + +The LOAD_FILTERS option can be used to specify a list of provider +objects (i.e. they implement the fetch() method) which are responsible +for returning and/or creating filter subroutines. The +Template::Context filter() method queries each provider in turn in a +"Chain of Responsibility" as per the template() and plugin() methods. + + my $context = Template::Context->new({ + LOAD_FILTERS => [ + MyTemplate::Filters->new(), + Template::Filters->new(), + ], + }); + +By default, a single Template::Filters object is created for the +LOAD_FILTERS list. + + + +=item STASH + +A reference to a Template::Stash object or sub-class which will take +responsibility for managing template variables. + + my $stash = MyOrg::Template::Stash->new({ ... }); + my $context = Template::Context->new({ + STASH => $stash, + }); + +If unspecified, a default stash object is created using the VARIABLES +configuration item to initialise the stash variables. These may also +be specified as the PRE_DEFINE option for backwards compatibility with +version 1. + + my $context = Template::Context->new({ + VARIABLES => { + id => 'abw', + name => 'Andy Wardley', + }, + }; + + + +=item DEBUG + +The DEBUG option can be used to enable various debugging features +of the Template::Context module. + + use Template::Constants qw( :debug ); + + my $template = Template->new({ + DEBUG => DEBUG_CONTEXT | DEBUG_DIRS, + }); + +The DEBUG value can include any of the following. Multiple values +should be combined using the logical OR operator, '|'. + +=over 4 + +=item DEBUG_CONTEXT + +Enables general debugging messages for the +L<Template::Context|Template::Context> module. + +=item DEBUG_DIRS + +This option causes the Template Toolkit to generate comments +indicating the source file, line and original text of each directive +in the template. These comments are embedded in the template output +using the format defined in the DEBUG_FORMAT configuration item, or a +simple default format if unspecified. + +For example, the following template fragment: + + + Hello World + +would generate this output: + + ## input text line 1 : ## + Hello + ## input text line 2 : World ## + World + + +=back + + + + + +=back + +=head2 template($name) + +Returns a compiled template by querying each of the LOAD_TEMPLATES providers +(instances of Template::Provider, or sub-class) in turn. + + $template = $context->template('header'); + +On error, a Template::Exception object of type 'file' is thrown via +die(). This can be caught by enclosing the call to template() in an +eval block and examining $@. + + eval { + $template = $context->template('header'); + }; + if ($@) { + print "failed to fetch template: $@\n"; + } + +=head2 plugin($name, \@args) + +Instantiates a plugin object by querying each of the LOAD_PLUGINS +providers. The default LOAD_PLUGINS provider is a Template::Plugins +object which attempts to load plugin modules, according the various +configuration items such as PLUGIN_BASE, LOAD_PERL, etc., and then +instantiate an object via new(). A reference to a list of constructor +arguments may be passed as the second parameter. These are forwarded +to the plugin constructor. + +Returns a reference to a plugin (which is generally an object, but +doesn't have to be). Errors are thrown as Template::Exception objects +of type 'plugin'. + + $plugin = $context->plugin('DBI', 'dbi:msql:mydbname'); + +=head2 filter($name, \@args, $alias) + +Instantiates a filter subroutine by querying the LOAD_FILTERS providers. +The default LOAD_FILTERS providers is a Template::Filters object. +Additional arguments may be passed by list reference along with an +optional alias under which the filter will be cached for subsequent +use. The filter is cached under its own $name if $alias is undefined. +Subsequent calls to filter($name) will return the cached entry, if +defined. Specifying arguments bypasses the caching mechanism and +always creates a new filter. Errors are thrown as Template::Exception +objects of typre 'filter'. + + # static filter (no args) + $filter = $context->filter('html'); + + # dynamic filter (args) aliased to 'padright' + $filter = $context->filter('format', '%60s', 'padright'); + + # retrieve previous filter via 'padright' alias + $filter = $context->filter('padright'); + +=head2 process($template, \%vars) + +Processes a template named or referenced by the first parameter and returns +the output generated. An optional reference to a hash array may be passed +as the second parameter, containing variable definitions which will be set +before the template is processed. The template is processed in the current +context, with no localisation of variables performed. Errors are thrown +as Template::Exception objects via die(). + + $output = $context->process('header', { title => 'Hello World' }); + +=head2 include($template, \%vars) + +Similar to process() above, but using localised variables. Changes made to +any variables will only persist until the include() method completes. + + $output = $context->include('header', { title => 'Hello World' }); + +=head2 throw($error_type, $error_message, \$output) + +Raises an exception in the form of a Template::Exception object by +calling die(). This method may be passed a reference to an existing +Template::Exception object; a single value containing an error message +which is used to instantiate a Template::Exception of type 'undef'; or +a pair of values representing the exception type and info from which a +Template::Exception object is instantiated. e.g. + + $context->throw($exception); + $context->throw("I'm sorry Dave, I can't do that"); + $context->throw('denied', "I'm sorry Dave, I can't do that"); + +The optional third parameter may be a reference to the current output +buffer. This is then stored in the exception object when created, +allowing the catcher to examine and use the output up to the point at +which the exception was raised. + + $output .= 'blah blah blah'; + $output .= 'more rhubarb'; + $context->throw('yack', 'Too much yacking', \$output); + +=head2 catch($exception, \$output) + +Catches an exception thrown, either as a reference to a +Template::Exception object or some other value. In the latter case, +the error string is promoted to a Template::Exception object of +'undef' type. This method also accepts a reference to the current +output buffer which is passed to the Template::Exception constructor, +or is appended to the output buffer stored in an existing +Template::Exception object, if unique (i.e. not the same reference). +By this process, the correct state of the output buffer can be +reconstructed for simple or nested throws. + +=head2 define_block($name, $block) + +Adds a new block definition to the internal BLOCKS cache. The first +argument should contain the name of the block and the second a reference +to a Template::Document object or template sub-routine, or template text +which is automatically compiled into a template sub-routine. Returns +a true value (the sub-routine or Template::Document reference) on +success or undef on failure. The relevant error message can be +retrieved by calling the error() method. + +=head2 define_filter($name, \&filter, $is_dynamic) + +Adds a new filter definition by calling the store() method on each of +the LOAD_FILTERS providers until accepted (in the usual case, this is +accepted straight away by the one and only Template::Filters +provider). The first argument should contain the name of the filter +and the second a reference to a filter subroutine. The optional +third argument can be set to any true value to indicate that the +subroutine is a dynamic filter factory. Returns a true value or +throws a 'filter' exception on error. + +=head2 localise(\%vars) + +Clones the stash to create a context with localised variables. Returns a +reference to the newly cloned stash object which is also stored +internally. + + $stash = $context->localise(); + +=head2 delocalise() + +Restore the stash to its state prior to localisation. + + $stash = $context->delocalise(); + +=head2 visit(\%blocks) + +This method is called by Template::Document objects immediately before +they process their content. It is called to register any local BLOCK +definitions with the context object so that they may be subsequently +delivered on request. + +=head2 leave() + +Compliment to visit(), above. Called by Template::Document objects +immediately after they process their content. + +=head2 reset() + +Clears the local BLOCKS cache of any BLOCK definitions. Any initial set of +BLOCKS specified as a configuration item to the constructor will be reinstated. + +=head2 AUTOLOAD + +An AUTOLOAD method provides access to context configuration items. + + $stash = $context->stash(); + $tflag = $context->trim(); + $epflag = $context->eval_perl(); + ... + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.89, 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|Template>, L<Template::Document|Template::Document>, L<Template::Exception|Template::Exception>, L<Template::Filters|Template::Filters>, L<Template::Plugins|Template::Plugins>, L<Template::Provider|Template::Provider>, L<Template::Service|Template::Service>, L<Template::Stash|Template::Stash> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Directive.pm b/lib/Template/Directive.pm new file mode 100644 index 0000000..c3f86a9 --- /dev/null +++ b/lib/Template/Directive.pm @@ -0,0 +1,1004 @@ +#================================================================= -*-Perl-*- +# +# Template::Directive +# +# DESCRIPTION +# Factory module for constructing templates from Perl code. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# WARNING +# Much of this module is hairy, even furry in places. It needs +# a lot of tidying up and may even be moved into a different place +# altogether. The generator code is often inefficient, particulary in +# being very anal about pretty-printing the Perl code all neatly, but +# at the moment, that's still high priority for the sake of easier +# debugging. +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Directive.pm,v 2.18 2003/10/08 09:34:41 abw Exp $ +# +#============================================================================ + +package Template::Directive; + +require 5.004; + +use strict; +use Template::Base; +use Template::Constants; +use Template::Exception; + +use base qw( Template::Base ); +use vars qw( $VERSION $DEBUG $PRETTY $WHILE_MAX $OUTPUT ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.18 $ =~ /(\d+)\.(\d+)/); + +$WHILE_MAX = 1000 unless defined $WHILE_MAX; +$PRETTY = 0 unless defined $PRETTY; +$OUTPUT = '$output .= '; + + +sub _init { + my ($self, $config) = @_; + $self->{ NAMESPACE } = $config->{ NAMESPACE }; + return $self; +} + + +sub pad { + my ($text, $pad) = @_; + $pad = ' ' x ($pad * 4); + $text =~ s/^(?!#line)/$pad/gm; + $text; +} + +#======================================================================== +# FACTORY METHODS +# +# These methods are called by the parser to construct directive instances. +#======================================================================== + +#------------------------------------------------------------------------ +# template($block) +#------------------------------------------------------------------------ + +sub template { + my ($class, $block) = @_; + $block = pad($block, 2) if $PRETTY; + + return "sub { return '' }" unless $block =~ /\S/; + + return <<EOF; +sub { + my \$context = shift || die "template sub called without context\\n"; + my \$stash = \$context->stash; + my \$output = ''; + my \$error; + + eval { BLOCK: { +$block + } }; + if (\$@) { + \$error = \$context->catch(\$@, \\\$output); + die \$error unless \$error->type eq 'return'; + } + + return \$output; +} +EOF +} + + +#------------------------------------------------------------------------ +# anon_block($block) [% BLOCK %] ... [% END %] +#------------------------------------------------------------------------ + +sub anon_block { + my ($class, $block) = @_; + $block = pad($block, 2) if $PRETTY; + + return <<EOF; + +# BLOCK +$OUTPUT do { + my \$output = ''; + my \$error; + + eval { BLOCK: { +$block + } }; + if (\$@) { + \$error = \$context->catch(\$@, \\\$output); + die \$error unless \$error->type eq 'return'; + } + + \$output; +}; +EOF +} + + +#------------------------------------------------------------------------ +# block($blocktext) +#------------------------------------------------------------------------ + +sub block { + my ($class, $block) = @_; + return join("\n", @{ $block || [] }); +} + + +#------------------------------------------------------------------------ +# textblock($text) +#------------------------------------------------------------------------ + +sub textblock { + my ($class, $text) = @_; + return "$OUTPUT " . &text($class, $text) . ';'; +} + + +#------------------------------------------------------------------------ +# text($text) +#------------------------------------------------------------------------ + +sub text { + my ($class, $text) = @_; + for ($text) { + s/(["\$\@\\])/\\$1/g; + s/\n/\\n/g; + } + return '"' . $text . '"'; +} + + +#------------------------------------------------------------------------ +# quoted(\@items) "foo$bar" +#------------------------------------------------------------------------ + +sub quoted { + my ($class, $items) = @_; + return '' unless @$items; + return ("('' . " . $items->[0] . ')') if scalar @$items == 1; + return '(' . join(' . ', @$items) . ')'; +# my $r = '(' . join(' . ', @$items) . ' . "")'; +# print STDERR "[$r]\n"; +# return $r; +} + + +#------------------------------------------------------------------------ +# ident(\@ident) foo.bar(baz) +#------------------------------------------------------------------------ + +sub ident { + my ($class, $ident) = @_; + return "''" unless @$ident; + my $ns; + + # does the first element of the identifier have a NAMESPACE + # handler defined? + if (ref $class && @$ident > 2 && ($ns = $class->{ NAMESPACE })) { + my $key = $ident->[0]; + $key =~ s/^'(.+)'$/$1/s; + if ($ns = $ns->{ $key }) { + return $ns->ident($ident); + } + } + + if (scalar @$ident <= 2 && ! $ident->[1]) { + $ident = $ident->[0]; + } + else { + $ident = '[' . join(', ', @$ident) . ']'; + } + return "\$stash->get($ident)"; +} + +#------------------------------------------------------------------------ +# identref(\@ident) \foo.bar(baz) +#------------------------------------------------------------------------ + +sub identref { + my ($class, $ident) = @_; + return "''" unless @$ident; + if (scalar @$ident <= 2 && ! $ident->[1]) { + $ident = $ident->[0]; + } + else { + $ident = '[' . join(', ', @$ident) . ']'; + } + return "\$stash->getref($ident)"; +} + + +#------------------------------------------------------------------------ +# assign(\@ident, $value, $default) foo = bar +#------------------------------------------------------------------------ + +sub assign { + my ($class, $var, $val, $default) = @_; + + if (ref $var) { + if (scalar @$var == 2 && ! $var->[1]) { + $var = $var->[0]; + } + else { + $var = '[' . join(', ', @$var) . ']'; + } + } + $val .= ', 1' if $default; + return "\$stash->set($var, $val)"; +} + + +#------------------------------------------------------------------------ +# args(\@args) foo, bar, baz = qux +#------------------------------------------------------------------------ + +sub args { + my ($class, $args) = @_; + my $hash = shift @$args; + push(@$args, '{ ' . join(', ', @$hash) . ' }') + if @$hash; + + return '0' unless @$args; + return '[ ' . join(', ', @$args) . ' ]'; +} + +#------------------------------------------------------------------------ +# filenames(\@names) +#------------------------------------------------------------------------ + +sub filenames { + my ($class, $names) = @_; + if (@$names > 1) { + $names = '[ ' . join(', ', @$names) . ' ]'; + } + else { + $names = shift @$names; + } + return $names; +} + + +#------------------------------------------------------------------------ +# get($expr) [% foo %] +#------------------------------------------------------------------------ + +sub get { + my ($class, $expr) = @_; + return "$OUTPUT $expr;"; +} + + +#------------------------------------------------------------------------ +# call($expr) [% CALL bar %] +#------------------------------------------------------------------------ + +sub call { + my ($class, $expr) = @_; + $expr .= ';'; + return $expr; +} + + +#------------------------------------------------------------------------ +# set(\@setlist) [% foo = bar, baz = qux %] +#------------------------------------------------------------------------ + +sub set { + my ($class, $setlist) = @_; + my $output; + while (my ($var, $val) = splice(@$setlist, 0, 2)) { + $output .= &assign($class, $var, $val) . ";\n"; + } + chomp $output; + return $output; +} + + +#------------------------------------------------------------------------ +# default(\@setlist) [% DEFAULT foo = bar, baz = qux %] +#------------------------------------------------------------------------ + +sub default { + my ($class, $setlist) = @_; + my $output; + while (my ($var, $val) = splice(@$setlist, 0, 2)) { + $output .= &assign($class, $var, $val, 1) . ";\n"; + } + chomp $output; + return $output; +} + + +#------------------------------------------------------------------------ +# insert(\@nameargs) [% INSERT file %] +# # => [ [ $file, ... ], \@args ] +#------------------------------------------------------------------------ + +sub insert { + my ($class, $nameargs) = @_; + my ($file, $args) = @$nameargs; + $file = $class->filenames($file); + return "$OUTPUT \$context->insert($file);"; +} + + +#------------------------------------------------------------------------ +# include(\@nameargs) [% INCLUDE template foo = bar %] +# # => [ [ $file, ... ], \@args ] +#------------------------------------------------------------------------ + +sub include { + my ($class, $nameargs) = @_; + my ($file, $args) = @$nameargs; + my $hash = shift @$args; + $file = $class->filenames($file); + $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : ''; + return "$OUTPUT \$context->include($file);"; +} + + +#------------------------------------------------------------------------ +# process(\@nameargs) [% PROCESS template foo = bar %] +# # => [ [ $file, ... ], \@args ] +#------------------------------------------------------------------------ + +sub process { + my ($class, $nameargs) = @_; + my ($file, $args) = @$nameargs; + my $hash = shift @$args; + $file = $class->filenames($file); + $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : ''; + return "$OUTPUT \$context->process($file);"; +} + + +#------------------------------------------------------------------------ +# if($expr, $block, $else) [% IF foo < bar %] +# ... +# [% ELSE %] +# ... +# [% END %] +#------------------------------------------------------------------------ + +sub if { + my ($class, $expr, $block, $else) = @_; + my @else = $else ? @$else : (); + $else = pop @else; + $block = pad($block, 1) if $PRETTY; + + my $output = "if ($expr) {\n$block\n}\n"; + + foreach my $elsif (@else) { + ($expr, $block) = @$elsif; + $block = pad($block, 1) if $PRETTY; + $output .= "elsif ($expr) {\n$block\n}\n"; + } + if (defined $else) { + $else = pad($else, 1) if $PRETTY; + $output .= "else {\n$else\n}\n"; + } + + return $output; +} + + +#------------------------------------------------------------------------ +# foreach($target, $list, $args, $block) [% FOREACH x = [ foo bar ] %] +# ... +# [% END %] +#------------------------------------------------------------------------ + +sub foreach { + my ($class, $target, $list, $args, $block) = @_; + $args = shift @$args; + $args = @$args ? ', { ' . join(', ', @$args) . ' }' : ''; + + my ($loop_save, $loop_set, $loop_restore, $setiter); + if ($target) { + $loop_save = 'eval { $oldloop = ' . &ident($class, ["'loop'"]) . ' }'; + $loop_set = "\$stash->{'$target'} = \$value"; + $loop_restore = "\$stash->set('loop', \$oldloop)"; + } + else { + $loop_save = '$stash = $context->localise()'; +# $loop_set = "\$stash->set('import', \$value) " +# . "if ref \$value eq 'HASH'"; + $loop_set = "\$stash->get(['import', [\$value]]) " + . "if ref \$value eq 'HASH'"; + $loop_restore = '$stash = $context->delocalise()'; + } + $block = pad($block, 3) if $PRETTY; + + return <<EOF; + +# FOREACH +do { + my (\$value, \$error, \$oldloop); + my \$list = $list; + + unless (UNIVERSAL::isa(\$list, 'Template::Iterator')) { + \$list = Template::Config->iterator(\$list) + || die \$Template::Config::ERROR, "\\n"; + } + + (\$value, \$error) = \$list->get_first(); + $loop_save; + \$stash->set('loop', \$list); + eval { +LOOP: while (! \$error) { + $loop_set; +$block; + (\$value, \$error) = \$list->get_next(); + } + }; + $loop_restore; + die \$@ if \$@; + \$error = 0 if \$error && \$error eq Template::Constants::STATUS_DONE; + die \$error if \$error; +}; +EOF +} + +#------------------------------------------------------------------------ +# next() [% NEXT %] +# +# Next iteration of a FOREACH loop (experimental) +#------------------------------------------------------------------------ + +sub next { + return <<EOF; +(\$value, \$error) = \$list->get_next(); +next LOOP; +EOF +} + + +#------------------------------------------------------------------------ +# wrapper(\@nameargs, $block) [% WRAPPER template foo = bar %] +# # => [ [$file,...], \@args ] +#------------------------------------------------------------------------ + +sub wrapper { + my ($class, $nameargs, $block) = @_; + my ($file, $args) = @$nameargs; + my $hash = shift @$args; + + local $" = ', '; +# print STDERR "wrapper([@$file], { @$hash })\n"; + + return $class->multi_wrapper($file, $hash, $block) + if @$file > 1; + $file = shift @$file; + + $block = pad($block, 1) if $PRETTY; + push(@$hash, "'content'", '$output'); + $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : ''; + + return <<EOF; + +# WRAPPER +$OUTPUT do { + my \$output = ''; +$block + \$context->include($file); +}; +EOF +} + + +sub multi_wrapper { + my ($class, $file, $hash, $block) = @_; + $block = pad($block, 1) if $PRETTY; + + push(@$hash, "'content'", '$output'); + $hash = @$hash ? ', { ' . join(', ', @$hash) . ' }' : ''; + + $file = join(', ', reverse @$file); +# print STDERR "multi wrapper: $file\n"; + + return <<EOF; + +# WRAPPER +$OUTPUT do { + my \$output = ''; +$block + foreach ($file) { + \$output = \$context->include(\$_$hash); + } + \$output; +}; +EOF +} + + +#------------------------------------------------------------------------ +# while($expr, $block) [% WHILE x < 10 %] +# ... +# [% END %] +#------------------------------------------------------------------------ + +sub while { + my ($class, $expr, $block) = @_; + $block = pad($block, 2) if $PRETTY; + + return <<EOF; + +# WHILE +do { + my \$failsafe = $WHILE_MAX; +LOOP: + while (--\$failsafe && ($expr)) { +$block + } + die "WHILE loop terminated (> $WHILE_MAX iterations)\\n" + unless \$failsafe; +}; +EOF +} + + +#------------------------------------------------------------------------ +# switch($expr, \@case) [% SWITCH %] +# [% CASE foo %] +# ... +# [% END %] +#------------------------------------------------------------------------ + +sub switch { + my ($class, $expr, $case) = @_; + my @case = @$case; + my ($match, $block, $default); + my $caseblock = ''; + + $default = pop @case; + + foreach $case (@case) { + $match = $case->[0]; + $block = $case->[1]; + $block = pad($block, 1) if $PRETTY; + $caseblock .= <<EOF; +\$match = $match; +\$match = [ \$match ] unless ref \$match eq 'ARRAY'; +if (grep(/^\$result\$/, \@\$match)) { +$block + last SWITCH; +} +EOF + } + + $caseblock .= $default + if defined $default; + $caseblock = pad($caseblock, 2) if $PRETTY; + +return <<EOF; + +# SWITCH +do { + my \$result = $expr; + my \$match; + SWITCH: { +$caseblock + } +}; +EOF +} + + +#------------------------------------------------------------------------ +# try($block, \@catch) [% TRY %] +# ... +# [% CATCH %] +# ... +# [% END %] +#------------------------------------------------------------------------ + +sub try { + my ($class, $block, $catch) = @_; + my @catch = @$catch; + my ($match, $mblock, $default, $final, $n); + my $catchblock = ''; + my $handlers = []; + + $block = pad($block, 2) if $PRETTY; + $final = pop @catch; + $final = "# FINAL\n" . ($final ? "$final\n" : '') + . 'die $error if $error;' . "\n" . '$output;'; + $final = pad($final, 1) if $PRETTY; + + $n = 0; + foreach $catch (@catch) { + $match = $catch->[0] || do { + $default ||= $catch->[1]; + next; + }; + $mblock = $catch->[1]; + $mblock = pad($mblock, 1) if $PRETTY; + push(@$handlers, "'$match'"); + $catchblock .= $n++ + ? "elsif (\$handler eq '$match') {\n$mblock\n}\n" + : "if (\$handler eq '$match') {\n$mblock\n}\n"; + } + $catchblock .= "\$error = 0;"; + $catchblock = pad($catchblock, 3) if $PRETTY; + if ($default) { + $default = pad($default, 1) if $PRETTY; + $default = "else {\n # DEFAULT\n$default\n \$error = '';\n}"; + } + else { + $default = '# NO DEFAULT'; + } + $default = pad($default, 2) if $PRETTY; + + $handlers = join(', ', @$handlers); +return <<EOF; + +# TRY +$OUTPUT do { + my \$output = ''; + my (\$error, \$handler); + eval { +$block + }; + if (\$@) { + \$error = \$context->catch(\$@, \\\$output); + die \$error if \$error->type =~ /^return|stop\$/; + \$stash->set('error', \$error); + \$stash->set('e', \$error); + if (defined (\$handler = \$error->select_handler($handlers))) { +$catchblock + } +$default + } +$final +}; +EOF +} + + +#------------------------------------------------------------------------ +# throw(\@nameargs) [% THROW foo "bar error" %] +# # => [ [$type], \@args ] +#------------------------------------------------------------------------ + +sub throw { + my ($class, $nameargs) = @_; + my ($type, $args) = @$nameargs; + my $hash = shift(@$args); + my $info = shift(@$args); + $type = shift @$type; # uses same parser production as INCLUDE + # etc., which allow multiple names + # e.g. INCLUDE foo+bar+baz + + if (! $info) { + $args = "$type, undef"; + } + elsif (@$hash || @$args) { + local $" = ', '; + my $i = 0; + $args = "$type, { args => [ " + . join(', ', $info, @$args) + . ' ], ' + . join(', ', + (map { "'" . $i++ . "' => $_" } ($info, @$args)), + @$hash) + . ' }'; + } + else { + $args = "$type, $info"; + } + + return "\$context->throw($args, \\\$output);"; +} + + +#------------------------------------------------------------------------ +# clear() [% CLEAR %] +# +# NOTE: this is redundant, being hard-coded (for now) into Parser.yp +#------------------------------------------------------------------------ + +sub clear { + return "\$output = '';"; +} + +#------------------------------------------------------------------------ +# break() [% BREAK %] +# +# NOTE: this is redundant, being hard-coded (for now) into Parser.yp +#------------------------------------------------------------------------ + +sub break { + return 'last LOOP;'; +} + +#------------------------------------------------------------------------ +# return() [% RETURN %] +#------------------------------------------------------------------------ + +sub return { + return "\$context->throw('return', '', \\\$output);"; +} + +#------------------------------------------------------------------------ +# stop() [% STOP %] +#------------------------------------------------------------------------ + +sub stop { + return "\$context->throw('stop', '', \\\$output);"; +} + + +#------------------------------------------------------------------------ +# use(\@lnameargs) [% USE alias = plugin(args) %] +# # => [ [$file, ...], \@args, $alias ] +#------------------------------------------------------------------------ + +sub use { + my ($class, $lnameargs) = @_; + my ($file, $args, $alias) = @$lnameargs; + $file = shift @$file; # same production rule as INCLUDE + $alias ||= $file; + $args = &args($class, $args); + $file .= ", $args" if $args; +# my $set = &assign($class, $alias, '$plugin'); + return "# USE\n" + . "\$stash->set($alias,\n" + . " \$context->plugin($file));"; +} + +#------------------------------------------------------------------------ +# view(\@nameargs, $block) [% VIEW name args %] +# # => [ [$file, ... ], \@args ] +#------------------------------------------------------------------------ + +sub view { + my ($class, $nameargs, $block, $defblocks) = @_; + my ($name, $args) = @$nameargs; + my $hash = shift @$args; + $name = shift @$name; # same production rule as INCLUDE + $block = pad($block, 1) if $PRETTY; + + if (%$defblocks) { + $defblocks = join(",\n", map { "'$_' => $defblocks->{ $_ }" } + keys %$defblocks); + $defblocks = pad($defblocks, 1) if $PRETTY; + $defblocks = "{\n$defblocks\n}"; + push(@$hash, "'blocks'", $defblocks); + } + $hash = @$hash ? '{ ' . join(', ', @$hash) . ' }' : ''; + + return <<EOF; +# VIEW +do { + my \$output = ''; + my \$oldv = \$stash->get('view'); + my \$view = \$context->view($hash); + \$stash->set($name, \$view); + \$stash->set('view', \$view); + +$block + + \$stash->set('view', \$oldv); + \$view->seal(); + \$output; +}; +EOF +} + + +#------------------------------------------------------------------------ +# perl($block) +#------------------------------------------------------------------------ + +sub perl { + my ($class, $block) = @_; + $block = pad($block, 1) if $PRETTY; + + return <<EOF; + +# PERL +\$context->throw('perl', 'EVAL_PERL not set') + unless \$context->eval_perl(); + +$OUTPUT do { + my \$output = "package Template::Perl;\\n"; + +$block + + local(\$Template::Perl::context) = \$context; + local(\$Template::Perl::stash) = \$stash; + + my \$result = ''; + tie *Template::Perl::PERLOUT, 'Template::TieString', \\\$result; + my \$save_stdout = select *Template::Perl::PERLOUT; + + eval \$output; + select \$save_stdout; + \$context->throw(\$@) if \$@; + \$result; +}; +EOF +} + + +#------------------------------------------------------------------------ +# no_perl() +#------------------------------------------------------------------------ + +sub no_perl { + my $class = shift; + return "\$context->throw('perl', 'EVAL_PERL not set');"; +} + + +#------------------------------------------------------------------------ +# rawperl($block) +# +# NOTE: perhaps test context EVAL_PERL switch at compile time rather than +# runtime? +#------------------------------------------------------------------------ + +sub rawperl { + my ($class, $block, $line) = @_; + for ($block) { + s/^\n+//; + s/\n+$//; + } + $block = pad($block, 1) if $PRETTY; + $line = $line ? " (starting line $line)" : ''; + + return <<EOF; +# RAWPERL +#line 1 "RAWPERL block$line" +$block +EOF +} + + + +#------------------------------------------------------------------------ +# filter() +#------------------------------------------------------------------------ + +sub filter { + my ($class, $lnameargs, $block) = @_; + my ($name, $args, $alias) = @$lnameargs; + $name = shift @$name; + $args = &args($class, $args); + $args = $args ? "$args, $alias" : ", undef, $alias" + if $alias; + $name .= ", $args" if $args; + $block = pad($block, 1) if $PRETTY; + + return <<EOF; + +# FILTER +$OUTPUT do { + my \$output = ''; + my \$filter = \$context->filter($name) + || \$context->throw(\$context->error); + +$block + + &\$filter(\$output); +}; +EOF +} + + +#------------------------------------------------------------------------ +# capture($name, $block) +#------------------------------------------------------------------------ + +sub capture { + my ($class, $name, $block) = @_; + + if (ref $name) { + if (scalar @$name == 2 && ! $name->[1]) { + $name = $name->[0]; + } + else { + $name = '[' . join(', ', @$name) . ']'; + } + } + $block = pad($block, 1) if $PRETTY; + + return <<EOF; + +# CAPTURE +\$stash->set($name, do { + my \$output = ''; +$block + \$output; +}); +EOF + +} + + +#------------------------------------------------------------------------ +# macro($name, $block, \@args) +#------------------------------------------------------------------------ + +sub macro { + my ($class, $ident, $block, $args) = @_; + $block = pad($block, 2) if $PRETTY; + + if ($args) { + my $nargs = scalar @$args; + $args = join(', ', map { "'$_'" } @$args); + $args = $nargs > 1 + ? "\@args{ $args } = splice(\@_, 0, $nargs)" + : "\$args{ $args } = shift"; + + return <<EOF; + +# MACRO +\$stash->set('$ident', sub { + my \$output = ''; + my (%args, \$params); + $args; + \$params = shift; + \$params = { } unless ref(\$params) eq 'HASH'; + \$params = { \%args, %\$params }; + + my \$stash = \$context->localise(\$params); + eval { +$block + }; + \$stash = \$context->delocalise(); + die \$@ if \$@; + return \$output; +}); +EOF + + } + else { + return <<EOF; + +# MACRO +\$stash->set('$ident', sub { + my \$params = \$_[0] if ref(\$_[0]) eq 'HASH'; + my \$output = ''; + + my \$stash = \$context->localise(\$params); + eval { +$block + }; + \$stash = \$context->delocalise(); + die \$@ if \$@; + return \$output; +}); +EOF + } +} + + +sub debug { + my ($class, $nameargs) = @_; + my ($file, $args) = @$nameargs; + my $hash = shift @$args; + $args = join(', ', @$file, @$args); + $args .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : ''; + return "$OUTPUT \$context->debugging($args); ## DEBUG ##"; +} + + +1; + +__END__ + diff --git a/lib/Template/Document.pm b/lib/Template/Document.pm new file mode 100644 index 0000000..ce3beb2 --- /dev/null +++ b/lib/Template/Document.pm @@ -0,0 +1,492 @@ +##============================================================= -*-Perl-*- +# +# Template::Document +# +# DESCRIPTION +# Module defining a class of objects which encapsulate compiled +# templates, storing additional block definitions and metadata +# as well as the compiled Perl sub-routine representing the main +# template content. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Document.pm,v 2.71 2004/01/13 16:19:10 abw Exp $ +# +#============================================================================ + +package Template::Document; + +require 5.004; + +use strict; +use vars qw( $VERSION $ERROR $COMPERR $DEBUG $AUTOLOAD ); +use base qw( Template::Base ); +use Template::Constants; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.71 $ =~ /(\d+)\.(\d+)/); + + +#======================================================================== +# ----- PUBLIC METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# new(\%document) +# +# Creates a new self-contained Template::Document object which +# encapsulates a compiled Perl sub-routine, $block, any additional +# BLOCKs defined within the document ($defblocks, also Perl sub-routines) +# and additional $metadata about the document. +#------------------------------------------------------------------------ + +sub new { + my ($class, $doc) = @_; + my ($block, $defblocks, $metadata) = @$doc{ qw( BLOCK DEFBLOCKS METADATA ) }; + $defblocks ||= { }; + $metadata ||= { }; + + # evaluate Perl code in $block to create sub-routine reference if necessary + unless (ref $block) { + local $SIG{__WARN__} = \&catch_warnings; + $COMPERR = ''; + + # DON'T LOOK NOW! - blindly untainting can make you go blind! + $block =~ /(.*)/s; + $block = $1; + + $block = eval $block; + return $class->error($@) + unless defined $block; + } + + # same for any additional BLOCK definitions + @$defblocks{ keys %$defblocks } = + # MORE BLIND UNTAINTING - turn away if you're squeamish + map { + ref($_) + ? $_ + : ( /(.*)/s && eval($1) or return $class->error($@) ) + } values %$defblocks; + + bless { + %$metadata, + _BLOCK => $block, + _DEFBLOCKS => $defblocks, + _HOT => 0, + }, $class; +} + + +#------------------------------------------------------------------------ +# block() +# +# Returns a reference to the internal sub-routine reference, _BLOCK, +# that constitutes the main document template. +#------------------------------------------------------------------------ + +sub block { + return $_[0]->{ _BLOCK }; +} + + +#------------------------------------------------------------------------ +# blocks() +# +# Returns a reference to a hash array containing any BLOCK definitions +# from the template. The hash keys are the BLOCK nameand the values +# are references to Template::Document objects. Returns 0 (# an empty hash) +# if no blocks are defined. +#------------------------------------------------------------------------ + +sub blocks { + return $_[0]->{ _DEFBLOCKS }; +} + + +#------------------------------------------------------------------------ +# process($context) +# +# Process the document in a particular context. Checks for recursion, +# registers the document with the context via visit(), processes itself, +# and then unwinds with a large gin and tonic. +#------------------------------------------------------------------------ + +sub process { + my ($self, $context) = @_; + my $defblocks = $self->{ _DEFBLOCKS }; + my $output; + + + # check we're not already visiting this template + return $context->throw(Template::Constants::ERROR_FILE, + "recursion into '$self->{ name }'") + if $self->{ _HOT } && ! $context->{ RECURSION }; ## RETURN ## + + $context->visit($self, $defblocks); + + $self->{ _HOT } = 1; + eval { + my $block = $self->{ _BLOCK }; + $output = &$block($context); + }; + $self->{ _HOT } = 0; + + $context->leave(); + + die $context->catch($@) + if $@; + + return $output; +} + + +#------------------------------------------------------------------------ +# AUTOLOAD +# +# Provides pseudo-methods for read-only access to various internal +# members. +#------------------------------------------------------------------------ + +sub AUTOLOAD { + my $self = shift; + my $method = $AUTOLOAD; + + $method =~ s/.*:://; + return if $method eq 'DESTROY'; +# my ($pkg, $file, $line) = caller(); +# print STDERR "called $self->AUTOLOAD($method) from $file line $line\n"; + return $self->{ $method }; +} + + +#======================================================================== +# ----- PRIVATE METHODS ----- +#======================================================================== + + +#------------------------------------------------------------------------ +# _dump() +# +# Debug method which returns a string representing the internal state +# of the object. +#------------------------------------------------------------------------ + +sub _dump { + my $self = shift; + my $dblks; + my $output = "$self : $self->{ name }\n"; + + $output .= "BLOCK: $self->{ _BLOCK }\nDEFBLOCKS:\n"; + + if ($dblks = $self->{ _DEFBLOCKS }) { + foreach my $b (keys %$dblks) { + $output .= " $b: $dblks->{ $b }\n"; + } + } + + return $output; +} + + +#======================================================================== +# ----- CLASS METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# as_perl($content) +# +# This method expects a reference to a hash passed as the first argument +# containing 3 items: +# METADATA # a hash of template metadata +# BLOCK # string containing Perl sub definition for main block +# DEFBLOCKS # hash containing further subs for addional BLOCK defs +# It returns a string containing Perl code which, when evaluated and +# executed, will instantiate a new Template::Document object with the +# above data. On error, it returns undef with an appropriate error +# message set in $ERROR. +#------------------------------------------------------------------------ + +sub as_perl { + my ($class, $content) = @_; + my ($block, $defblocks, $metadata) = @$content{ qw( BLOCK DEFBLOCKS METADATA ) }; + + $block =~ s/\n/\n /g; + $block =~ s/\s+$//; + + $defblocks = join('', map { + my $code = $defblocks->{ $_ }; + $code =~ s/\n/\n /g; + $code =~ s/\s*$//; + " '$_' => $code,\n"; + } keys %$defblocks); + $defblocks =~ s/\s+$//; + + $metadata = join('', map { + my $x = $metadata->{ $_ }; + $x =~ s/(['\\])/\\$1/g; + " '$_' => '$x',\n"; + } keys %$metadata); + $metadata =~ s/\s+$//; + + return <<EOF +#------------------------------------------------------------------------ +# Compiled template generated by the Template Toolkit version $Template::VERSION +#------------------------------------------------------------------------ + +$class->new({ + METADATA => { +$metadata + }, + BLOCK => $block, + DEFBLOCKS => { +$defblocks + }, +}); +EOF +} + + +#------------------------------------------------------------------------ +# write_perl_file($filename, \%content) +# +# This method calls as_perl() to generate the Perl code to represent a +# compiled template with the content passed as the second argument. +# It then writes this to the file denoted by the first argument. +# +# Returns 1 on success. On error, sets the $ERROR package variable +# to contain an error message and returns undef. +#------------------------------------------------------------------------ + +sub write_perl_file { + my ($class, $file, $content) = @_; + my ($fh, $tmpfile); + + return $class->error("invalid filename: $file") + unless $file =~ /^(.+)$/s; + + eval { + require File::Temp; + require File::Basename; + ($fh, $tmpfile) = File::Temp::tempfile( + DIR => File::Basename::dirname($file) + ); + print $fh $class->as_perl($content) || die $!; + close($fh); + }; + return $class->error($@) if $@; + return rename($tmpfile, $file) + || $class->error($!); +} + + +#------------------------------------------------------------------------ +# catch_warnings($msg) +# +# Installed as +#------------------------------------------------------------------------ + +sub catch_warnings { + $COMPERR .= join('', @_); +} + + +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::Document - Compiled template document object + +=head1 SYNOPSIS + + use Template::Document; + + $doc = Template::Document->new({ + BLOCK => sub { # some perl code; return $some_text }, + DEFBLOCKS => { + header => sub { # more perl code; return $some_text }, + footer => sub { # blah blah blah; return $some_text }, + }, + METADATA => { + author => 'Andy Wardley', + version => 3.14, + } + }) || die $Template::Document::ERROR; + + print $doc->process($context); + +=head1 DESCRIPTION + +This module defines an object class whose instances represent compiled +template documents. The Template::Parser module creates a +Template::Document instance to encapsulate a template as it is compiled +into Perl code. + +The constructor method, new(), expects a reference to a hash array +containing the BLOCK, DEFBLOCKS and METADATA items. The BLOCK item +should contain a reference to a Perl subroutine or a textual +representation of Perl code, as generated by the Template::Parser +module, which is then evaluated into a subroutine reference using +eval(). The DEFLOCKS item should reference a hash array containing +further named BLOCKs which may be defined in the template. The keys +represent BLOCK names and the values should be subroutine references +or text strings of Perl code as per the main BLOCK item. The METADATA +item should reference a hash array of metadata items relevant to the +document. + +The process() method can then be called on the instantiated +Template::Document object, passing a reference to a Template::Content +object as the first parameter. This will install any locally defined +blocks (DEFBLOCKS) in the the contexts() BLOCKS cache (via a call to +visit()) so that they may be subsequently resolved by the context. The +main BLOCK subroutine is then executed, passing the context reference +on as a parameter. The text returned from the template subroutine is +then returned by the process() method, after calling the context leave() +method to permit cleanup and de-registration of named BLOCKS previously +installed. + +An AUTOLOAD method provides access to the METADATA items for the document. +The Template::Service module installs a reference to the main +Template::Document object in the stash as the 'template' variable. +This allows metadata items to be accessed from within templates, +including PRE_PROCESS templates. + +header: + + <html> + <head> + <title>[% template.title %] + </head> + ... + +Template::Document objects are usually created by the Template::Parser +but can be manually instantiated or sub-classed to provide custom +template components. + +=head1 METHODS + +=head2 new(\%config) + +Constructor method which accept a reference to a hash array containing the +structure as shown in this example: + + $doc = Template::Document->new({ + BLOCK => sub { # some perl code; return $some_text }, + DEFBLOCKS => { + header => sub { # more perl code; return $some_text }, + footer => sub { # blah blah blah; return $some_text }, + }, + METADATA => { + author => 'Andy Wardley', + version => 3.14, + } + }) || die $Template::Document::ERROR; + +BLOCK and DEFBLOCKS items may be expressed as references to Perl subroutines +or as text strings containing Perl subroutine definitions, as is generated +by the Template::Parser module. These are evaluated into subroutine references +using eval(). + +Returns a new Template::Document object or undef on error. The error() class +method can be called, or the $ERROR package variable inspected to retrieve +the relevant error message. + +=head2 process($context) + +Main processing routine for the compiled template document. A reference to +a Template::Context object should be passed as the first parameter. The +method installs any locally defined blocks via a call to the context +visit() method, processes it's own template, passing the context reference +by parameter and then calls leave() in the context to allow cleanup. + + print $doc->process($context); + +Returns a text string representing the generated output for the template. +Errors are thrown via die(). + +=head2 block() + +Returns a reference to the main BLOCK subroutine. + +=head2 blocks() + +Returns a reference to the hash array of named DEFBLOCKS subroutines. + +=head2 AUTOLOAD + +An autoload method returns METADATA items. + + print $doc->author(); + +=head1 PACKAGE SUB-ROUTINES + +=head2 write_perl_file(\%config) + +This package subroutine is provided to effect persistance of compiled +templates. If the COMPILE_EXT option (to indicate a file extension +for saving compiled templates) then the Template::Parser module calls +this subroutine before calling the new() constructor. At this stage, +the parser has a representation of the template as text strings +containing Perl code. We can write that to a file, enclosed in a +small wrapper which will allow us to susequently require() the file +and have Perl parse and compile it into a Template::Document. Thus we +have persistance of compiled templates. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.71, 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|Template>, L<Template::Parser|Template::Parser> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Exception.pm b/lib/Template/Exception.pm new file mode 100644 index 0000000..9a95af7 --- /dev/null +++ b/lib/Template/Exception.pm @@ -0,0 +1,254 @@ +#============================================================= -*-Perl-*- +# +# Template::Exception +# +# DESCRIPTION +# Module implementing a generic exception class used for error handling +# in the Template Toolkit. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#------------------------------------------------------------------------ +# +# $Id: Exception.pm,v 2.64 2004/01/13 16:19:10 abw Exp $ +# +#======================================================================== + + +package Template::Exception; + +require 5.005; + +use strict; +use vars qw( $VERSION ); + +use constant TYPE => 0; +use constant INFO => 1; +use constant TEXT => 2; +use overload q|""| => "as_string", fallback => 1; + + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); + + +#------------------------------------------------------------------------ +# new($type, $info, \$text) +# +# Constructor method used to instantiate a new Template::Exception +# object. The first parameter should contain the exception type. This +# can be any arbitrary string of the caller's choice to represent a +# specific exception. The second parameter should contain any +# information (i.e. error message or data reference) relevant to the +# specific exception event. The third optional parameter may be a +# reference to a scalar containing output text from the template +# block up to the point where the exception was thrown. +#------------------------------------------------------------------------ + +sub new { + my ($class, $type, $info, $textref) = @_; + bless [ $type, $info, $textref ], $class; +} + + +#------------------------------------------------------------------------ +# type() +# info() +# type_info() +# +# Accessor methods to return the internal TYPE and INFO fields. +#------------------------------------------------------------------------ + +sub type { + $_[0]->[ TYPE ]; +} + +sub info { + $_[0]->[ INFO ]; +} + +sub type_info { + my $self = shift; + @$self[ TYPE, INFO ]; +} + +#------------------------------------------------------------------------ +# text() +# text(\$pretext) +# +# Method to return the text referenced by the TEXT member. A text +# reference may be passed as a parameter to supercede the existing +# member. The existing text is added to the *end* of the new text +# before being stored. This facility is provided for template blocks +# to gracefully de-nest when an exception occurs and allows them to +# reconstruct their output in the correct order. +#------------------------------------------------------------------------ + +sub text { + my ($self, $newtextref) = @_; + my $textref = $self->[ TEXT ]; + + if ($newtextref) { + $$newtextref .= $$textref if $textref && $textref ne $newtextref; + $self->[ TEXT ] = $newtextref; + return ''; + + } + elsif ($textref) { + return $$textref; + } + else { + return ''; + } +} + + +#------------------------------------------------------------------------ +# as_string() +# +# Accessor method to return a string indicating the exception type and +# information. +#------------------------------------------------------------------------ + +sub as_string { + my $self = shift; + return $self->[ TYPE ] . ' error - ' . $self->[ INFO ]; +} + + +#------------------------------------------------------------------------ +# select_handler(@types) +# +# Selects the most appropriate handler for the exception TYPE, from +# the list of types passed in as parameters. The method returns the +# item which is an exact match for TYPE or the closest, more +# generic handler (e.g. foo being more generic than foo.bar, etc.) +#------------------------------------------------------------------------ + +sub select_handler { + my ($self, @options) = @_; + my $type = $self->[ TYPE ]; + my %hlut; + @hlut{ @options } = (1) x @options; + + while ($type) { + return $type if $hlut{ $type }; + + # strip .element from the end of the exception type to find a + # more generic handler + $type =~ s/\.?[^\.]*$//; + } + return undef; +} + +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::Exception - Exception handling class module + +=head1 SYNOPSIS + + use Template::Exception; + + my $exception = Template::Exception->new($type, $info); + $type = $exception->type; + $info = $exception->info; + ($type, $info) = $exception->type_info; + + print $exception->as_string(); + + $handler = $exception->select_handler(\@candidates); + +=head1 DESCRIPTION + +The Template::Exception module defines an object class for +representing exceptions within the template processing life cycle. +Exceptions can be raised by modules within the Template Toolkit, or +can be generated and returned by user code bound to template +variables. + + +Exceptions can be raised in a template using the THROW directive, + + [% THROW user.login 'no user id: please login' %] + +or by calling the throw() method on the current Template::Context object, + + $context->throw('user.passwd', 'Incorrect Password'); + $context->throw('Incorrect Password'); # type 'undef' + +or from Perl code by calling die() with a Template::Exception object, + + die (Template::Exception->new('user.denied', 'Invalid User ID')); + +or by simply calling die() with an error string. This is +automagically caught and converted to an exception of 'undef' +type which can then be handled in the usual way. + + die "I'm sorry Dave, I can't do that"; + + + +Each exception is defined by its type and a information component +(e.g. error message). The type can be any identifying string and may +contain dotted components (e.g. 'foo', 'foo.bar', 'foo.bar.baz'). +Exception types are considered to be hierarchical such that 'foo.bar' +would be a specific type of the more general 'foo' type. + +=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|Template>, L<Template::Context|Template::Context> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/FAQ.pod b/lib/Template/FAQ.pod new file mode 100644 index 0000000..0807ace --- /dev/null +++ b/lib/Template/FAQ.pod @@ -0,0 +1,329 @@ +#============================================================= -*-perl-*- +# +# Template::FAQ +# +# DESCRIPTION +# This is the Frequently Asked Questions list for the Template +# Toolkit. More accurately, it's a very thin placeholder for where +# the FAQ will soon be. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# 2.69 +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::FAQ - Frequently Asked Questions about the Template Toolkit + +=head1 DESCRIPTION + +This is the Frequently Asked Questions list for the Template Toolkit. +More accurately, it's a very thin placeholder for where the FAQ will +soon be. + + +=head1 Template Toolkit Language + +=head2 Why doesn't [% a = b IF c %] work as expected? + +Because the parser interprets it as + + [% a = (b IF c) %] + +Do this instead: + + [% SET a = b IF c %] + +=head2 If I'm using TT to write out a TT template, is there a good way to escape [% and %]? + +You can do this: + + [% stag = "[\%" + etag = "%\]" + %] + +and then: + + [% stag; 'hello'; etag %] + +Or something like: + + [% TAGS [- -] %] + [- INCLUDE foo -] # is a directive + [% INCLUDE foo %] # not a directive, just plain text, passed through + +=head2 How do I iterate over a hash? + +This is covered in the L<Template::Manual::VMethods|VMethods> section +of the manual page. A list of all the keys that are in the hash can +be obtained with the 'keys' virtual method. You can then iterate +over that list and by looking up each key in turn get the value. + + [% FOREACH key = product.keys %] + [% key %] => [% product.$key %] + [% END %] + +=head1 Plugins + +=head2 How do I get the Table plugin to order data across rather than down? + +Order the data into rows: + + Steve Karen Jeff + Brooklyn Nantucket Fairfax + NY MA VA + + [% USE table(data, rows=3) %] + +Then ask for each column + + [% FOREACH column = table.cols %] + +And then print each item in the column going across the output rows + + [% FOREACH item = column %] + <td>[% item %]</td> + [% END %] + +=head2 Accessing Cookies + +Jeff Boes E<lt>jboes@nexcerpt.comE<gt> asks: + + Does anyone have a quick-n-dirty approach to accessing + cookies from templates? + +Jonas Liljegren answers: + + [% USE CGI %] + + <p>The value is [% CGI.cookie('cookie_name') | html %] + + +=head1 Extending the Template Toolkit + +=head2 Can I serve templates from a database? + +Short answer: yes, Chris Nandor has done this for Slash. You need to +subclass Template::Provider. See the mailing list archives for further +info. + +=head2 Can I fetch templates via http? + +To do the job properly, you should sublcass Template::Provider to +Template::Provider::HTTP and use a PREFIX_MAP option to bind the +'http' template prefix to that particular provider (you may want to +go digging around in the F<Changes> file around version 2.01 for +more info on PREFIX_MAP - it may not be properly documented anywhere +else...yet!). e.g. (untested due to lack of existing HTTP Provider +- patches welcome!). + + use Template::Provider::HTTP; + + my $file = Template::Provider( INCLUDE_PATH => [...] ); + my $http = Template::Provider::HTTP->new(...); + my $tt2 = Template->new({ + LOAD_TEMPLATES => [ $file, $http ], + PREFIX_MAP => { + file => '0', # file:foo.html + http => '1', # http:foo.html + default => '0', # foo.html => file:foo.html + } + }); + +Now a template specified as: + + [% INCLUDE foo %] + +will be served by the 'file' provider (the default). Otherwise you +can explicitly add a prefix: + + [% INCLUDE file:foo.html %] + [% INCLUDE http:foo.html %] + [% INCLUDE http://www.xyz.com/tt2/header.tt2 %] + +This same principal can be used to create a DBI template provider. e.g. + + [% INCLUDE dbi:foo.html %] + +But similarly, alas, we don't yet have a DBI provider as part of the +Template Toolkit. There has been some talk on the mailing list about +efforts to develop DBI and/or HTTP providers but as yet no-one has +stepped forward to take up the challenge... + +In the mean time, Craig's post from the mailing list has some useful +pointers on how to acheive this using existing modules: + + To: Adam Theo <adamtheo@theoretic.com> + From: Craig Barratt <craig@arraycomm.com> + Date: Fri, 18 May 2001 17:06:59 -0700 + + > i was wondering if there is anyway to fetch a file using http:// or + > ftp:// and include that? + + Here's one way. Set the LOAD_PERL option: + + use Template; + + my $template = Template->new({ + LOAD_PERL => 1 + }); + $template->process("example.tt", { stdout => *STDOUT }) + || die $template->error(); + + and then use LWP::UserAgent and HTTP::Request: + + [% + USE ua = LWP.UserAgent; + ua.proxy("http", "http://your_proxy/"); + USE req = HTTP.Request("GET", "http://www.cpan.org"); + ua.request(req).content; + -%] + + For FTP use Net::FTP: + + [% + USE ftp = Net.FTP("ftp.cpan.org"); + x = ftp.login("anonymous", "me@here.there"); + x = ftp.cwd("/"); + x = ftp.get("welcome.msg", stdout); + x = ftp.quit; + -%] + + Normally ftp.get would write the file into the current directory. + Instead we pass stdout as a second argument so that it is written + to stdout. We set stdout to STDOUT in the variables we pass to + process. + + Craig + +=head1 Miscellaneous + +=head2 How can I configure variables on a per-request basis? + +One easy way to acheive this is to define a single PRE_PROCESS template which +loads in other configuration files based on variables defined or other +conditions. + +For example, my setup usually looks something like this: + + PRE_PROCESS => 'config/main' + +config/main: + + [% DEFAULT style = 'text' + section = template.section or 'home'; + + PROCESS config/site + + config/urls + + config/macros + + "config/style/$style" + + "config/section/$section" + + ... + %] + +This allows me to set a single 'style' variable to control which config +file gets pre-processed to set my various style options (colours, img paths, +etc). For example: + +config/style/basic: + + [% style = { + name = style # save existing 'style' var as 'style.name' + + # define various other style variables.... + col = { + back => '#ffffff' + text => '#000000' + # ...etc... + } + + logo = { + # ...etc... + } + + # ...etc... + } + %] + +Each source template can declare which section it's in via a META +directive: + + [% META + title = 'General Information' + section = 'info' + %] + + ... + +This controls which section configuration file gets loaded to set various +other variables for defining the section title, menu, etc. + +config/section/info: + + [% section = { + name = section # save 'section' var as 'section.name' + title = 'Information' + menu = [ ... ] + # ...etc... + } + %] + +This illustrates the basic principal but you can extend it to perform +pretty much any kind of per-document initialisation that you require. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.69, 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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Filters.pm b/lib/Template/Filters.pm new file mode 100644 index 0000000..a9c4846 --- /dev/null +++ b/lib/Template/Filters.pm @@ -0,0 +1,1448 @@ +#============================================================= -*-Perl-*- +# +# Template::Filters +# +# DESCRIPTION +# Defines filter plugins as used by the FILTER directive. +# +# AUTHORS +# Andy Wardley <abw@kfs.org>, with a number of filters contributed +# by Leslie Michael Orchard <deus_x@nijacode.com> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Filters.pm,v 2.77 2004/01/13 16:19:10 abw Exp $ +# +#============================================================================ + +package Template::Filters; + +require 5.004; + +use strict; +use base qw( Template::Base ); +use vars qw( $VERSION $DEBUG $FILTERS $URI_ESCAPES $PLUGIN_FILTER ); +use Template::Constants; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.77 $ =~ /(\d+)\.(\d+)/); + + +#------------------------------------------------------------------------ +# standard filters, defined in one of the following forms: +# name => \&static_filter +# name => [ \&subref, $is_dynamic ] +# If the $is_dynamic flag is set then the sub-routine reference +# is called to create a new filter each time it is requested; if +# not set, then it is a single, static sub-routine which is returned +# for every filter request for that name. +#------------------------------------------------------------------------ + +$FILTERS = { + # static filters + 'html' => \&html_filter, + 'html_para' => \&html_paragraph, + 'html_break' => \&html_para_break, + 'html_para_break' => \&html_para_break, + 'html_line_break' => \&html_line_break, + 'uri' => \&uri_filter, + 'upper' => sub { uc $_[0] }, + 'lower' => sub { lc $_[0] }, + 'ucfirst' => sub { ucfirst $_[0] }, + 'lcfirst' => sub { lcfirst $_[0] }, + 'stderr' => sub { print STDERR @_; return '' }, + 'trim' => sub { for ($_[0]) { s/^\s+//; s/\s+$// }; $_[0] }, + 'null' => sub { return '' }, + 'collapse' => sub { for ($_[0]) { s/^\s+//; s/\s+$//; s/\s+/ /g }; + $_[0] }, + + # dynamic filters + 'html_entity' => [ \&html_entity_filter_factory, 1 ], + 'indent' => [ \&indent_filter_factory, 1 ], + 'format' => [ \&format_filter_factory, 1 ], + 'truncate' => [ \&truncate_filter_factory, 1 ], + 'repeat' => [ \&repeat_filter_factory, 1 ], + 'replace' => [ \&replace_filter_factory, 1 ], + 'remove' => [ \&remove_filter_factory, 1 ], + 'eval' => [ \&eval_filter_factory, 1 ], + 'evaltt' => [ \&eval_filter_factory, 1 ], # alias + 'perl' => [ \&perl_filter_factory, 1 ], + 'evalperl' => [ \&perl_filter_factory, 1 ], # alias + 'redirect' => [ \&redirect_filter_factory, 1 ], + 'file' => [ \&redirect_filter_factory, 1 ], # alias + 'stdout' => [ \&stdout_filter_factory, 1 ], + 'latex' => [ \&latex_filter_factory, 1 ], +}; + +# name of module implementing plugin filters +$PLUGIN_FILTER = 'Template::Plugin::Filter'; + + +#======================================================================== +# -- PUBLIC METHODS -- +#======================================================================== + +#------------------------------------------------------------------------ +# fetch($name, \@args, $context) +# +# Attempts to instantiate or return a reference to a filter sub-routine +# named by the first parameter, $name, with additional constructor +# arguments passed by reference to a list as the second parameter, +# $args. A reference to the calling Template::Context object is +# passed as the third paramter. +# +# Returns a reference to a filter sub-routine or a pair of values +# (undef, STATUS_DECLINED) or ($error, STATUS_ERROR) to decline to +# deliver the filter or to indicate an error. +#------------------------------------------------------------------------ + +sub fetch { + my ($self, $name, $args, $context) = @_; + my ($factory, $is_dynamic, $filter, $error); + + $self->debug("fetch($name, ", + defined $args ? ('[ ', join(', ', @$args), ' ]') : '<no args>', ', ', + defined $context ? $context : '<no context>', + ')') if $self->{ DEBUG }; + + # allow $name to be specified as a reference to + # a plugin filter object; any other ref is + # assumed to be a coderef and hence already a filter; + # non-refs are assumed to be regular name lookups + + if (ref $name) { + if (UNIVERSAL::isa($name, $PLUGIN_FILTER)) { + $factory = $name->factory() + || return $self->error($name->error()); + } + else { + return $name; + } + } + else { + return (undef, Template::Constants::STATUS_DECLINED) + unless ($factory = $self->{ FILTERS }->{ $name } + || $FILTERS->{ $name }); + } + + # factory can be an [ $code, $dynamic ] or just $code + if (ref $factory eq 'ARRAY') { + ($factory, $is_dynamic) = @$factory; + } + else { + $is_dynamic = 0; + } + + if (ref $factory eq 'CODE') { + if ($is_dynamic) { + # if the dynamic flag is set then the sub-routine is a + # factory which should be called to create the actual + # filter... + eval { + ($filter, $error) = &$factory($context, $args ? @$args : ()); + }; + $error ||= $@; + $error = "invalid FILTER for '$name' (not a CODE ref)" + unless $error || ref($filter) eq 'CODE'; + } + else { + # ...otherwise, it's a static filter sub-routine + $filter = $factory; + } + } + else { + $error = "invalid FILTER entry for '$name' (not a CODE ref)"; + } + + if ($error) { + return $self->{ TOLERANT } + ? (undef, Template::Constants::STATUS_DECLINED) + : ($error, Template::Constants::STATUS_ERROR) ; + } + else { + return $filter; + } +} + + +#------------------------------------------------------------------------ +# store($name, \&filter) +# +# Stores a new filter in the internal FILTERS hash. The first parameter +# is the filter name, the second a reference to a subroutine or +# array, as per the standard $FILTERS entries. +#------------------------------------------------------------------------ + +sub store { + my ($self, $name, $filter) = @_; + + $self->debug("store($name, $filter)") if $self->{ DEBUG }; + + $self->{ FILTERS }->{ $name } = $filter; + return 1; +} + + +#======================================================================== +# -- PRIVATE METHODS -- +#======================================================================== + +#------------------------------------------------------------------------ +# _init(\%config) +# +# Private initialisation method. +#------------------------------------------------------------------------ + +sub _init { + my ($self, $params) = @_; + + $self->{ FILTERS } = $params->{ FILTERS } || { }; + $self->{ TOLERANT } = $params->{ TOLERANT } || 0; + $self->{ DEBUG } = ( $params->{ DEBUG } || 0 ) + & Template::Constants::DEBUG_FILTERS; + + + return $self; +} + + + +#------------------------------------------------------------------------ +# _dump() +# +# Debug method +#------------------------------------------------------------------------ + +sub _dump { + my $self = shift; + my $output = "[Template::Filters] {\n"; + my $format = " %-16s => %s\n"; + my $key; + + foreach $key (qw( TOLERANT )) { + my $val = $self->{ $key }; + $val = '<undef>' unless defined $val; + $output .= sprintf($format, $key, $val); + } + + my $filters = $self->{ FILTERS }; + $filters = join('', map { + sprintf(" $format", $_, $filters->{ $_ }); + } keys %$filters); + $filters = "{\n$filters }"; + + $output .= sprintf($format, 'FILTERS (local)' => $filters); + + $filters = $FILTERS; + $filters = join('', map { + my $f = $filters->{ $_ }; + my ($ref, $dynamic) = ref $f eq 'ARRAY' ? @$f : ($f, 0); + sprintf(" $format", $_, $dynamic ? 'dynamic' : 'static'); + } sort keys %$filters); + $filters = "{\n$filters }"; + + $output .= sprintf($format, 'FILTERS (global)' => $filters); + + $output .= '}'; + return $output; +} + + +#======================================================================== +# -- STATIC FILTER SUBS -- +#======================================================================== + +#------------------------------------------------------------------------ +# uri_filter() [% FILTER uri %] +# +# URI escape a string. This code is borrowed from Gisle Aas' URI::Escape +# module. For something so simple, I can't see any validation in making +# the user install the URI modules just for this, so we cut and paste. +# +# URI::Escape is Copyright 1995-2000 Gisle Aas. +#------------------------------------------------------------------------ + +sub uri_filter { + my $text = shift; + + # construct and cache a lookup table for escapes (faster than + # doing a sprintf() for every character in every string each + # time) + $URI_ESCAPES ||= { + map { ( chr($_), sprintf("%%%02X", $_) ) } (0..255), + }; + + $text =~ s/([^;\/?:@&=+\$,A-Za-z0-9\-_.!~*'()])/$URI_ESCAPES->{$1}/g; + $text; +} + + +#------------------------------------------------------------------------ +# html_filter() [% FILTER html %] +# +# Convert any '<', '>' or '&' characters to the HTML equivalents, '<', +# '>' and '&', respectively. +#------------------------------------------------------------------------ + +sub html_filter { + my $text = shift; + for ($text) { + s/&/&/g; + s/</</g; + s/>/>/g; + s/"/"/g; + } + return $text; +} + + +#------------------------------------------------------------------------ +# html_paragraph() [% FILTER html_para %] +# +# Wrap each paragraph of text (delimited by two or more newlines) in the +# <p>...</p> HTML tags. +#------------------------------------------------------------------------ + +sub html_paragraph { + my $text = shift; + return "<p>\n" + . join("\n</p>\n\n<p>\n", split(/(?:\r?\n){2,}/, $text)) + . "</p>\n"; +} + + +#------------------------------------------------------------------------ +# html_para_break() [% FILTER html_para_break %] +# +# Join each paragraph of text (delimited by two or more newlines) with +# <br><br> HTML tags. +#------------------------------------------------------------------------ + +sub html_para_break { + my $text = shift; + $text =~ s|(\r?\n){2,}|$1<br />$1<br />$1|g; + return $text; +} + +#------------------------------------------------------------------------ +# html_line_break() [% FILTER html_line_break %] +# +# replaces any newlines with <br> HTML tags. +#------------------------------------------------------------------------ + +sub html_line_break { + my $text = shift; + $text =~ s|(\r?\n)|<br />$1|g; + return $text; +} + +#======================================================================== +# -- DYNAMIC FILTER FACTORIES -- +#======================================================================== + +#------------------------------------------------------------------------ +# html_entity_filter_factory(\%options) [% FILTER html %] +# +# Dynamic version of the static html filter which attempts to locate the +# Apache::Util or HTML::Entities modules to perform full entity encoding +# of the text passed. Returns an exception if one or other of the +# modules can't be located. +#------------------------------------------------------------------------ + +sub html_entity_filter_factory { + my $context = shift; + + # if Apache::Util is installed then we use it + eval { + require Apache::Util; + Apache::Util::escape_html(''); + }; + return \&Apache::Util::escape_html + unless $@; + + # otherwise if HTML::Entities is installed then we use that + eval { + require HTML::Entities; + }; + return \&HTML::Entities::encode_entities + unless $@; + + return (undef, Template::Exception->new( html_entity => + 'cannot locate Apache::Util or HTML::Entities' )); + +} + + +#------------------------------------------------------------------------ +# indent_filter_factory($pad) [% FILTER indent(pad) %] +# +# Create a filter to indent text by a fixed pad string or when $pad is +# numerical, a number of space. +#------------------------------------------------------------------------ + +sub indent_filter_factory { + my ($context, $pad) = @_; + $pad = 4 unless defined $pad; + $pad = ' ' x $pad if $pad =~ /^\d+$/; + + return sub { + my $text = shift; + $text = '' unless defined $text; + $text =~ s/^/$pad/mg; + return $text; + } +} + +#------------------------------------------------------------------------ +# format_filter_factory() [% FILTER format(format) %] +# +# Create a filter to format text according to a printf()-like format +# string. +#------------------------------------------------------------------------ + +sub format_filter_factory { + my ($context, $format) = @_; + $format = '%s' unless defined $format; + + return sub { + my $text = shift; + $text = '' unless defined $text; + return join("\n", map{ sprintf($format, $_) } split(/\n/, $text)); + } +} + + +#------------------------------------------------------------------------ +# repeat_filter_factory($n) [% FILTER repeat(n) %] +# +# Create a filter to repeat text n times. +#------------------------------------------------------------------------ + +sub repeat_filter_factory { + my ($context, $iter) = @_; + $iter = 1 unless defined $iter and length $iter; + + return sub { + my $text = shift; + $text = '' unless defined $text; + return join('\n', $text) x $iter; + } +} + + +#------------------------------------------------------------------------ +# replace_filter_factory($s, $r) [% FILTER replace(search, replace) %] +# +# Create a filter to replace 'search' text with 'replace' +#------------------------------------------------------------------------ + +sub replace_filter_factory { + my ($context, $search, $replace) = @_; + $search = '' unless defined $search; + $replace = '' unless defined $replace; + + return sub { + my $text = shift; + $text = '' unless defined $text; + $text =~ s/$search/$replace/g; + return $text; + } +} + + +#------------------------------------------------------------------------ +# remove_filter_factory($text) [% FILTER remove(text) %] +# +# Create a filter to remove 'search' string from the input text. +#------------------------------------------------------------------------ + +sub remove_filter_factory { + my ($context, $search) = @_; + + return sub { + my $text = shift; + $text = '' unless defined $text; + $text =~ s/$search//g; + return $text; + } +} + + +#------------------------------------------------------------------------ +# truncate_filter_factory($n) [% FILTER truncate(n) %] +# +# Create a filter to truncate text after n characters. +#------------------------------------------------------------------------ + +sub truncate_filter_factory { + my ($context, $len) = @_; + $len = 32 unless defined $len; + + return sub { + my $text = shift; + return $text if length $text < $len; + return substr($text, 0, $len - 3) . "..."; + } +} + + +#------------------------------------------------------------------------ +# eval_filter_factory [% FILTER eval %] +# +# Create a filter to evaluate template text. +#------------------------------------------------------------------------ + +sub eval_filter_factory { + my $context = shift; + + return sub { + my $text = shift; + $context->process(\$text); + } +} + + +#------------------------------------------------------------------------ +# perl_filter_factory [% FILTER perl %] +# +# Create a filter to process Perl text iff the context EVAL_PERL flag +# is set. +#------------------------------------------------------------------------ + +sub perl_filter_factory { + my $context = shift; + my $stash = $context->stash; + + return (undef, Template::Exception->new('perl', 'EVAL_PERL is not set')) + unless $context->eval_perl(); + + return sub { + my $text = shift; + local($Template::Perl::context) = $context; + local($Template::Perl::stash) = $stash; + my $out = eval <<EOF; +package Template::Perl; +\$stash = \$context->stash(); +$text +EOF + $context->throw($@) if $@; + return $out; + } +} + + +#------------------------------------------------------------------------ +# redirect_filter_factory($context, $file) [% FILTER redirect(file) %] +# +# Create a filter to redirect the block text to a file. +#------------------------------------------------------------------------ + +sub redirect_filter_factory { + my ($context, $file, $options) = @_; + my $outpath = $context->config->{ OUTPUT_PATH }; + + return (undef, Template::Exception->new('redirect', + 'OUTPUT_PATH is not set')) + unless $outpath; + + $options = { binmode => $options } unless ref $options; + + sub { + my $text = shift; + my $outpath = $context->config->{ OUTPUT_PATH } + || return ''; + $outpath .= "/$file"; + my $error = Template::_output($outpath, \$text, $options); + die Template::Exception->new('redirect', $error) + if $error; + return ''; + } +} + + +#------------------------------------------------------------------------ +# stdout_filter_factory($context, $binmode) [% FILTER stdout(binmode) %] +# +# Create a filter to print a block to stdout, with an optional binmode. +#------------------------------------------------------------------------ + +sub stdout_filter_factory { + my ($context, $options) = @_; + + $options = { binmode => $options } unless ref $options; + + sub { + my $text = shift; + binmode(STDOUT) if $options->{ binmode }; + print STDOUT $text; + return ''; + } +} + + +#------------------------------------------------------------------------ +# latex_filter_factory($context, $outputType) [% FILTER latex(outputType) %] +# +# Return a filter sub that converts a (hopefully) complete LaTeX source +# file to either "ps", "dvi", or "pdf". Output type should be "ps", "dvi" +# or "pdf" (pdf is default). +# +# Creates a temporary directory below File::Spec->tmpdir() (often /tmp) +# and writes the text into doc.tex. It then runs either pdflatex or +# latex and optionally dvips. Based on the exit status either returns +# the entire doc.(pdf|ps|dvi) output or throws an error with a summary +# of the error messages from doc.log. +# +# Written by Craig Barratt, Apr 28 2001. +# Win32 additions by Richard Tietjen. +#------------------------------------------------------------------------ +use File::Path; +use File::Spec; +use Cwd; + +sub latex_filter_factory +{ + my($context, $output) = @_; + + $output = lc($output); + my $fName = "latex"; + my($LaTeXPath, $PdfLaTeXPath, $DviPSPath) + = @{Template::Config->latexpaths()}; + if ( $output eq "ps" || $output eq "dvi" ) { + $context->throw($fName, + "latex not installed (see Template::Config::LATEX_PATH)") + if ( $LaTeXPath eq "" ); + } else { + $output = "pdf"; + $LaTeXPath = $PdfLaTeXPath; + $context->throw($fName, + "pdflatex not installed (see Template::Config::PDFLATEX_PATH)") + if ( $LaTeXPath eq "" ); + } + if ( $output eq "ps" && $DviPSPath eq "" ) { + $context->throw($fName, + "dvips not installed (see Template::Config::DVIPS_PATH)"); + } + if ( $^O !~ /^(MacOS|os2|VMS)$/i ) { + return sub { + local(*FH); + my $text = shift; + my $tmpRootDir = File::Spec->tmpdir(); + my $cnt = 0; + my($tmpDir, $fileName, $devnull); + my $texDoc = 'doc'; + + do { + $tmpDir = File::Spec->catdir($tmpRootDir, + "tt2latex$$" . "_$cnt"); + $cnt++; + } while ( -e $tmpDir ); + mkpath($tmpDir, 0, 0700); + $context->throw($fName, "can't create temp dir $tmpDir") + if ( !-d $tmpDir ); + $fileName = File::Spec->catfile($tmpDir, "$texDoc.tex"); + $devnull = File::Spec->devnull(); + if ( !open(FH, ">$fileName") ) { + rmtree($tmpDir); + $context->throw($fName, "can't open $fileName for output"); + } + print(FH $text); + close(FH); + + # latex must run in tmpDir directory + my $currDir = cwd(); + if ( !chdir($tmpDir) ) { + rmtree($tmpDir); + $context->throw($fName, "can't chdir $tmpDir"); + } + # + # We don't need to quote the backslashes on windows, but we + # do on other OSs + # + my $LaTeX_arg = "\\nonstopmode\\input{$texDoc}"; + $LaTeX_arg = "'$LaTeX_arg'" if ( $^O ne 'MSWin32' ); + if ( system("$LaTeXPath $LaTeX_arg" + . " 1>$devnull 2>$devnull 0<$devnull") ) { + my $texErrs = ""; + $fileName = File::Spec->catfile($tmpDir, "$texDoc.log"); + if ( open(FH, "<$fileName") ) { + my $state = 0; + # + # Try to extract just the interesting errors from + # the verbose log file + # + while ( <FH> ) { + # + # TeX errors seems to start with a "!" at the + # start of the line, and are followed several + # lines later by a line designator of the + # form "l.nnn" where nnn is the line number. + # We make sure we pick up every /^!/ line, and + # the first /^l.\d/ line after each /^!/ line. + # + if ( /^(!.*)/ ) { + $texErrs .= $1 . "\n"; + $state = 1; + } + if ( $state == 1 && /^(l\.\d.*)/ ) { + $texErrs .= $1 . "\n"; + $state = 0; + } + } + close(FH); + } else { + $texErrs = "Unable to open $fileName\n"; + } + my $ok = chdir($currDir); + rmtree($tmpDir); + $context->throw($fName, "can't chdir $currDir") if ( !$ok ); + $context->throw($fName, "latex exited with errors:\n$texErrs"); + } + if ( $output eq "ps" ) { + $fileName = File::Spec->catfile($tmpDir, "$texDoc.dvi"); + if ( system("$DviPSPath $texDoc -o" + . " 1>$devnull 2>$devnull 0<$devnull") ) { + my $ok = chdir($currDir); + rmtree($tmpDir); + $context->throw($fName, "can't chdir $currDir") if ( !$ok ); + $context->throw($fName, "can't run $DviPSPath $fileName"); + } + } + if ( !chdir($currDir) ) { + rmtree($tmpDir); + $context->throw($fName, "can't chdir $currDir"); + } + + my $retStr; + $fileName = File::Spec->catfile($tmpDir, "$texDoc.$output"); + if ( open(FH, $fileName) ) { + local $/ = undef; # slurp file in one go + binmode(FH); + $retStr = <FH>; + close(FH); + } else { + rmtree($tmpDir); + $context->throw($fName, "Can't open output file $fileName"); + } + rmtree($tmpDir); + return $retStr; + } + } else { + $context->throw("$fName not yet supported on $^O OS." + . " Please contribute code!!"); + } +} + +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::Filters - Post-processing filters for template blocks + +=head1 SYNOPSIS + + use Template::Filters; + + $filters = Template::Filters->new(\%config); + + ($filter, $error) = $filters->fetch($name, \@args, $context); + +=head1 DESCRIPTION + +The Template::Filters module implements a provider for creating and/or +returning subroutines that implement the standard filters. Additional +custom filters may be provided via the FILTERS options. + +=head1 METHODS + +=head2 new(\%params) + +Constructor method which instantiates and returns a reference to a +Template::Filters object. A reference to a hash array of configuration +items may be passed as a parameter. These are described below. + + my $filters = Template::Filters->new({ + FILTERS => { ... }, + }); + + my $template = Template->new({ + LOAD_FILTERS => [ $filters ], + }); + +A default Template::Filters module is created by the Template.pm module +if the LOAD_FILTERS option isn't specified. All configuration parameters +are forwarded to the constructor. + + $template = Template->new({ + FILTERS => { ... }, + }); + +=head2 fetch($name, \@args, $context) + +Called to request that a filter of a given name be provided. The name +of the filter should be specified as the first parameter. This should +be one of the standard filters or one specified in the FILTERS +configuration hash. The second argument should be a reference to an +array containing configuration parameters for the filter. This may be +specified as 0, or undef where no parameters are provided. The third +argument should be a reference to the current Template::Context +object. + +The method returns a reference to a filter sub-routine on success. It +may also return (undef, STATUS_DECLINE) to decline the request, to allow +delegation onto other filter providers in the LOAD_FILTERS chain of +responsibility. On error, ($error, STATUS_ERROR) is returned where $error +is an error message or Template::Exception object indicating the error +that occurred. + +When the TOLERANT option is set, errors are automatically downgraded to +a STATUS_DECLINE response. + + +=head1 CONFIGURATION OPTIONS + +The following list details the configuration options that can be provided +to the Template::Filters new() constructor. + +=over 4 + + + + +=item FILTERS + +The FILTERS option can be used to specify custom filters which can +then be used with the FILTER directive like any other. These are +added to the standard filters which are available by default. Filters +specified via this option will mask any standard filters of the same +name. + +The FILTERS option should be specified as a reference to a hash array +in which each key represents the name of a filter. The corresponding +value should contain a reference to an array containing a subroutine +reference and a flag which indicates if the filter is static (0) or +dynamic (1). A filter may also be specified as a solitary subroutine +reference and is assumed to be static. + + $filters = Template::Filters->new({ + FILTERS => { + 'sfilt1' => \&static_filter, # static + 'sfilt2' => [ \&static_filter, 0 ], # same as above + 'dfilt1' => [ \&dyanamic_filter_factory, 1 ], + }, + }); + +Additional filters can be specified at any time by calling the +define_filter() method on the current Template::Context object. +The method accepts a filter name, a reference to a filter +subroutine and an optional flag to indicate if the filter is +dynamic. + + my $context = $template->context(); + $context->define_filter('new_html', \&new_html); + $context->define_filter('new_repeat', \&new_repeat, 1); + +Static filters are those where a single subroutine reference is used +for all invocations of a particular filter. Filters that don't accept +any configuration parameters (e.g. 'html') can be implemented +statically. The subroutine reference is simply returned when that +particular filter is requested. The subroutine is called to filter +the output of a template block which is passed as the only argument. +The subroutine should return the modified text. + + sub static_filter { + my $text = shift; + # do something to modify $text... + return $text; + } + +The following template fragment: + + [% FILTER sfilt1 %] + Blah blah blah. + [% END %] + +is approximately equivalent to: + + &static_filter("\nBlah blah blah.\n"); + +Filters that can accept parameters (e.g. 'truncate') should be +implemented dynamically. In this case, the subroutine is taken to be +a filter 'factory' that is called to create a unique filter subroutine +each time one is requested. A reference to the current +Template::Context object is passed as the first parameter, followed by +any additional parameters specified. The subroutine should return +another subroutine reference (usually a closure) which implements the +filter. + + sub dynamic_filter_factory { + my ($context, @args) = @_; + + return sub { + my $text = shift; + # do something to modify $text... + return $text; + } + } + +The following template fragment: + + [% FILTER dfilt1(123, 456) %] + Blah blah blah + [% END %] + +is approximately equivalent to: + + my $filter = &dynamic_filter_factory($context, 123, 456); + &$filter("\nBlah blah blah.\n"); + +See the FILTER directive for further examples. + + + + +=item TOLERANT + +The TOLERANT flag is used by the various Template Toolkit provider +modules (Template::Provider, Template::Plugins, Template::Filters) to +control their behaviour when errors are encountered. By default, any +errors are reported as such, with the request for the particular +resource (template, plugin, filter) being denied and an exception +raised. When the TOLERANT flag is set to any true values, errors will +be silently ignored and the provider will instead return +STATUS_DECLINED. This allows a subsequent provider to take +responsibility for providing the resource, rather than failing the +request outright. If all providers decline to service the request, +either through tolerated failure or a genuine disinclination to +comply, then a 'E<lt>resourceE<gt> not found' exception is raised. + + + + +=item DEBUG + +The DEBUG option can be used to enable debugging messages from the +Template::Filters module by setting it to include the DEBUG_FILTERS +value. + + use Template::Constants qw( :debug ); + + my $template = Template->new({ + DEBUG => DEBUG_FILTERS | DEBUG_PLUGINS, + }); + + + + +=back + +=head1 TEMPLATE TOOLKIT FILTERS + +The following standard filters are distributed with the Template Toolkit. + + + +=head2 format(format) + +The 'format' filter takes a format string as a parameter (as per +printf()) and formats each line of text accordingly. + + [% FILTER format('<!-- %-40s -->') %] + This is a block of text filtered + through the above format. + [% END %] + +output: + + <!-- This is a block of text filtered --> + <!-- through the above format. --> + +=head2 upper + +Folds the input to UPPER CASE. + + [% "hello world" FILTER upper %] + +output: + + HELLO WORLD + +=head2 lower + +Folds the input to lower case. + + [% "Hello World" FILTER lower %] + +output: + + hello world + +=head2 ucfirst + +Folds the first character of the input to UPPER CASE. + + [% "hello" FILTER ucfirst %] + +output: + + Hello + +=head2 lcfirst + +Folds the first character of the input to lower case. + + [% "HELLO" FILTER lcfirst %] + +output: + + hELLO + +=head2 trim + +Trims any leading or trailing whitespace from the input text. Particularly +useful in conjunction with INCLUDE, PROCESS, etc., having the same effect +as the TRIM configuration option. + + [% INCLUDE myfile | trim %] + +=head2 collapse + +Collapse any whitespace sequences in the input text into a single space. +Leading and trailing whitespace (which would be reduced to a single space) +is removed, as per trim. + + [% FILTER collapse %] + + The cat + + sat on + + the mat + + [% END %] + +output: + + The cat sat on the mat + +=head2 html + +Converts the characters 'E<lt>', 'E<gt>' and '&' to '<', '>' and +'&', respectively, protecting them from being interpreted as +representing HTML tags or entities. + + [% FILTER html %] + Binary "<=>" returns -1, 0, or 1 depending on... + [% END %] + +output: + + Binary "<=>" returns -1, 0, or 1 depending on... + +=head2 html_entity + +The html filter is fast and simple but it doesn't encode the full +range of HTML entities that your text may contain. The html_entity +filter uses either the Apache::Util module (which is written in C and +is therefore faster) or the HTML::Entities module (written in Perl but +equally as comprehensive) to perform the encoding. If one or other of +these modules are installed on your system then the text will be +encoded (via the escape_html() or encode_entities() subroutines +respectively) to convert all extended characters into their +appropriate HTML entities (e.g. converting 'é' to 'é'). If +neither module is available on your system then an 'html_entity' exception +will be thrown reporting an appropriate message. + +For further information on HTML entity encoding, see +http://www.w3.org/TR/REC-html40/sgml/entities.html. + +=head2 html_para + +This filter formats a block of text into HTML paragraphs. A sequence of +two or more newlines is used as the delimiter for paragraphs which are +then wrapped in HTML E<lt>pE<gt>...E<lt>/pE<gt> tags. + + [% FILTER html_para %] + The cat sat on the mat. + + Mary had a little lamb. + [% END %] + +output: + + <p> + The cat sat on the mat. + </p> + + <p> + Mary had a little lamb. + </p> + +=head2 html_break / html_para_break + +Similar to the html_para filter described above, but uses the HTML tag +sequence E<lt>brE<gt>E<lt>brE<gt> to join paragraphs. + + [% FILTER html_break %] + The cat sat on the mat. + + Mary had a little lamb. + [% END %] + +output: + + The cat sat on the mat. + <br> + <br> + Mary had a little lamb. + +=head2 html_line_break + +This filter replaces any newlines with E<lt>brE<gt> HTML tags, +thus preserving the line breaks of the original text in the +HTML output. + + [% FILTER html_line_break %] + The cat sat on the mat. + Mary had a little lamb. + [% END %] + +output: + + The cat sat on the mat.<br> + Mary had a little lamb.<br> + +=head2 uri + +This filter URI escapes the input text, converting any characters +outside of the permitted URI character set (as defined by RFC 2396) +into a C<%nn> hex escape. + + [% 'my file.html' | uri %] + +output: + + my%20file.html + +Note that URI escaping isn't always enough when generating hyperlinks in +an HTML document. The C<&> character, for example, is valid in a URI and +will not be escaped by the URI filter. In this case you should also filter +the text through the 'html' filter. + + <a href="[% filename | uri | html %]">click here</a> + +=head2 indent(pad) + +Indents the text block by a fixed pad string or width. The 'pad' argument +can be specified as a string, or as a numerical value to indicate a pad +width (spaces). Defaults to 4 spaces if unspecified. + + [% FILTER indent('ME> ') %] + blah blah blah + cabbages, rhubard, onions + [% END %] + +output: + + ME> blah blah blah + ME> cabbages, rhubard, onions + +=head2 truncate(length) + +Truncates the text block to the length specified, or a default length of +32. Truncated text will be terminated with '...' (i.e. the '...' falls +inside the required length, rather than appending to it). + + [% FILTER truncate(21) %] + I have much to say on this matter that has previously + been said on more than one occasion. + [% END %] + +output: + + I have much to say... + +=head2 repeat(iterations) + +Repeats the text block for as many iterations as are specified (default: 1). + + [% FILTER repeat(3) %] + We want more beer and we want more beer, + [% END %] + We are the more beer wanters! + +output: + + We want more beer and we want more beer, + We want more beer and we want more beer, + We want more beer and we want more beer, + We are the more beer wanters! + +=head2 remove(string) + +Searches the input text for any occurrences of the specified string and +removes them. A Perl regular expression may be specified as the search +string. + + [% "The cat sat on the mat" FILTER remove('\s+') %] + +output: + + Thecatsatonthemat + +=head2 replace(search, replace) + +Similar to the remove filter described above, but taking a second parameter +which is used as a replacement string for instances of the search string. + + [% "The cat sat on the mat" | replace('\s+', '_') %] + +output: + + The_cat_sat_on_the_mat + +=head2 redirect(file, options) + +The 'redirect' filter redirects the output of the block into a separate +file, specified relative to the OUTPUT_PATH configuration item. + + [% FOREACH user = myorg.userlist %] + [% FILTER redirect("users/${user.id}.html") %] + [% INCLUDE userinfo %] + [% END %] + [% END %] + +or more succinctly, using side-effect notation: + + [% INCLUDE userinfo + FILTER redirect("users/${user.id}.html") + FOREACH user = myorg.userlist + %] + +A 'file' exception will be thrown if the OUTPUT_PATH option is undefined. + +An optional 'binmode' argument can follow the filename to explicitly set +the output file to binary mode. + + [% PROCESS my/png/generator + FILTER redirect("images/logo.png", binmode=1) %] + +For backwards compatibility with earlier versions, a single true/false +value can be used to set binary mode. + + [% PROCESS my/png/generator + FILTER redirect("images/logo.png", 1) %] + +For the sake of future compatibility and clarity, if nothing else, we +would strongly recommend you explicitly use the named 'binmode' option +as shown in the first example. + +=head2 eval / evaltt + +The 'eval' filter evaluates the block as template text, processing +any directives embedded within it. This allows template variables to +contain template fragments, or for some method to be provided for +returning template fragments from an external source such as a +database, which can then be processed in the template as required. + + my $vars = { + fragment => "The cat sat on the [% place %]", + }; + $template->process($file, $vars); + +The following example: + + [% fragment | eval %] + +is therefore equivalent to + + The cat sat on the [% place %] + +The 'evaltt' filter is provided as an alias for 'eval'. + +=head2 perl / evalperl + +The 'perl' filter evaluates the block as Perl code. The EVAL_PERL +option must be set to a true value or a 'perl' exception will be +thrown. + + [% my_perl_code | perl %] + +In most cases, the [% PERL %] ... [% END %] block should suffice for +evaluating Perl code, given that template directives are processed +before being evaluate as Perl. Thus, the previous example could have +been written in the more verbose form: + + [% PERL %] + [% my_perl_code %] + [% END %] + +as well as + + [% FILTER perl %] + [% my_perl_code %] + [% END %] + +The 'evalperl' filter is provided as an alias for 'perl' for backwards +compatibility. + +=head2 stdout(options) + +The stdout filter prints the output generated by the enclosing block to +STDOUT. The 'binmode' option can be passed as either a named parameter +or a single argument to set STDOUT to binary mode (see the +binmode perl function). + + [% PROCESS something/cool + FILTER stdout(binmode=1) # recommended %] + + [% PROCESS something/cool + FILTER stdout(1) # alternate %] + +The stdout filter can be used to force binmode on STDOUT, or also inside +redirect, null or stderr blocks to make sure that particular output goes +to stdout. See the null filter below for an example. + +=head2 stderr + +The stderr filter prints the output generated by the enclosing block to +STDERR. + +=head2 null + +The null filter prints nothing. This is useful for plugins whose +methods return values that you don't want to appear in the output. +Rather than assigning every plugin method call to a dummy variable +to silence it, you can wrap the block in a null filter: + + [% FILTER null; + USE im = GD.Image(100,100); + black = im.colorAllocate(0, 0, 0); + red = im.colorAllocate(255,0, 0); + blue = im.colorAllocate(0, 0, 255); + im.arc(50,50,95,75,0,360,blue); + im.fill(50,50,red); + im.png | stdout(1); + END; + -%] + +Notice the use of the stdout filter to ensure that a particular expression +generates output to stdout (in this case in binary mode). + +=head2 latex(outputType) + +Passes the text block to LaTeX and produces either PDF, DVI or +PostScript output. The 'outputType' argument determines the output +format and it should be set to one of the strings: "pdf" (default), +"dvi", or "ps". + +The text block should be a complete LaTeX source file. + + [% FILTER latex("pdf") -%] + \documentclass{article} + + \begin{document} + + \title{A Sample TT2 \LaTeX\ Source File} + \author{Craig Barratt} + \maketitle + + \section{Introduction} + This is some text. + + \end{document} + [% END -%] + +The output will be a PDF file. You should be careful not to prepend or +append any extraneous characters or text outside the FILTER block, +since this text will wrap the (binary) output of the latex filter. +Notice the END directive uses '-%]' for the END_TAG to remove the +trailing new line. + +One example where you might prepend text is in a CGI script where +you might include the Content-Type before the latex output, eg: + + Content-Type: application/pdf + + [% FILTER latex("pdf") -%] + \documentclass{article} + \begin{document} + ... + \end{document} + [% END -%] + +In other cases you might use the redirect filter to put the output +into a file, rather than delivering it to stdout. This might be +suitable for batch scripts: + + [% output = FILTER latex("pdf") -%] + \documentclass{article} + \begin{document} + ... + \end{document} + [% END; output | redirect("document.pdf", 1) -%] + +(Notice the second argument to redirect to force binary mode.) + +Note that the latex filter runs one or two external programs, so it +isn't very fast. But for modest documents the performance is adequate, +even for interactive applications. + +A error of type 'latex' will be thrown if there is an error reported +by latex, pdflatex or dvips. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.77, 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|Template>, L<Template::Context|Template::Context>, L<Template::Manual::Filters|Template::Manual::Filters> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Grammar.pm b/lib/Template/Grammar.pm new file mode 100644 index 0000000..8635426 --- /dev/null +++ b/lib/Template/Grammar.pm @@ -0,0 +1,6179 @@ +#============================================================= -*-Perl-*- +# +# Template::Grammar +# +# DESCRIPTION +# Grammar file for the Template Toolkit language containing token +# definitions and parser state/rules tables generated by Parse::Yapp. +# +# AUTHOR +# Andy Wardley <abw@wardley.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#------------------------------------------------------------------------ +# +# NOTE: this module is constructed from the parser/Grammar.pm.skel +# file by running the parser/yc script. You only need to do this if +# you have modified the grammar in the parser/Parser.yp file and need +# to-recompile it. See the README in the 'parser' directory for more +# information (sub-directory of the Template distribution). +# +#------------------------------------------------------------------------ +# +# $Id: Grammar.pm,v 2.22 2004/01/13 16:19:10 abw Exp $ +# +#======================================================================== + +package Template::Grammar; + +require 5.004; + +use strict; +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.22 $ =~ /(\d+)\.(\d+)/); + +my (@RESERVED, %CMPOP, $LEXTABLE, $RULES, $STATES); +my ($factory, $rawstart); + + +#======================================================================== + +# Reserved words, comparison and binary operators +#======================================================================== + +@RESERVED = qw( + GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER BLOCK END + USE PLUGIN FILTER MACRO PERL RAWPERL TO STEP AND OR NOT DIV MOD + IF UNLESS ELSE ELSIF FOR NEXT WHILE SWITCH CASE META IN + TRY THROW CATCH FINAL LAST RETURN STOP CLEAR VIEW DEBUG + ); + +# for historical reasons, != and == are converted to ne and eq to perform +# stringwise comparison (mainly because it doesn't generate "non-numerical +# comparison" warnings which != and == can) but the others (e.g. < > <= >=) +# are not converted to their stringwise equivalents. I added 'gt' et al, +# briefly for v2.04d and then took them out again in 2.04e. + +%CMPOP = qw( + != ne + == eq + < < + > > + >= >= + <= <= +); + + +#======================================================================== +# Lexer Token Table +#======================================================================== + +# lookup table used by lexer is initialised with special-cases +$LEXTABLE = { + 'FOREACH' => 'FOR', + 'BREAK' => 'LAST', + '&&' => 'AND', + '||' => 'OR', + '!' => 'NOT', + '|' => 'FILTER', + '.' => 'DOT', + '_' => 'CAT', + '..' => 'TO', +# ':' => 'MACRO', + '=' => 'ASSIGN', + '=>' => 'ASSIGN', +# '->' => 'ARROW', + ',' => 'COMMA', + '\\' => 'REF', + 'and' => 'AND', # explicitly specified so that qw( and or + 'or' => 'OR', # not ) can always be used in lower case, + 'not' => 'NOT', # regardless of ANYCASE flag + 'mod' => 'MOD', + 'div' => 'DIV', +}; + +# localise the temporary variables needed to complete lexer table +{ +# my @tokens = qw< ( ) [ ] { } ${ $ / ; : ? >; + my @tokens = qw< ( ) [ ] { } ${ $ + / ; : ? >; + my @cmpop = keys %CMPOP; +# my @binop = qw( + - * % ); # '/' above, in @tokens + my @binop = qw( - * % ); # '+' and '/' above, in @tokens + + # fill lexer table, slice by slice, with reserved words and operators + @$LEXTABLE{ @RESERVED, @cmpop, @binop, @tokens } + = ( @RESERVED, ('CMPOP') x @cmpop, ('BINOP') x @binop, @tokens ); +} + + +#======================================================================== +# CLASS METHODS +#======================================================================== + +sub new { + my $class = shift; + bless { + LEXTABLE => $LEXTABLE, + STATES => $STATES, + RULES => $RULES, + }, $class; +} + +# update method to set package-scoped $factory lexical +sub install_factory { + my ($self, $new_factory) = @_; + $factory = $new_factory; +} + + +#======================================================================== +# States +#======================================================================== + +$STATES = [ + {#State 0 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'template' => 52, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'switch' => 34, + 'try' => 35, + 'assign' => 19, + 'block' => 72, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 1 + ACTIONS => { + "\$" => 43, + 'LITERAL' => 75, + 'IDENT' => 2, + "\${" => 37 + }, + GOTOS => { + 'setlist' => 76, + 'item' => 39, + 'assign' => 19, + 'node' => 23, + 'ident' => 74 + } + }, + {#State 2 + DEFAULT => -130 + }, + {#State 3 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 79, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 4 + DEFAULT => -23 + }, + {#State 5 + ACTIONS => { + ";" => 80 + } + }, + {#State 6 + DEFAULT => -37 + }, + {#State 7 + DEFAULT => -14 + }, + {#State 8 + ACTIONS => { + "\"" => 89, + "\$" => 86, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 90, + 'filename' => 85, + 'name' => 82 + } + }, + {#State 9 + ACTIONS => { + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "]" => 94, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 96, + 'item' => 39, + 'range' => 93, + 'node' => 23, + 'ident' => 77, + 'term' => 95, + 'list' => 92, + 'lterm' => 56 + } + }, + {#State 10 + ACTIONS => { + ";" => 97 + } + }, + {#State 11 + DEFAULT => -5 + }, + {#State 12 + ACTIONS => { + ";" => -20 + }, + DEFAULT => -27 + }, + {#State 13 + DEFAULT => -78, + GOTOS => { + '@5-1' => 98 + } + }, + {#State 14 + ACTIONS => { + 'IDENT' => 99 + }, + DEFAULT => -87, + GOTOS => { + 'blockargs' => 102, + 'metadata' => 101, + 'meta' => 100 + } + }, + {#State 15 + ACTIONS => { + 'IDENT' => 99 + }, + GOTOS => { + 'metadata' => 103, + 'meta' => 100 + } + }, + {#State 16 + ACTIONS => { + 'DOT' => 104, + 'ASSIGN' => 105 + }, + DEFAULT => -109 + }, + {#State 17 + ACTIONS => { + "\"" => 89, + "\$" => 86, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 106, + 'filename' => 85, + 'name' => 82 + } + }, + {#State 18 + ACTIONS => { + 'IDENT' => 107 + } + }, + {#State 19 + DEFAULT => -149 + }, + {#State 20 + DEFAULT => -12 + }, + {#State 21 + ACTIONS => { + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 108, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 39, + 'loopvar' => 110, + 'node' => 23, + 'ident' => 77, + 'term' => 109, + 'lterm' => 56 + } + }, + {#State 22 + DEFAULT => -40 + }, + {#State 23 + DEFAULT => -127 + }, + {#State 24 + DEFAULT => -6 + }, + {#State 25 + ACTIONS => { + "\"" => 117, + "\$" => 114, + 'LITERAL' => 116, + 'FILENAME' => 83, + 'IDENT' => 111, + 'NUMBER' => 84, + "\${" => 37 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 118, + 'filename' => 85, + 'lvalue' => 112, + 'lnameargs' => 115, + 'item' => 113, + 'name' => 82 + } + }, + {#State 26 + DEFAULT => -113 + }, + {#State 27 + ACTIONS => { + "\$" => 43, + 'IDENT' => 2, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'ident' => 119 + } + }, + {#State 28 + ACTIONS => { + 'LITERAL' => 124, + 'FILENAME' => 83, + 'IDENT' => 120, + 'NUMBER' => 84 + }, + DEFAULT => -87, + GOTOS => { + 'blockargs' => 123, + 'filepart' => 87, + 'filename' => 122, + 'blockname' => 121, + 'metadata' => 101, + 'meta' => 100 + } + }, + {#State 29 + DEFAULT => -43 + }, + {#State 30 + ACTIONS => { + "\$" => 43, + 'LITERAL' => 129, + 'IDENT' => 2, + "\${" => 37 + }, + DEFAULT => -119, + GOTOS => { + 'params' => 128, + 'hash' => 125, + 'item' => 126, + 'param' => 127 + } + }, + {#State 31 + DEFAULT => -25 + }, + {#State 32 + ACTIONS => { + "\"" => 117, + "\$" => 114, + 'LITERAL' => 116, + 'FILENAME' => 83, + 'IDENT' => 111, + 'NUMBER' => 84, + "\${" => 37 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 118, + 'filename' => 85, + 'lvalue' => 112, + 'lnameargs' => 130, + 'item' => 113, + 'name' => 82 + } + }, + {#State 33 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -2, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 131, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 34 + DEFAULT => -22 + }, + {#State 35 + DEFAULT => -24 + }, + {#State 36 + ACTIONS => { + "\"" => 89, + "\$" => 86, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 132, + 'filename' => 85, + 'name' => 82 + } + }, + {#State 37 + ACTIONS => { + "\"" => 60, + "\$" => 43, + 'LITERAL' => 78, + 'IDENT' => 2, + 'REF' => 27, + 'NUMBER' => 26, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 133, + 'item' => 39, + 'node' => 23, + 'ident' => 77 + } + }, + {#State 38 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 134, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 39 + ACTIONS => { + "(" => 135 + }, + DEFAULT => -128 + }, + {#State 40 + ACTIONS => { + ";" => 136 + } + }, + {#State 41 + DEFAULT => -38 + }, + {#State 42 + DEFAULT => -11 + }, + {#State 43 + ACTIONS => { + 'IDENT' => 137 + } + }, + {#State 44 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 138, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 45 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 139, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 46 + DEFAULT => -42 + }, + {#State 47 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 140, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 48 + ACTIONS => { + 'IF' => 144, + 'FILTER' => 143, + 'FOR' => 142, + 'WHILE' => 146, + 'WRAPPER' => 145, + 'UNLESS' => 141 + } + }, + {#State 49 + DEFAULT => -39 + }, + {#State 50 + DEFAULT => -10 + }, + {#State 51 + ACTIONS => { + "\"" => 89, + "\$" => 86, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 147, + 'filename' => 85, + 'name' => 82 + } + }, + {#State 52 + ACTIONS => { + '' => 148 + } + }, + {#State 53 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 57, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 149, + 'term' => 58, + 'expr' => 151, + 'assign' => 150, + 'lterm' => 56 + } + }, + {#State 54 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 152, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 55 + ACTIONS => { + "\"" => 89, + "\$" => 86, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 153, + 'filename' => 85, + 'name' => 82 + } + }, + {#State 56 + DEFAULT => -103 + }, + {#State 57 + ACTIONS => { + 'ASSIGN' => 154 + }, + DEFAULT => -112 + }, + {#State 58 + DEFAULT => -146 + }, + {#State 59 + DEFAULT => -15 + }, + {#State 60 + DEFAULT => -176, + GOTOS => { + 'quoted' => 155 + } + }, + {#State 61 + ACTIONS => { + "\"" => 89, + "\$" => 86, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 156, + 'filename' => 85, + 'name' => 82 + } + }, + {#State 62 + ACTIONS => { + ";" => -16, + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -26 + }, + {#State 63 + DEFAULT => -13 + }, + {#State 64 + DEFAULT => -36 + }, + {#State 65 + ACTIONS => { + "\"" => 89, + "\$" => 86, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 167, + 'filename' => 85, + 'name' => 82 + } + }, + {#State 66 + DEFAULT => -9 + }, + {#State 67 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 168, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 68 + DEFAULT => -104 + }, + {#State 69 + ACTIONS => { + "\$" => 43, + 'LITERAL' => 75, + 'IDENT' => 2, + "\${" => 37 + }, + GOTOS => { + 'setlist' => 169, + 'item' => 39, + 'assign' => 19, + 'node' => 23, + 'ident' => 74 + } + }, + {#State 70 + ACTIONS => { + "\$" => 43, + 'COMMA' => 171, + 'LITERAL' => 75, + 'IDENT' => 2, + "\${" => 37 + }, + DEFAULT => -19, + GOTOS => { + 'item' => 39, + 'assign' => 170, + 'node' => 23, + 'ident' => 74 + } + }, + {#State 71 + DEFAULT => -8 + }, + {#State 72 + DEFAULT => -1 + }, + {#State 73 + DEFAULT => -21 + }, + {#State 74 + ACTIONS => { + 'ASSIGN' => 172, + 'DOT' => 104 + } + }, + {#State 75 + ACTIONS => { + 'ASSIGN' => 154 + } + }, + {#State 76 + ACTIONS => { + "\$" => 43, + 'COMMA' => 171, + 'LITERAL' => 75, + 'IDENT' => 2, + "\${" => 37 + }, + DEFAULT => -30, + GOTOS => { + 'item' => 39, + 'assign' => 170, + 'node' => 23, + 'ident' => 74 + } + }, + {#State 77 + ACTIONS => { + 'DOT' => 104 + }, + DEFAULT => -109 + }, + {#State 78 + DEFAULT => -112 + }, + {#State 79 + ACTIONS => { + 'CMPOP' => 164, + "?" => 158, + ";" => 173, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + 'OR' => 162 + } + }, + {#State 80 + DEFAULT => -7 + }, + {#State 81 + DEFAULT => -173 + }, + {#State 82 + DEFAULT => -166 + }, + {#State 83 + DEFAULT => -172 + }, + {#State 84 + DEFAULT => -174 + }, + {#State 85 + ACTIONS => { + 'DOT' => 174 + }, + DEFAULT => -168 + }, + {#State 86 + ACTIONS => { + "\$" => 43, + 'IDENT' => 2, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'ident' => 175 + } + }, + {#State 87 + DEFAULT => -171 + }, + {#State 88 + DEFAULT => -169 + }, + {#State 89 + DEFAULT => -176, + GOTOS => { + 'quoted' => 176 + } + }, + {#State 90 + DEFAULT => -35 + }, + {#State 91 + ACTIONS => { + "+" => 177, + "(" => 178 + }, + DEFAULT => -156, + GOTOS => { + 'args' => 179 + } + }, + {#State 92 + ACTIONS => { + "{" => 30, + 'COMMA' => 182, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "]" => 180, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 181, + 'lterm' => 56 + } + }, + {#State 93 + ACTIONS => { + "]" => 183 + } + }, + {#State 94 + DEFAULT => -107 + }, + {#State 95 + DEFAULT => -116 + }, + {#State 96 + ACTIONS => { + 'TO' => 184 + }, + DEFAULT => -104 + }, + {#State 97 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 185, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 98 + ACTIONS => { + ";" => 186 + } + }, + {#State 99 + ACTIONS => { + 'ASSIGN' => 187 + } + }, + {#State 100 + DEFAULT => -99 + }, + {#State 101 + ACTIONS => { + 'COMMA' => 189, + 'IDENT' => 99 + }, + DEFAULT => -86, + GOTOS => { + 'meta' => 188 + } + }, + {#State 102 + ACTIONS => { + ";" => 190 + } + }, + {#State 103 + ACTIONS => { + 'COMMA' => 189, + 'IDENT' => 99 + }, + DEFAULT => -17, + GOTOS => { + 'meta' => 188 + } + }, + {#State 104 + ACTIONS => { + "\$" => 43, + 'IDENT' => 2, + 'NUMBER' => 192, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 191 + } + }, + {#State 105 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'WRAPPER' => 55, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + "\"" => 60, + 'PROCESS' => 61, + 'FILTER' => 25, + 'RETURN' => 64, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 193, + 'DEFAULT' => 69, + "{" => 30, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'term' => 58, + 'loop' => 4, + 'expr' => 195, + 'wrapper' => 46, + 'atomexpr' => 48, + 'atomdir' => 12, + 'mdir' => 194, + 'sterm' => 68, + 'filter' => 29, + 'ident' => 149, + 'perl' => 31, + 'setlist' => 70, + 'switch' => 34, + 'try' => 35, + 'assign' => 19, + 'directive' => 196, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 106 + DEFAULT => -33 + }, + {#State 107 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'INCLUDE' => 17, + "(" => 198, + 'SWITCH' => 54, + 'WRAPPER' => 55, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + "\"" => 60, + 'PROCESS' => 61, + 'FILTER' => 25, + 'RETURN' => 64, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 193, + 'DEFAULT' => 69, + "{" => 30, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'term' => 58, + 'loop' => 4, + 'expr' => 199, + 'wrapper' => 46, + 'atomexpr' => 48, + 'atomdir' => 12, + 'mdir' => 197, + 'sterm' => 68, + 'filter' => 29, + 'ident' => 149, + 'perl' => 31, + 'setlist' => 70, + 'switch' => 34, + 'try' => 35, + 'assign' => 19, + 'directive' => 196, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 108 + ACTIONS => { + 'IN' => 201, + 'ASSIGN' => 200 + }, + DEFAULT => -130 + }, + {#State 109 + DEFAULT => -156, + GOTOS => { + 'args' => 202 + } + }, + {#State 110 + ACTIONS => { + ";" => 203 + } + }, + {#State 111 + ACTIONS => { + 'ASSIGN' => -130 + }, + DEFAULT => -173 + }, + {#State 112 + ACTIONS => { + 'ASSIGN' => 204 + } + }, + {#State 113 + DEFAULT => -159 + }, + {#State 114 + ACTIONS => { + "\$" => 43, + 'IDENT' => 205, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'ident' => 175 + } + }, + {#State 115 + ACTIONS => { + ";" => 206 + } + }, + {#State 116 + ACTIONS => { + 'ASSIGN' => -161 + }, + DEFAULT => -169 + }, + {#State 117 + DEFAULT => -176, + GOTOS => { + 'quoted' => 207 + } + }, + {#State 118 + DEFAULT => -158 + }, + {#State 119 + ACTIONS => { + 'DOT' => 104 + }, + DEFAULT => -110 + }, + {#State 120 + ACTIONS => { + 'ASSIGN' => 187 + }, + DEFAULT => -173 + }, + {#State 121 + DEFAULT => -83 + }, + {#State 122 + ACTIONS => { + 'DOT' => 174 + }, + DEFAULT => -84 + }, + {#State 123 + ACTIONS => { + ";" => 208 + } + }, + {#State 124 + DEFAULT => -85 + }, + {#State 125 + ACTIONS => { + "}" => 209 + } + }, + {#State 126 + ACTIONS => { + 'ASSIGN' => 210 + } + }, + {#State 127 + DEFAULT => -122 + }, + {#State 128 + ACTIONS => { + "\$" => 43, + 'COMMA' => 212, + 'LITERAL' => 129, + 'IDENT' => 2, + "\${" => 37 + }, + DEFAULT => -118, + GOTOS => { + 'item' => 126, + 'param' => 211 + } + }, + {#State 129 + ACTIONS => { + 'ASSIGN' => 213 + } + }, + {#State 130 + DEFAULT => -73 + }, + {#State 131 + DEFAULT => -4 + }, + {#State 132 + ACTIONS => { + ";" => 214 + } + }, + {#State 133 + ACTIONS => { + "}" => 215 + } + }, + {#State 134 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'BINOP' => 161 + }, + DEFAULT => -142 + }, + {#State 135 + DEFAULT => -156, + GOTOS => { + 'args' => 216 + } + }, + {#State 136 + DEFAULT => -76, + GOTOS => { + '@4-2' => 217 + } + }, + {#State 137 + DEFAULT => -132 + }, + {#State 138 + ACTIONS => { + 'CMPOP' => 164, + "?" => 158, + ";" => 218, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + 'OR' => 162 + } + }, + {#State 139 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -29 + }, + {#State 140 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -28 + }, + {#State 141 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 219, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 142 + ACTIONS => { + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 108, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 39, + 'loopvar' => 220, + 'node' => 23, + 'ident' => 77, + 'term' => 109, + 'lterm' => 56 + } + }, + {#State 143 + ACTIONS => { + "\"" => 117, + "\$" => 114, + 'LITERAL' => 116, + 'FILENAME' => 83, + 'IDENT' => 111, + 'NUMBER' => 84, + "\${" => 37 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 118, + 'filename' => 85, + 'lvalue' => 112, + 'lnameargs' => 221, + 'item' => 113, + 'name' => 82 + } + }, + {#State 144 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 222, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 145 + ACTIONS => { + "\"" => 89, + "\$" => 86, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 223, + 'filename' => 85, + 'name' => 82 + } + }, + {#State 146 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 224, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 147 + DEFAULT => -41 + }, + {#State 148 + DEFAULT => 0 + }, + {#State 149 + ACTIONS => { + 'DOT' => 104, + 'ASSIGN' => 172 + }, + DEFAULT => -109 + }, + {#State 150 + ACTIONS => { + ")" => 225 + } + }, + {#State 151 + ACTIONS => { + 'CMPOP' => 164, + "?" => 158, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + ")" => 226, + 'OR' => 162 + } + }, + {#State 152 + ACTIONS => { + 'CMPOP' => 164, + "?" => 158, + ";" => 227, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + 'OR' => 162 + } + }, + {#State 153 + ACTIONS => { + ";" => 228 + } + }, + {#State 154 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 229, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 155 + ACTIONS => { + "\"" => 234, + 'TEXT' => 231, + ";" => 233, + "\$" => 43, + 'IDENT' => 2, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'ident' => 230, + 'quotable' => 232 + } + }, + {#State 156 + DEFAULT => -34 + }, + {#State 157 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 235, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 158 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 236, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 159 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 237, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 160 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 238, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 161 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 239, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 162 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 240, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 163 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 241, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 164 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 242, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 165 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 243, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 166 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 244, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 167 + DEFAULT => -32 + }, + {#State 168 + ACTIONS => { + 'CMPOP' => 164, + "?" => 158, + ";" => 245, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + 'OR' => 162 + } + }, + {#State 169 + ACTIONS => { + "\$" => 43, + 'COMMA' => 171, + 'LITERAL' => 75, + 'IDENT' => 2, + "\${" => 37 + }, + DEFAULT => -31, + GOTOS => { + 'item' => 39, + 'assign' => 170, + 'node' => 23, + 'ident' => 74 + } + }, + {#State 170 + DEFAULT => -147 + }, + {#State 171 + DEFAULT => -148 + }, + {#State 172 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 246, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 173 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 247, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 174 + ACTIONS => { + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 248 + } + }, + {#State 175 + ACTIONS => { + 'DOT' => 104 + }, + DEFAULT => -156, + GOTOS => { + 'args' => 249 + } + }, + {#State 176 + ACTIONS => { + "\"" => 250, + 'TEXT' => 231, + ";" => 233, + "\$" => 43, + 'IDENT' => 2, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'ident' => 230, + 'quotable' => 232 + } + }, + {#State 177 + ACTIONS => { + "\"" => 89, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'filename' => 85, + 'name' => 251 + } + }, + {#State 178 + DEFAULT => -156, + GOTOS => { + 'args' => 252 + } + }, + {#State 179 + ACTIONS => { + "{" => 30, + 'COMMA' => 258, + 'LITERAL' => 256, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + DEFAULT => -163, + GOTOS => { + 'sterm' => 68, + 'item' => 254, + 'param' => 255, + 'node' => 23, + 'ident' => 253, + 'term' => 257, + 'lterm' => 56 + } + }, + {#State 180 + DEFAULT => -105 + }, + {#State 181 + DEFAULT => -114 + }, + {#State 182 + DEFAULT => -115 + }, + {#State 183 + DEFAULT => -106 + }, + {#State 184 + ACTIONS => { + "\"" => 60, + "\$" => 43, + 'LITERAL' => 78, + 'IDENT' => 2, + 'REF' => 27, + 'NUMBER' => 26, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 259, + 'item' => 39, + 'node' => 23, + 'ident' => 77 + } + }, + {#State 185 + ACTIONS => { + 'FINAL' => 260, + 'CATCH' => 262 + }, + DEFAULT => -72, + GOTOS => { + 'final' => 261 + } + }, + {#State 186 + ACTIONS => { + 'TEXT' => 263 + } + }, + {#State 187 + ACTIONS => { + "\"" => 266, + 'LITERAL' => 265, + 'NUMBER' => 264 + } + }, + {#State 188 + DEFAULT => -97 + }, + {#State 189 + DEFAULT => -98 + }, + {#State 190 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'template' => 267, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 72, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 191 + DEFAULT => -125 + }, + {#State 192 + DEFAULT => -126 + }, + {#State 193 + ACTIONS => { + ";" => 268 + } + }, + {#State 194 + DEFAULT => -89 + }, + {#State 195 + ACTIONS => { + ";" => -150, + "+" => 157, + 'LITERAL' => -150, + 'IDENT' => -150, + 'CAT' => 163, + "\$" => -150, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + 'COMMA' => -150, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162, + "\${" => -150 + }, + DEFAULT => -26 + }, + {#State 196 + DEFAULT => -92 + }, + {#State 197 + DEFAULT => -91 + }, + {#State 198 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 57, + 'IDENT' => 269, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 39, + 'margs' => 270, + 'node' => 23, + 'ident' => 149, + 'term' => 58, + 'expr' => 151, + 'assign' => 150, + 'lterm' => 56 + } + }, + {#State 199 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -26 + }, + {#State 200 + ACTIONS => { + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 271, + 'lterm' => 56 + } + }, + {#State 201 + ACTIONS => { + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 272, + 'lterm' => 56 + } + }, + {#State 202 + ACTIONS => { + "{" => 30, + 'COMMA' => 258, + 'LITERAL' => 256, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + DEFAULT => -64, + GOTOS => { + 'sterm' => 68, + 'item' => 254, + 'param' => 255, + 'node' => 23, + 'ident' => 253, + 'term' => 257, + 'lterm' => 56 + } + }, + {#State 203 + DEFAULT => -56, + GOTOS => { + '@1-3' => 273 + } + }, + {#State 204 + ACTIONS => { + "\"" => 89, + "\$" => 86, + 'LITERAL' => 88, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'names' => 91, + 'nameargs' => 274, + 'filename' => 85, + 'name' => 82 + } + }, + {#State 205 + ACTIONS => { + 'ASSIGN' => -132 + }, + DEFAULT => -130 + }, + {#State 206 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 275, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 207 + ACTIONS => { + "\"" => 276, + 'TEXT' => 231, + ";" => 233, + "\$" => 43, + 'IDENT' => 2, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'ident' => 230, + 'quotable' => 232 + } + }, + {#State 208 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 277, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 209 + DEFAULT => -108 + }, + {#State 210 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 278, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 211 + DEFAULT => -120 + }, + {#State 212 + DEFAULT => -121 + }, + {#State 213 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 279, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 214 + DEFAULT => -74, + GOTOS => { + '@3-3' => 280 + } + }, + {#State 215 + DEFAULT => -131 + }, + {#State 216 + ACTIONS => { + "{" => 30, + 'COMMA' => 258, + 'LITERAL' => 256, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + ")" => 281, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 254, + 'param' => 255, + 'node' => 23, + 'ident' => 253, + 'term' => 257, + 'lterm' => 56 + } + }, + {#State 217 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 282, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 218 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 283, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 219 + ACTIONS => { + 'CMPOP' => 164, + "?" => 158, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -47 + }, + {#State 220 + DEFAULT => -58 + }, + {#State 221 + DEFAULT => -81 + }, + {#State 222 + ACTIONS => { + 'CMPOP' => 164, + "?" => 158, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -45 + }, + {#State 223 + DEFAULT => -66 + }, + {#State 224 + ACTIONS => { + 'CMPOP' => 164, + "?" => 158, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -61 + }, + {#State 225 + DEFAULT => -144 + }, + {#State 226 + DEFAULT => -145 + }, + {#State 227 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 284, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 228 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 285, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 229 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -151 + }, + {#State 230 + ACTIONS => { + 'DOT' => 104 + }, + DEFAULT => -177 + }, + {#State 231 + DEFAULT => -178 + }, + {#State 232 + DEFAULT => -175 + }, + {#State 233 + DEFAULT => -179 + }, + {#State 234 + DEFAULT => -111 + }, + {#State 235 + ACTIONS => { + 'DIV' => 159, + 'MOD' => 165, + "/" => 166 + }, + DEFAULT => -135 + }, + {#State 236 + ACTIONS => { + ":" => 286, + 'CMPOP' => 164, + "?" => 158, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + 'OR' => 162 + } + }, + {#State 237 + ACTIONS => { + 'MOD' => 165 + }, + DEFAULT => -136 + }, + {#State 238 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'BINOP' => 161 + }, + DEFAULT => -140 + }, + {#State 239 + ACTIONS => { + "+" => 157, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166 + }, + DEFAULT => -133 + }, + {#State 240 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'BINOP' => 161 + }, + DEFAULT => -141 + }, + {#State 241 + ACTIONS => { + "+" => 157, + 'CMPOP' => 164, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'BINOP' => 161 + }, + DEFAULT => -139 + }, + {#State 242 + ACTIONS => { + "+" => 157, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'BINOP' => 161 + }, + DEFAULT => -138 + }, + {#State 243 + DEFAULT => -137 + }, + {#State 244 + ACTIONS => { + 'DIV' => 159, + 'MOD' => 165 + }, + DEFAULT => -134 + }, + {#State 245 + DEFAULT => -59, + GOTOS => { + '@2-3' => 287 + } + }, + {#State 246 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -150 + }, + {#State 247 + ACTIONS => { + 'ELSIF' => 290, + 'ELSE' => 288 + }, + DEFAULT => -50, + GOTOS => { + 'else' => 289 + } + }, + {#State 248 + DEFAULT => -170 + }, + {#State 249 + ACTIONS => { + "{" => 30, + 'COMMA' => 258, + 'LITERAL' => 256, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + DEFAULT => -162, + GOTOS => { + 'sterm' => 68, + 'item' => 254, + 'param' => 255, + 'node' => 23, + 'ident' => 253, + 'term' => 257, + 'lterm' => 56 + } + }, + {#State 250 + DEFAULT => -167 + }, + {#State 251 + DEFAULT => -165 + }, + {#State 252 + ACTIONS => { + "{" => 30, + 'COMMA' => 258, + 'LITERAL' => 256, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + ")" => 291, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 254, + 'param' => 255, + 'node' => 23, + 'ident' => 253, + 'term' => 257, + 'lterm' => 56 + } + }, + {#State 253 + ACTIONS => { + 'DOT' => 104, + 'ASSIGN' => 292 + }, + DEFAULT => -109 + }, + {#State 254 + ACTIONS => { + "(" => 135, + 'ASSIGN' => 210 + }, + DEFAULT => -128 + }, + {#State 255 + DEFAULT => -153 + }, + {#State 256 + ACTIONS => { + 'ASSIGN' => 213 + }, + DEFAULT => -112 + }, + {#State 257 + DEFAULT => -152 + }, + {#State 258 + DEFAULT => -155 + }, + {#State 259 + DEFAULT => -117 + }, + {#State 260 + ACTIONS => { + ";" => 293 + } + }, + {#State 261 + ACTIONS => { + 'END' => 294 + } + }, + {#State 262 + ACTIONS => { + ";" => 296, + 'DEFAULT' => 297, + 'FILENAME' => 83, + 'IDENT' => 81, + 'NUMBER' => 84 + }, + GOTOS => { + 'filepart' => 87, + 'filename' => 295 + } + }, + {#State 263 + ACTIONS => { + 'END' => 298 + } + }, + {#State 264 + DEFAULT => -102 + }, + {#State 265 + DEFAULT => -100 + }, + {#State 266 + ACTIONS => { + 'TEXT' => 299 + } + }, + {#State 267 + ACTIONS => { + 'END' => 300 + } + }, + {#State 268 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 301, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 269 + ACTIONS => { + 'COMMA' => -96, + 'IDENT' => -96, + ")" => -96 + }, + DEFAULT => -130 + }, + {#State 270 + ACTIONS => { + 'COMMA' => 304, + 'IDENT' => 302, + ")" => 303 + } + }, + {#State 271 + DEFAULT => -156, + GOTOS => { + 'args' => 305 + } + }, + {#State 272 + DEFAULT => -156, + GOTOS => { + 'args' => 306 + } + }, + {#State 273 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 307, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 274 + DEFAULT => -157 + }, + {#State 275 + ACTIONS => { + 'END' => 308 + } + }, + {#State 276 + ACTIONS => { + 'ASSIGN' => -160 + }, + DEFAULT => -167 + }, + {#State 277 + ACTIONS => { + 'END' => 309 + } + }, + {#State 278 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -124 + }, + {#State 279 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -123 + }, + {#State 280 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 310, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 281 + DEFAULT => -129 + }, + {#State 282 + ACTIONS => { + 'END' => 311 + } + }, + {#State 283 + ACTIONS => { + 'ELSIF' => 290, + 'ELSE' => 288 + }, + DEFAULT => -50, + GOTOS => { + 'else' => 312 + } + }, + {#State 284 + ACTIONS => { + 'CASE' => 313 + }, + DEFAULT => -55, + GOTOS => { + 'case' => 314 + } + }, + {#State 285 + ACTIONS => { + 'END' => 315 + } + }, + {#State 286 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 316, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 287 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 317, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 288 + ACTIONS => { + ";" => 318 + } + }, + {#State 289 + ACTIONS => { + 'END' => 319 + } + }, + {#State 290 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 320, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 291 + DEFAULT => -164 + }, + {#State 292 + ACTIONS => { + 'NOT' => 38, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "(" => 53, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'expr' => 321, + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 58, + 'lterm' => 56 + } + }, + {#State 293 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 322, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 294 + DEFAULT => -67 + }, + {#State 295 + ACTIONS => { + 'DOT' => 174, + ";" => 323 + } + }, + {#State 296 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 324, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 297 + ACTIONS => { + ";" => 325 + } + }, + {#State 298 + DEFAULT => -79 + }, + {#State 299 + ACTIONS => { + "\"" => 326 + } + }, + {#State 300 + DEFAULT => -82 + }, + {#State 301 + ACTIONS => { + 'END' => 327 + } + }, + {#State 302 + DEFAULT => -94 + }, + {#State 303 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'WRAPPER' => 55, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + "\"" => 60, + 'PROCESS' => 61, + 'FILTER' => 25, + 'RETURN' => 64, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 193, + 'DEFAULT' => 69, + "{" => 30, + "\${" => 37 + }, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'term' => 58, + 'loop' => 4, + 'expr' => 199, + 'wrapper' => 46, + 'atomexpr' => 48, + 'atomdir' => 12, + 'mdir' => 328, + 'sterm' => 68, + 'filter' => 29, + 'ident' => 149, + 'perl' => 31, + 'setlist' => 70, + 'switch' => 34, + 'try' => 35, + 'assign' => 19, + 'directive' => 196, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 304 + DEFAULT => -95 + }, + {#State 305 + ACTIONS => { + "{" => 30, + 'COMMA' => 258, + 'LITERAL' => 256, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + DEFAULT => -62, + GOTOS => { + 'sterm' => 68, + 'item' => 254, + 'param' => 255, + 'node' => 23, + 'ident' => 253, + 'term' => 257, + 'lterm' => 56 + } + }, + {#State 306 + ACTIONS => { + "{" => 30, + 'COMMA' => 258, + 'LITERAL' => 256, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + DEFAULT => -63, + GOTOS => { + 'sterm' => 68, + 'item' => 254, + 'param' => 255, + 'node' => 23, + 'ident' => 253, + 'term' => 257, + 'lterm' => 56 + } + }, + {#State 307 + ACTIONS => { + 'END' => 329 + } + }, + {#State 308 + DEFAULT => -80 + }, + {#State 309 + DEFAULT => -88 + }, + {#State 310 + ACTIONS => { + 'END' => 330 + } + }, + {#State 311 + DEFAULT => -77 + }, + {#State 312 + ACTIONS => { + 'END' => 331 + } + }, + {#State 313 + ACTIONS => { + ";" => 332, + 'DEFAULT' => 334, + "{" => 30, + 'LITERAL' => 78, + 'IDENT' => 2, + "\"" => 60, + "\$" => 43, + "[" => 9, + 'NUMBER' => 26, + 'REF' => 27, + "\${" => 37 + }, + GOTOS => { + 'sterm' => 68, + 'item' => 39, + 'node' => 23, + 'ident' => 77, + 'term' => 333, + 'lterm' => 56 + } + }, + {#State 314 + ACTIONS => { + 'END' => 335 + } + }, + {#State 315 + DEFAULT => -65 + }, + {#State 316 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -143 + }, + {#State 317 + ACTIONS => { + 'END' => 336 + } + }, + {#State 318 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 337, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 319 + DEFAULT => -46 + }, + {#State 320 + ACTIONS => { + 'CMPOP' => 164, + "?" => 158, + ";" => 338, + "+" => 157, + 'MOD' => 165, + 'DIV' => 159, + "/" => 166, + 'AND' => 160, + 'CAT' => 163, + 'BINOP' => 161, + 'OR' => 162 + } + }, + {#State 321 + ACTIONS => { + "+" => 157, + 'CAT' => 163, + 'CMPOP' => 164, + "?" => 158, + 'DIV' => 159, + 'MOD' => 165, + "/" => 166, + 'AND' => 160, + 'BINOP' => 161, + 'OR' => 162 + }, + DEFAULT => -154 + }, + {#State 322 + DEFAULT => -71 + }, + {#State 323 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 339, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 324 + ACTIONS => { + 'FINAL' => 260, + 'CATCH' => 262 + }, + DEFAULT => -72, + GOTOS => { + 'final' => 340 + } + }, + {#State 325 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 341, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 326 + DEFAULT => -101 + }, + {#State 327 + DEFAULT => -93 + }, + {#State 328 + DEFAULT => -90 + }, + {#State 329 + DEFAULT => -57 + }, + {#State 330 + DEFAULT => -75 + }, + {#State 331 + DEFAULT => -44 + }, + {#State 332 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 342, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 333 + ACTIONS => { + ";" => 343 + } + }, + {#State 334 + ACTIONS => { + ";" => 344 + } + }, + {#State 335 + DEFAULT => -51 + }, + {#State 336 + DEFAULT => -60 + }, + {#State 337 + DEFAULT => -49 + }, + {#State 338 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 345, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 339 + ACTIONS => { + 'FINAL' => 260, + 'CATCH' => 262 + }, + DEFAULT => -72, + GOTOS => { + 'final' => 346 + } + }, + {#State 340 + DEFAULT => -70 + }, + {#State 341 + ACTIONS => { + 'FINAL' => 260, + 'CATCH' => 262 + }, + DEFAULT => -72, + GOTOS => { + 'final' => 347 + } + }, + {#State 342 + DEFAULT => -54 + }, + {#State 343 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 348, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 344 + ACTIONS => { + 'SET' => 1, + 'PERL' => 40, + 'NOT' => 38, + 'IDENT' => 2, + 'CLEAR' => 41, + 'UNLESS' => 3, + 'IF' => 44, + "\$" => 43, + 'STOP' => 6, + 'CALL' => 45, + 'THROW' => 8, + 'GET' => 47, + "[" => 9, + 'TRY' => 10, + 'LAST' => 49, + 'DEBUG' => 51, + 'RAWPERL' => 13, + 'META' => 15, + 'INCLUDE' => 17, + "(" => 53, + 'SWITCH' => 54, + 'MACRO' => 18, + 'WRAPPER' => 55, + ";" => -18, + 'FOR' => 21, + 'NEXT' => 22, + 'LITERAL' => 57, + 'TEXT' => 24, + "\"" => 60, + 'PROCESS' => 61, + 'RETURN' => 64, + 'FILTER' => 25, + 'INSERT' => 65, + 'NUMBER' => 26, + 'REF' => 27, + 'WHILE' => 67, + 'BLOCK' => 28, + 'DEFAULT' => 69, + "{" => 30, + 'USE' => 32, + 'VIEW' => 36, + "\${" => 37 + }, + DEFAULT => -3, + GOTOS => { + 'item' => 39, + 'node' => 23, + 'rawperl' => 59, + 'term' => 58, + 'loop' => 4, + 'use' => 63, + 'expr' => 62, + 'capture' => 42, + 'statement' => 5, + 'view' => 7, + 'wrapper' => 46, + 'atomexpr' => 48, + 'chunk' => 11, + 'defblock' => 66, + 'atomdir' => 12, + 'anonblock' => 50, + 'sterm' => 68, + 'defblockname' => 14, + 'filter' => 29, + 'ident' => 16, + 'perl' => 31, + 'setlist' => 70, + 'chunks' => 33, + 'try' => 35, + 'switch' => 34, + 'assign' => 19, + 'block' => 349, + 'directive' => 71, + 'macro' => 20, + 'condition' => 73, + 'lterm' => 56 + } + }, + {#State 345 + ACTIONS => { + 'ELSIF' => 290, + 'ELSE' => 288 + }, + DEFAULT => -50, + GOTOS => { + 'else' => 350 + } + }, + {#State 346 + DEFAULT => -68 + }, + {#State 347 + DEFAULT => -69 + }, + {#State 348 + ACTIONS => { + 'CASE' => 313 + }, + DEFAULT => -55, + GOTOS => { + 'case' => 351 + } + }, + {#State 349 + DEFAULT => -53 + }, + {#State 350 + DEFAULT => -48 + }, + {#State 351 + DEFAULT => -52 + } +]; + + +#======================================================================== +# Rules +#======================================================================== + +$RULES = [ + [#Rule 0 + '$start', 2, undef + ], + [#Rule 1 + 'template', 1, +sub +#line 64 "Parser.yp" +{ $factory->template($_[1]) } + ], + [#Rule 2 + 'block', 1, +sub +#line 67 "Parser.yp" +{ $factory->block($_[1]) } + ], + [#Rule 3 + 'block', 0, +sub +#line 68 "Parser.yp" +{ $factory->block() } + ], + [#Rule 4 + 'chunks', 2, +sub +#line 71 "Parser.yp" +{ push(@{$_[1]}, $_[2]) + if defined $_[2]; $_[1] } + ], + [#Rule 5 + 'chunks', 1, +sub +#line 73 "Parser.yp" +{ defined $_[1] ? [ $_[1] ] : [ ] } + ], + [#Rule 6 + 'chunk', 1, +sub +#line 76 "Parser.yp" +{ $factory->textblock($_[1]) } + ], + [#Rule 7 + 'chunk', 2, +sub +#line 77 "Parser.yp" +{ return '' unless $_[1]; + $_[0]->location() . $_[1]; + } + ], + [#Rule 8 + 'statement', 1, undef + ], + [#Rule 9 + 'statement', 1, undef + ], + [#Rule 10 + 'statement', 1, undef + ], + [#Rule 11 + 'statement', 1, undef + ], + [#Rule 12 + 'statement', 1, undef + ], + [#Rule 13 + 'statement', 1, undef + ], + [#Rule 14 + 'statement', 1, undef + ], + [#Rule 15 + 'statement', 1, undef + ], + [#Rule 16 + 'statement', 1, +sub +#line 90 "Parser.yp" +{ $factory->get($_[1]) } + ], + [#Rule 17 + 'statement', 2, +sub +#line 91 "Parser.yp" +{ $_[0]->add_metadata($_[2]); } + ], + [#Rule 18 + 'statement', 0, undef + ], + [#Rule 19 + 'directive', 1, +sub +#line 95 "Parser.yp" +{ $factory->set($_[1]) } + ], + [#Rule 20 + 'directive', 1, undef + ], + [#Rule 21 + 'directive', 1, undef + ], + [#Rule 22 + 'directive', 1, undef + ], + [#Rule 23 + 'directive', 1, undef + ], + [#Rule 24 + 'directive', 1, undef + ], + [#Rule 25 + 'directive', 1, undef + ], + [#Rule 26 + 'atomexpr', 1, +sub +#line 109 "Parser.yp" +{ $factory->get($_[1]) } + ], + [#Rule 27 + 'atomexpr', 1, undef + ], + [#Rule 28 + 'atomdir', 2, +sub +#line 113 "Parser.yp" +{ $factory->get($_[2]) } + ], + [#Rule 29 + 'atomdir', 2, +sub +#line 114 "Parser.yp" +{ $factory->call($_[2]) } + ], + [#Rule 30 + 'atomdir', 2, +sub +#line 115 "Parser.yp" +{ $factory->set($_[2]) } + ], + [#Rule 31 + 'atomdir', 2, +sub +#line 116 "Parser.yp" +{ $factory->default($_[2]) } + ], + [#Rule 32 + 'atomdir', 2, +sub +#line 117 "Parser.yp" +{ $factory->insert($_[2]) } + ], + [#Rule 33 + 'atomdir', 2, +sub +#line 118 "Parser.yp" +{ $factory->include($_[2]) } + ], + [#Rule 34 + 'atomdir', 2, +sub +#line 119 "Parser.yp" +{ $factory->process($_[2]) } + ], + [#Rule 35 + 'atomdir', 2, +sub +#line 120 "Parser.yp" +{ $factory->throw($_[2]) } + ], + [#Rule 36 + 'atomdir', 1, +sub +#line 121 "Parser.yp" +{ $factory->return() } + ], + [#Rule 37 + 'atomdir', 1, +sub +#line 122 "Parser.yp" +{ $factory->stop() } + ], + [#Rule 38 + 'atomdir', 1, +sub +#line 123 "Parser.yp" +{ "\$output = '';"; } + ], + [#Rule 39 + 'atomdir', 1, +sub +#line 124 "Parser.yp" +{ $_[0]->{ INFOR } || $_[0]->{ INWHILE } + ? 'last LOOP;' + : 'last;' } + ], + [#Rule 40 + 'atomdir', 1, +sub +#line 127 "Parser.yp" +{ $_[0]->{ INFOR } + ? $factory->next() + : ($_[0]->{ INWHILE } + ? 'next LOOP;' + : 'next;') } + ], + [#Rule 41 + 'atomdir', 2, +sub +#line 132 "Parser.yp" +{ if ($_[2]->[0]->[0] =~ /^'(on|off)'$/) { + $_[0]->{ DEBUG_DIRS } = ($1 eq 'on'); + $factory->debug($_[2]); + } + else { + $_[0]->{ DEBUG_DIRS } ? $factory->debug($_[2]) : ''; + } + } + ], + [#Rule 42 + 'atomdir', 1, undef + ], + [#Rule 43 + 'atomdir', 1, undef + ], + [#Rule 44 + 'condition', 6, +sub +#line 145 "Parser.yp" +{ $factory->if(@_[2, 4, 5]) } + ], + [#Rule 45 + 'condition', 3, +sub +#line 146 "Parser.yp" +{ $factory->if(@_[3, 1]) } + ], + [#Rule 46 + 'condition', 6, +sub +#line 148 "Parser.yp" +{ $factory->if("!($_[2])", @_[4, 5]) } + ], + [#Rule 47 + 'condition', 3, +sub +#line 149 "Parser.yp" +{ $factory->if("!($_[3])", $_[1]) } + ], + [#Rule 48 + 'else', 5, +sub +#line 153 "Parser.yp" +{ unshift(@{$_[5]}, [ @_[2, 4] ]); + $_[5]; } + ], + [#Rule 49 + 'else', 3, +sub +#line 155 "Parser.yp" +{ [ $_[3] ] } + ], + [#Rule 50 + 'else', 0, +sub +#line 156 "Parser.yp" +{ [ undef ] } + ], + [#Rule 51 + 'switch', 6, +sub +#line 160 "Parser.yp" +{ $factory->switch(@_[2, 5]) } + ], + [#Rule 52 + 'case', 5, +sub +#line 164 "Parser.yp" +{ unshift(@{$_[5]}, [ @_[2, 4] ]); + $_[5]; } + ], + [#Rule 53 + 'case', 4, +sub +#line 166 "Parser.yp" +{ [ $_[4] ] } + ], + [#Rule 54 + 'case', 3, +sub +#line 167 "Parser.yp" +{ [ $_[3] ] } + ], + [#Rule 55 + 'case', 0, +sub +#line 168 "Parser.yp" +{ [ undef ] } + ], + [#Rule 56 + '@1-3', 0, +sub +#line 171 "Parser.yp" +{ $_[0]->{ INFOR }++ } + ], + [#Rule 57 + 'loop', 6, +sub +#line 172 "Parser.yp" +{ $_[0]->{ INFOR }--; + $factory->foreach(@{$_[2]}, $_[5]) } + ], + [#Rule 58 + 'loop', 3, +sub +#line 176 "Parser.yp" +{ $factory->foreach(@{$_[3]}, $_[1]) } + ], + [#Rule 59 + '@2-3', 0, +sub +#line 177 "Parser.yp" +{ $_[0]->{ INWHILE }++ } + ], + [#Rule 60 + 'loop', 6, +sub +#line 178 "Parser.yp" +{ $_[0]->{ INWHILE }--; + $factory->while(@_[2, 5]) } + ], + [#Rule 61 + 'loop', 3, +sub +#line 180 "Parser.yp" +{ $factory->while(@_[3, 1]) } + ], + [#Rule 62 + 'loopvar', 4, +sub +#line 183 "Parser.yp" +{ [ @_[1, 3, 4] ] } + ], + [#Rule 63 + 'loopvar', 4, +sub +#line 184 "Parser.yp" +{ [ @_[1, 3, 4] ] } + ], + [#Rule 64 + 'loopvar', 2, +sub +#line 185 "Parser.yp" +{ [ 0, @_[1, 2] ] } + ], + [#Rule 65 + 'wrapper', 5, +sub +#line 189 "Parser.yp" +{ $factory->wrapper(@_[2, 4]) } + ], + [#Rule 66 + 'wrapper', 3, +sub +#line 191 "Parser.yp" +{ $factory->wrapper(@_[3, 1]) } + ], + [#Rule 67 + 'try', 5, +sub +#line 195 "Parser.yp" +{ $factory->try(@_[3, 4]) } + ], + [#Rule 68 + 'final', 5, +sub +#line 199 "Parser.yp" +{ unshift(@{$_[5]}, [ @_[2,4] ]); + $_[5]; } + ], + [#Rule 69 + 'final', 5, +sub +#line 202 "Parser.yp" +{ unshift(@{$_[5]}, [ undef, $_[4] ]); + $_[5]; } + ], + [#Rule 70 + 'final', 4, +sub +#line 205 "Parser.yp" +{ unshift(@{$_[4]}, [ undef, $_[3] ]); + $_[4]; } + ], + [#Rule 71 + 'final', 3, +sub +#line 207 "Parser.yp" +{ [ $_[3] ] } + ], + [#Rule 72 + 'final', 0, +sub +#line 208 "Parser.yp" +{ [ 0 ] } + ], + [#Rule 73 + 'use', 2, +sub +#line 211 "Parser.yp" +{ $factory->use($_[2]) } + ], + [#Rule 74 + '@3-3', 0, +sub +#line 214 "Parser.yp" +{ $_[0]->push_defblock(); } + ], + [#Rule 75 + 'view', 6, +sub +#line 215 "Parser.yp" +{ $factory->view(@_[2,5], + $_[0]->pop_defblock) } + ], + [#Rule 76 + '@4-2', 0, +sub +#line 219 "Parser.yp" +{ ${$_[0]->{ INPERL }}++; } + ], + [#Rule 77 + 'perl', 5, +sub +#line 220 "Parser.yp" +{ ${$_[0]->{ INPERL }}--; + $_[0]->{ EVAL_PERL } + ? $factory->perl($_[4]) + : $factory->no_perl(); } + ], + [#Rule 78 + '@5-1', 0, +sub +#line 226 "Parser.yp" +{ ${$_[0]->{ INPERL }}++; + $rawstart = ${$_[0]->{'LINE'}}; } + ], + [#Rule 79 + 'rawperl', 5, +sub +#line 228 "Parser.yp" +{ ${$_[0]->{ INPERL }}--; + $_[0]->{ EVAL_PERL } + ? $factory->rawperl($_[4], $rawstart) + : $factory->no_perl(); } + ], + [#Rule 80 + 'filter', 5, +sub +#line 235 "Parser.yp" +{ $factory->filter(@_[2,4]) } + ], + [#Rule 81 + 'filter', 3, +sub +#line 237 "Parser.yp" +{ $factory->filter(@_[3,1]) } + ], + [#Rule 82 + 'defblock', 5, +sub +#line 242 "Parser.yp" +{ my $name = join('/', @{ $_[0]->{ DEFBLOCKS } }); + pop(@{ $_[0]->{ DEFBLOCKS } }); + $_[0]->define_block($name, $_[4]); + undef + } + ], + [#Rule 83 + 'defblockname', 2, +sub +#line 249 "Parser.yp" +{ push(@{ $_[0]->{ DEFBLOCKS } }, $_[2]); + $_[2]; + } + ], + [#Rule 84 + 'blockname', 1, undef + ], + [#Rule 85 + 'blockname', 1, +sub +#line 255 "Parser.yp" +{ $_[1] =~ s/^'(.*)'$/$1/; $_[1] } + ], + [#Rule 86 + 'blockargs', 1, undef + ], + [#Rule 87 + 'blockargs', 0, undef + ], + [#Rule 88 + 'anonblock', 5, +sub +#line 263 "Parser.yp" +{ local $" = ', '; + print STDERR "experimental block args: [@{ $_[2] }]\n" + if $_[2]; + $factory->anon_block($_[4]) } + ], + [#Rule 89 + 'capture', 3, +sub +#line 269 "Parser.yp" +{ $factory->capture(@_[1, 3]) } + ], + [#Rule 90 + 'macro', 6, +sub +#line 273 "Parser.yp" +{ $factory->macro(@_[2, 6, 4]) } + ], + [#Rule 91 + 'macro', 3, +sub +#line 274 "Parser.yp" +{ $factory->macro(@_[2, 3]) } + ], + [#Rule 92 + 'mdir', 1, undef + ], + [#Rule 93 + 'mdir', 4, +sub +#line 278 "Parser.yp" +{ $_[3] } + ], + [#Rule 94 + 'margs', 2, +sub +#line 281 "Parser.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 95 + 'margs', 2, +sub +#line 282 "Parser.yp" +{ $_[1] } + ], + [#Rule 96 + 'margs', 1, +sub +#line 283 "Parser.yp" +{ [ $_[1] ] } + ], + [#Rule 97 + 'metadata', 2, +sub +#line 286 "Parser.yp" +{ push(@{$_[1]}, @{$_[2]}); $_[1] } + ], + [#Rule 98 + 'metadata', 2, undef + ], + [#Rule 99 + 'metadata', 1, undef + ], + [#Rule 100 + 'meta', 3, +sub +#line 291 "Parser.yp" +{ for ($_[3]) { s/^'//; s/'$//; + s/\\'/'/g }; + [ @_[1,3] ] } + ], + [#Rule 101 + 'meta', 5, +sub +#line 294 "Parser.yp" +{ [ @_[1,4] ] } + ], + [#Rule 102 + 'meta', 3, +sub +#line 295 "Parser.yp" +{ [ @_[1,3] ] } + ], + [#Rule 103 + 'term', 1, undef + ], + [#Rule 104 + 'term', 1, undef + ], + [#Rule 105 + 'lterm', 3, +sub +#line 307 "Parser.yp" +{ "[ $_[2] ]" } + ], + [#Rule 106 + 'lterm', 3, +sub +#line 308 "Parser.yp" +{ "[ $_[2] ]" } + ], + [#Rule 107 + 'lterm', 2, +sub +#line 309 "Parser.yp" +{ "[ ]" } + ], + [#Rule 108 + 'lterm', 3, +sub +#line 310 "Parser.yp" +{ "{ $_[2] }" } + ], + [#Rule 109 + 'sterm', 1, +sub +#line 313 "Parser.yp" +{ $factory->ident($_[1]) } + ], + [#Rule 110 + 'sterm', 2, +sub +#line 314 "Parser.yp" +{ $factory->identref($_[2]) } + ], + [#Rule 111 + 'sterm', 3, +sub +#line 315 "Parser.yp" +{ $factory->quoted($_[2]) } + ], + [#Rule 112 + 'sterm', 1, undef + ], + [#Rule 113 + 'sterm', 1, undef + ], + [#Rule 114 + 'list', 2, +sub +#line 320 "Parser.yp" +{ "$_[1], $_[2]" } + ], + [#Rule 115 + 'list', 2, undef + ], + [#Rule 116 + 'list', 1, undef + ], + [#Rule 117 + 'range', 3, +sub +#line 325 "Parser.yp" +{ $_[1] . '..' . $_[3] } + ], + [#Rule 118 + 'hash', 1, undef + ], + [#Rule 119 + 'hash', 0, +sub +#line 330 "Parser.yp" +{ "" } + ], + [#Rule 120 + 'params', 2, +sub +#line 333 "Parser.yp" +{ "$_[1], $_[2]" } + ], + [#Rule 121 + 'params', 2, undef + ], + [#Rule 122 + 'params', 1, undef + ], + [#Rule 123 + 'param', 3, +sub +#line 338 "Parser.yp" +{ "$_[1] => $_[3]" } + ], + [#Rule 124 + 'param', 3, +sub +#line 339 "Parser.yp" +{ "$_[1] => $_[3]" } + ], + [#Rule 125 + 'ident', 3, +sub +#line 342 "Parser.yp" +{ push(@{$_[1]}, @{$_[3]}); $_[1] } + ], + [#Rule 126 + 'ident', 3, +sub +#line 343 "Parser.yp" +{ push(@{$_[1]}, + map {($_, 0)} split(/\./, $_[3])); + $_[1]; } + ], + [#Rule 127 + 'ident', 1, undef + ], + [#Rule 128 + 'node', 1, +sub +#line 349 "Parser.yp" +{ [ $_[1], 0 ] } + ], + [#Rule 129 + 'node', 4, +sub +#line 350 "Parser.yp" +{ [ $_[1], $factory->args($_[3]) ] } + ], + [#Rule 130 + 'item', 1, +sub +#line 353 "Parser.yp" +{ "'$_[1]'" } + ], + [#Rule 131 + 'item', 3, +sub +#line 354 "Parser.yp" +{ $_[2] } + ], + [#Rule 132 + 'item', 2, +sub +#line 355 "Parser.yp" +{ $_[0]->{ V1DOLLAR } + ? "'$_[2]'" + : $factory->ident(["'$_[2]'", 0]) } + ], + [#Rule 133 + 'expr', 3, +sub +#line 360 "Parser.yp" +{ "$_[1] $_[2] $_[3]" } + ], + [#Rule 134 + 'expr', 3, +sub +#line 361 "Parser.yp" +{ "$_[1] $_[2] $_[3]" } + ], + [#Rule 135 + 'expr', 3, +sub +#line 362 "Parser.yp" +{ "$_[1] $_[2] $_[3]" } + ], + [#Rule 136 + 'expr', 3, +sub +#line 363 "Parser.yp" +{ "int($_[1] / $_[3])" } + ], + [#Rule 137 + 'expr', 3, +sub +#line 364 "Parser.yp" +{ "$_[1] % $_[3]" } + ], + [#Rule 138 + 'expr', 3, +sub +#line 365 "Parser.yp" +{ "$_[1] $CMPOP{ $_[2] } $_[3]" } + ], + [#Rule 139 + 'expr', 3, +sub +#line 366 "Parser.yp" +{ "$_[1] . $_[3]" } + ], + [#Rule 140 + 'expr', 3, +sub +#line 367 "Parser.yp" +{ "$_[1] && $_[3]" } + ], + [#Rule 141 + 'expr', 3, +sub +#line 368 "Parser.yp" +{ "$_[1] || $_[3]" } + ], + [#Rule 142 + 'expr', 2, +sub +#line 369 "Parser.yp" +{ "! $_[2]" } + ], + [#Rule 143 + 'expr', 5, +sub +#line 370 "Parser.yp" +{ "$_[1] ? $_[3] : $_[5]" } + ], + [#Rule 144 + 'expr', 3, +sub +#line 371 "Parser.yp" +{ $factory->assign(@{$_[2]}) } + ], + [#Rule 145 + 'expr', 3, +sub +#line 372 "Parser.yp" +{ "($_[2])" } + ], + [#Rule 146 + 'expr', 1, undef + ], + [#Rule 147 + 'setlist', 2, +sub +#line 376 "Parser.yp" +{ push(@{$_[1]}, @{$_[2]}); $_[1] } + ], + [#Rule 148 + 'setlist', 2, undef + ], + [#Rule 149 + 'setlist', 1, undef + ], + [#Rule 150 + 'assign', 3, +sub +#line 382 "Parser.yp" +{ [ $_[1], $_[3] ] } + ], + [#Rule 151 + 'assign', 3, +sub +#line 383 "Parser.yp" +{ [ @_[1,3] ] } + ], + [#Rule 152 + 'args', 2, +sub +#line 390 "Parser.yp" +{ push(@{$_[1]}, $_[2]); $_[1] } + ], + [#Rule 153 + 'args', 2, +sub +#line 391 "Parser.yp" +{ push(@{$_[1]->[0]}, $_[2]); $_[1] } + ], + [#Rule 154 + 'args', 4, +sub +#line 392 "Parser.yp" +{ push(@{$_[1]->[0]}, "'', " . + $factory->assign(@_[2,4])); $_[1] } + ], + [#Rule 155 + 'args', 2, +sub +#line 394 "Parser.yp" +{ $_[1] } + ], + [#Rule 156 + 'args', 0, +sub +#line 395 "Parser.yp" +{ [ [ ] ] } + ], + [#Rule 157 + 'lnameargs', 3, +sub +#line 405 "Parser.yp" +{ push(@{$_[3]}, $_[1]); $_[3] } + ], + [#Rule 158 + 'lnameargs', 1, undef + ], + [#Rule 159 + 'lvalue', 1, undef + ], + [#Rule 160 + 'lvalue', 3, +sub +#line 410 "Parser.yp" +{ $factory->quoted($_[2]) } + ], + [#Rule 161 + 'lvalue', 1, undef + ], + [#Rule 162 + 'nameargs', 3, +sub +#line 414 "Parser.yp" +{ [ [$factory->ident($_[2])], $_[3] ] } + ], + [#Rule 163 + 'nameargs', 2, +sub +#line 415 "Parser.yp" +{ [ @_[1,2] ] } + ], + [#Rule 164 + 'nameargs', 4, +sub +#line 416 "Parser.yp" +{ [ @_[1,3] ] } + ], + [#Rule 165 + 'names', 3, +sub +#line 419 "Parser.yp" +{ push(@{$_[1]}, $_[3]); $_[1] } + ], + [#Rule 166 + 'names', 1, +sub +#line 420 "Parser.yp" +{ [ $_[1] ] } + ], + [#Rule 167 + 'name', 3, +sub +#line 423 "Parser.yp" +{ $factory->quoted($_[2]) } + ], + [#Rule 168 + 'name', 1, +sub +#line 424 "Parser.yp" +{ "'$_[1]'" } + ], + [#Rule 169 + 'name', 1, undef + ], + [#Rule 170 + 'filename', 3, +sub +#line 436 "Parser.yp" +{ "$_[1].$_[3]" } + ], + [#Rule 171 + 'filename', 1, undef + ], + [#Rule 172 + 'filepart', 1, undef + ], + [#Rule 173 + 'filepart', 1, undef + ], + [#Rule 174 + 'filepart', 1, undef + ], + [#Rule 175 + 'quoted', 2, +sub +#line 450 "Parser.yp" +{ push(@{$_[1]}, $_[2]) + if defined $_[2]; $_[1] } + ], + [#Rule 176 + 'quoted', 0, +sub +#line 452 "Parser.yp" +{ [ ] } + ], + [#Rule 177 + 'quotable', 1, +sub +#line 455 "Parser.yp" +{ $factory->ident($_[1]) } + ], + [#Rule 178 + 'quotable', 1, +sub +#line 456 "Parser.yp" +{ $factory->text($_[1]) } + ], + [#Rule 179 + 'quotable', 1, +sub +#line 457 "Parser.yp" +{ undef } + ] +]; + + + +1; + + + + + + + + + + + + diff --git a/lib/Template/Iterator.pm b/lib/Template/Iterator.pm new file mode 100644 index 0000000..710ecdc --- /dev/null +++ b/lib/Template/Iterator.pm @@ -0,0 +1,456 @@ +#============================================================= -*-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. +# +# An iterator is an object which provides a consistent way to +# navigate through data which may have a complex underlying form. +# This implementation uses the get_first() and get_next() methods to +# iterate through a dataset. The get_first() method is called once +# to perform any data initialisation and return the first value, +# then get_next() is called repeatedly to return successive values. +# Both these methods return a pair of values which are the data item +# itself and a status code. The default implementation handles +# iteration through an array (list) of elements which is passed by +# reference to the constructor. An empty list is used if none is +# passed. The module may be sub-classed to provide custom +# implementations which iterate through any kind of data in any +# manner as long as it can conforms to the get_first()/get_next() +# interface. The object also implements the get_all() method for +# returning all remaining elements as a list reference. +# +# For further information on iterators see "Design Patterns", by the +# "Gang of Four" (Erich Gamma, Richard Helm, Ralph Johnson, John +# Vlissides), Addision-Wesley, ISBN 0-201-63361-2. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Iterator.pm,v 2.64 2004/01/13 16:19:15 abw Exp $ +# +#============================================================================ + +package Template::Iterator; + +require 5.004; + +use strict; +use vars qw( $VERSION $DEBUG $AUTOLOAD ); # AUTO? +use base qw( Template::Base ); +use Template::Constants; +use Template::Exception; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0 unless defined $DEBUG; + + +#======================================================================== +# ----- 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 (UNIVERSAL::can($data, '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 $self = shift; + my ($max, $index) = @$self{ qw( MAX INDEX ) }; + my $data = $self->{ _DATASET }; + + # warn about incorrect usage + unless (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... + if ($index < $max) { + # update counters and flags + $index++; + @$self{ qw( INDEX COUNT FIRST LAST ) } + = ( $index, $index + 1, 0, $index == $max ? 1 : 0 ); + @$self{ qw( PREV NEXT ) } = @$data[ $index - 1, $index + 1 ]; + return $data->[ $index ]; ## RETURN ## + } + else { + return (undef, Template::Constants::STATUS_DONE); ## 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; + + # if there's still some data to go... + if ($index < $max) { + $index++; + @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 ## + } +} + + +#------------------------------------------------------------------------ +# AUTOLOAD +# +# Provides access to internal fields (e.g. size, first, last, max, etc) +#------------------------------------------------------------------------ + +sub AUTOLOAD { + my $self = shift; + my $item = $AUTOLOAD; + $item =~ s/.*:://; + return if $item eq 'DESTROY'; + + # alias NUMBER to COUNT for backwards compatability + $item = 'COUNT' if $item =~ /NUMBER/i; + + 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__ + + +#------------------------------------------------------------------------ +# 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::Iterator - Data iterator used by the FOREACH directive + +=head1 SYNOPSIS + + my $iter = Template::Iterator->new(\@data, \%options); + +=head1 DESCRIPTION + +The Template::Iterator module defines a generic data iterator for use +by the 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 get_first() and 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 'key' and '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 as_list() method then the 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 MyListObject +object, $list. + +By adding an as_list() method to the MyListObject class, we can force +the 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 item, 'foo' and 'bar', which the +MyObjectList encapsulates. + +=head2 get_first() + +Returns a ($value, $error) pair for the first item in the iterator set. +The $error returned may be zero or undefined to indicate a valid datum +was successfully returned. Returns an error of STATUS_DONE if the list +is empty. + +=head2 get_next() + +Returns a ($value, $error) pair for the next item in the iterator set. +Returns an error of STATUS_DONE if all items in the list have been +visited. + +=head2 get_all() + +Returns a (\@values, $error) pair for all remaining items in the iterator +set. Returns an error of 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 size() - 1. + +=head2 index() + +Returns the current index number which is in the range 0 to max(). + +=head2 count() + +Returns the current iteration count in the range 1 to size(). This is +equivalent to index() + 1. Note that number() is supported as an alias +for count() for backwards compatability. + +=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 undef if the iterator is +on the first item. + +=head2 next() + +Returns the next item in the data set or undef if the iterator is on the +last item. + +=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|Template> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Library/HTML.pod b/lib/Template/Library/HTML.pod new file mode 100644 index 0000000..e39c120 --- /dev/null +++ b/lib/Template/Library/HTML.pod @@ -0,0 +1,316 @@ +#============================================================= -*-perl-*- +# +# Template::Library::HTML +# +# DESCRIPTION +# The HTML library provides a number of basic templates for use in +# building HTML pages. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# 2.69 +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Library::HTML - Template library for building basic HTML pages + +=head1 DESCRIPTION + +B<NOTE:> This documentation is incomplete and may be incorrect +in places. + +The 'html' template library is distributed as part of the Template +Toolkit. It can be found in the 'templates' sub-directory of the +installation directory. + + use Template; + + my $tt2 = Template->new({ + INCLUDE_PATH => '/usr/local/tt2/templates', + }); + +For a portable way to determine the installation 'templates' directory, +you can use the C<Template::Config-E<gt>instdir()> class method. + + use Template; + + my $tt2 = Template->new({ + INCLUDE_PATH => Template::Config->instdir('templates'), + }); + +You should now be able to access the html library as, for example: + + [% INCLUDE html/header %] + +Note that some of the more basic elements don't give you much more +than the raw HTML tags. In many cases you might be well advised to +stick to regular HTML rather than complicating matters by the use +of template elements. + +e.g. + + <table> + . . . + </table> + +vs + + [% WRAPPER html/table %] + . . . + [% END %] + +However, the use of template elements to generate the underlying HTML +does have some important benefits, particularly as the constructs start +to get more complicated and more magical. + +See the example in the 'examples' sub-directory of the distribution +directory for further examples and enlightenment on using this library. + +=head2 Headers, Footers and Pages + +=over 4 + +=item header + +The 'header' element generates the regular header required as the +pre-amble for an HTML document. That is, everything from the initial +E<lt>htmlE<gt> to the opening E<lt>bodyE<gt>. + + [% INCLUDE html/header + title = 'This is a Test' + bgcol = '#ffffff' + %] + +Additional header items can be provided by explicitly setting the 'headers' +variable, e.g. + + [% headers = BLOCK %] + <META name="description" content="Template Toolkit"> + <META name="REVISIT-AFTER" content="14 days"> + <META name="keywords" content="Templates, Web, ...etc..."> + [% END %] + + [% INCLUDE html/header + title = 'This is a Test' + bgcol = '#ffffff' + %] + +=item footer + +The 'footer' element generates the terminating E<lt>/bodyE<gt> and +E<lt>/htmlE<gt> element to balance the header. + + [% PROCESS html/header %] + + ...page content here... + + [% PROCESS html/footer %] + +=item page + +The 'page' element combines the 'html/header' and 'html/footer' elements. + + [% WRAPPER html/page %] + + ...page content here... + + [% END %] + +Page content should be defined in the 'content' variable (e.g. via WRAPPER). +Additional HTML headers should be defined in the 'headers' variable. + + [% WRAPPER html/page + headers = '<META name="keywords" content="foo, bar, ...">' + %] + + ...page content here... + + [% END %] + +=back + +=head2 Tables, Bars and Boxes + +=over 4 + +=item table + +A basic element for creating HTML tables. + + [% WRAPPER html/table pad=10 space=4 col='#404040' %] + <tr> + <td>Hello</td> <td>World</td> + </tr> + [% END %] + +The following variables may be defined: + +=over 4 + +=item border + +Set the border width (default: 0) + +=item col + +Set the background colour (default: none). + +=item width + +Set a fixed table width. + +=item pad + +Set the cellpadding. + +=item space + +Set the cellspacing. + +=item content + +Content for the box. Supplied automatically if used via WRAPPER. + +=back + +=item row + +A basic element for creating HTML table rows. + + [% WRAPPER html/table %] + [% WRAPPER html/row %] + <td>Hello</td> <td>World</td> + [% END %] + [% END %] + +The following variables may be defined: + +=over 4 + +=item col + +Set the background colour (default: none). + +=item valign + +Set the vertical alignment. + +=item rowspan + +Specify the number of rows to span. + +=item content + +Content for the box. Supplied automatically if used via WRAPPER. + +=back + +=item cell + +A basic element for creating HTML table cells. + + [% WRAPPER html/table %] + [% WRAPPER html/row %] + [% INCLUDE html/cell + FOREACH content = ['Hello', 'World'] %] + [% END %] + [% END %] + +The following variables may be defined: + +=over 4 + +=item col + +Set the background colour (default: none). + +=item align + +Set the horizontal alignment. + +=item colspan + +Specify the number of columns to span. + +=item content + +Content for the cell. Supplied automatically if used via WRAPPER. + +=back + +=item bar + +The bar element is a wrapping of html/table + html/row. + + [% WRAPPER html/bar %] + <td>Foo</td> <td>Bar</td> + [% END %] + +=item box + +The box element is a wrapping of html/table + html/row + html/cell + + [% WRAPPER html/box %] + Hello World! + [% END %] + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.69, 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::Library::Splash|Template::Library::Splash> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Library/PostScript.pod b/lib/Template/Library/PostScript.pod new file mode 100644 index 0000000..c30246c --- /dev/null +++ b/lib/Template/Library/PostScript.pod @@ -0,0 +1,78 @@ +#============================================================= -*-perl-*- +# +# Template::Library::PostScript +# +# DESCRIPTION +# This library contains a number of useful templates for generating +# PostScript pages. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# 2.69 +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Library::PostScript - Template library for generating PostScript + +=head1 DESCRIPTION + +The PostScript library contains a number of templates for generating +PostScript pages. It's very new, very incomplete, very ad-hoc and +isn't yet documented. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.69, 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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Library/Splash.pod b/lib/Template/Library/Splash.pod new file mode 100644 index 0000000..e8c4f8b --- /dev/null +++ b/lib/Template/Library/Splash.pod @@ -0,0 +1,1030 @@ +#============================================================= -*-perl-*- +# +# Template::Library::Splash +# +# DESCRIPTION +# The Splash! library is built on top of the HTML library and +# implements a set of widgets for easy construction of stylish HTML +# pages +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# 2.69 +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Library::Splash - Template library for building stylish HTML user interfaces + +=head1 DESCRIPTION + +B<NOTE:> This documentation is incomplete, incorrect and outdated. +The Splash! library is still evolving and subject to change. See +the examples for a much more recent and accurate demonstration of +use. + +=head2 Introduction + +The 'Splash' template library is distributed as part of the Template +Toolkit. It can be found in the 'templates' sub-directory of the +installation directory. + + /your/tt2/installation + | + +-- docs + | ... + | + +-- images + | ... + | + +-- examples + | ... + | + +-- templates + | + +-- html + | ... + +-- pod + | ... + +-- splash <<<< YOU ARE HERE + ... + + +To use the Splash library, you first need to tell the Template Toolkit +where to find the template files. + + use Template; + + my $tt2 = Template->new({ + INCLUDE_PATH => '/usr/local/tt2/templates', + }); + +For a portable way to determine the installation 'templates' directory, +you can use the C<Template::Config-E<gt>instdir()> class method. + + use Template; + + my $tt2 = Template->new({ + INCLUDE_PATH => Template::Config->instdir('templates'), + }); + +Note that you should set the INCLUDE_PATH to the 'templates' directory +as shown here and don't be tempted to set the INCLUDE_PATH to +'templates/splash'. Many of the Splash! components use elements in +the 'html' directory and contain directives of the form: + + [% INCLUDE html/something %]. + +=head2 Configuration + +The 'splash/config' template defines a 'splash' hash array which +contains numerous configuration items for the Splash library. You +must PROCESS this template to ensure that the hash definition is +imported into your calling template. An INCLUDE is not sufficient as +it localises variables and prevents the 'splash' hash array from +existing outside the splash/config template. + + [% PROCESS splash/config %] + +Alternately, you can define the splash/config template as a PRE_PROCESS +item when you create the Template processor. + + use Template; + + my $tt2 = Template->new({ + INCLUDE_PATH => Template::Config->instdir('templates'), + PRE_PROCESS => 'splash/config', + }); + +You can modify the default configuration by creating your own +PRE_PROCESS config file which loads the 'splash/config' and then +tweaks the settings to your own preferences. + + my $tt2 = Template->new({ + INCLUDE_PATH => [ '/home/abw/tt2/templates', + Template::Config->instdir('templates') ], + PRE_PROCESS => 'config' + }); + +/home/abw/tt2/templates/config: + + [% # load the 'splash' configuration + PROCESS splash/config; + + # tweak values to personal preferences + splash.images = '/~abw/tt2/images/splash' + splash.select.col = 'leaf' + splash.unselect.col = 'bud' + %] + +The splash/config file includes some instructional comments on +things you might like to tweak. + +=head2 Colours + +The Splash! library uses the colours defined in the html/rgb template. +The 'rgb' hash defined therein is imported as the 'splash.rgb' hash. + + [% INCLUDE splash/box col='grey75' %] + +See the examples for further enlightenment on using colour. + +=head2 Style + +There are two very primitive "styles" implemented called "select" and +"unselect". These are used to indicate which item on a menu is +selected, for example. Each style defines characteristics like +background colour, font face, size and colour, text alignment, and so +on. + +The styles are implemented as hashes within the 'splash' hash. Many +of the components respond to a 'style' variable being set and you can +pass a direct reference to splash.select or splash.unselect (or your +own styles). e.g. + + [% INCLUDE splash/button + content = "Unselected" + style = splash.unselect + %] + [% INCLUDE splash/button + content ="Selected" + style = splash.select + %] + +Alternately, you can use the 'select' variable to indicate either +of the inbuilt styles: splash.select or splash.unselect. + + [% INCLUDE splash/button + content = "Unselected" + select = 0 + %] + [% INCLUDE splash/button + content = "Selected" + select = 1 + %] + +=head1 COMPONENT TEMPLATES + +This section describes some of the component templates in the Splash! +library. This documentation is incomplete and may also be inaccurate +in places. The examples in the 'examples' directory are likely to be +a much better reference. + + +=head2 splash/text + +Simple template to format text according to a selected/unselected style, +adding links, etc. + + [% INCLUDE splash/text + content = 'Template Toolkit' + link = 'http://www.template-toolkit.org' + select = 0 + bold = 1 + %] + + +Configuration items: + +=over 4 + + +=item content + +Text content. + + +=item link + +URL which can be defined to make the text a link. + + +=item style + +Reference to a style hash. + + +=item select + +Flag to default the style to splash.select (select == true value) or +splash.unselect (select == false value). + + +=back + +The following items default to the relevant style values: + +=over 4 + + +=item col (style.col.text) + + +=item font (style.font.face) + + +=item bold (style.font.bold) + + +=item size (style.font.size) + + +=back + + + +=head2 splash/table + +A thin wrapper around html/table, allowing a colour to be specified +by name. + + [% WRAPPER splash/table + col = 'aqua' + pad = 4 + width = '100%' + %] + <tr> + <td>Foo</td> + <td>Bar</td> + </tr> + [% END %] + + +Configuration items: + +=over 4 + + +=item content + +Table content. + + +=item col + +Background colour. + + +=item border + +Border width (default: 0) + + +=item width + +Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%'). + +=item pad + +Cell padding. + + +=item space + +Cell padding. + + +=back + + + +=head2 splash/row + +Creates a row for an HTML table. + + [% WRAPPER splash/table %] + + [% WRAPPER splash/row col='marine' %] + <td>Foo</td><td>Bar</td> + [% END %] + + [% WRAPPER splash/row col='aqua' %] + <td>Foo</td><td>Bar</td> + [% END %] + + [% END %] + + +Configuration items: + +=over 4 + + +=item content + +Row content. + + +=item col + +Background colour. + + +=item valign + +Vertical alignment + + +=item rowspan + +Number of rows to span. + + +=back + + + +=head2 splash/cell + +Creates a cell for an HTML table. + + [% WRAPPER splash/table + splash/row + splash/cell col='grey75' %] + Hello World + [% END %] + + +Configuration items: + +=over 4 + + +=item content + +Cell content. + + +=item col + +Background colour. + + +=item align + +Horizontal alignment + + +=item colspan + +Number of columns to span. + + +=back + + + +=head2 splash/box + +A box created from a union of splash/table, splash/row and splash/cell. +The following is equivalent to the previous example. + + [% WRAPPER splash/box col='grey75' %] + Hello World + [% END %] + +Configuration items are as per the individual templates. + + +=head2 splash/button + +Creates a small button with rounded corners. + + [% INCLUDE splash/button + content = 'Template Toolkit' + select = 1 + width = '50%' + %] + + +Configuration items: + +=over 4 + + +=item content + +Button content. + + +=item style + +Reference to a style hash. + + +=item select + +Flag to default the style to splash.select (select == true value) or +splash.unselect (select == false value). + + +=item width + +Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%'). + +=back + +The following items default to the relevant style values: + +=over 4 + + +=item col (style.col.text) + + +=item textcol (style.col.text) + + +=item font (style.font.face) + + +=item size (style.font.size) + + +=item bold (style.font.bold) + + +=item width (style.button.width) + + +=item align (style.button.align) + + +=back + + + +=head2 splash/bar + +Creates a bar with rounded corners at either the top or bottom, and +square corners on the other. Default has rounded at the top, set +'invert' to select bottom. + + [% INCLUDE splash/bar + content = 'Hello World', + select = 1 + %] + + +Configuration items: + +=over 4 + + +=item content + +Bar content. + + +=item style + +Reference to a style hash. + + +=item select + +Flag to default the style to splash.select (select == true value) or +splash.unselect (select == false value). + + +=item width + +Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%'). + +=item invert + +Flag to invert bar to hang down instead of sitting +upright. + + +=back + +The following items default to the relevant style values: + +=over 4 + + +=item col (style.col.text) + + +=item textcol (style.col.text) + + +=item font (style.font.face) + + +=item size (style.font.size) + + +=item bold (style.font.bold) + + +=item width (style.button.width) + + +=item align (style.button.align) + + +=back + + +=head2 splash/hair + +Generates a frame enclosing the content within crosshair corners. + + [% INCLUDE splash/hair + content = 'Template Toolkit' + %] + + +Configuration items: + +=over 4 + + +=item content + +Hair content. + + +=item style + +Reference to a style hash. + + +=back + +The following items default to the relevant style values: + +=over 4 + + +=item col (style.col.text) + + +=item bgcol (style.col.back) + + +=item align (style.button.align) + + +=back + + +=head2 splash/menu + +Creates a menu as a series of splash/button elements. + + [% buttons = [ + { text => 'One', link => 'one.html' } + { text => 'Two', link => 'two.html' } + ] + %] + + [% INCLUDE splash/menu + select = 2 # Two + %] + + +Configuration items: + +=over 4 + + +=item buttons + +A reference to a list of hash arrays containing 'text' and 'link' items. + + +=item select (n or 0) + +Indicates which button should be selected. First item is 1. 0 indicates +no button selected. + + +=item width + +Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%'). + +=item align + +Horizontal alignment + + +=back + + + +=head2 splash/menubar + +As above, but incorporated into a wider bar. + + [% WRAPPER splash/menubar %] + Section Title + [% END %] + + +Configuration items: + +=over 4 + + +=item buttons + +A reference to a list of hash arrays containing 'text' and 'link' items. + + +=item select (n or 0) + +Indicates which button should be selected. First item is 1. 0 indicates +no button selected. + + +=item width + +Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%'). + +=item align + +Horizontal alignment + + +=back + + + +=head2 splash/panel + +A table with a coloured edge. + + [% WRAPPER splash/panel edge='black' fill='grey75' border=2 %] + <tr> + <td>Hello World</td> + </tr> + [% END %] + + +Configuration items: + +=over 4 + + +=item content + +Panel content. + + +=item style + +Reference to a style hash. + + +=item select + +Flag to default the style to splash.select (select == true value) or +splash.unselect (select == false value). + + +=item width + +Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%'). + +=item align + +Horizontal alignment + + +=item border + +Border width (default: 0) + + +=back + +The following items default to the relevant style values: + +=over 4 + + +=item edge (style.col.edge) + + +=item fill (style.col.fill) + + +=item pad (style.pad) + + +=back + + + +=head2 splash/pane + +A union of splash/row + splash/cell. + + [% WRAPPER splash/panel select=1 %] + [% WRAPPER splash/pane col='grey75' %] + Hello World + [% END %] + + [% WRAPPER splash/pane col='grey50' %] + Hello Again + [% END %] + [% END %] + + +=head2 splash/tab + +A simple button looking like a page tab. + + [% INCLUDE splash/tab + content = 'Option 1' + col = 'aqua' + %] + + +Configuration items: + +=over 4 + + +=item content + +Tab content. + + +=item style + +Reference to a style hash. + + +=item select + +Flag to default the style to splash.select (select == true value) or +splash.unselect (select == false value). + + +=item width + +Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%'). + +=item align + +Horizontal alignment + + +=back + +The following items default to the relevant style values: + +=over 4 + + +=item col (style.col.text) + + +=item textcol (style.col.text) + + +=item font (style.font.face) + + +=item size (style.font.size) + + +=item bold (style.font.bold) + + +=item tabalign (style.tab.align) + + +=back + + + +=head2 splash/tabset + +A set of splash/tab components, similar to a menu. + + +Configuration items: + +=over 4 + + +=item tabs + +List of hash references containing text/link entries, as per +menu buttons. + + +=item select + +Flag to default the style to splash.select (select == true value) or +splash.unselect (select == false value). + + +=item invert + +Flag to invert tab to hang down instead of sitting +upright. + + +=back + + + +=head2 splash/tabbox + +Add a splash/tab to the top of a splash/box. + + +Configuration items: + +=over 4 + + +=item title + + title. + + +=item content + + content. + + +=item width + +Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%'). + +=item tabwidth + +Width of tabs. + + +=item select + +Flag to default the style to splash.select (select == true value) or +splash.unselect (select == false value). + + +=item border + +Border width (default: 0) + + +=back + +The following items default to the relevant style values: + +=over 4 + + +=item col (style.col.text) + + +=item fill (style.col.fill) + + +=item tabalign (style.tab.align) + + +=item tablocate (style.tab.locate) + + +=back + + + +=head2 splash/tabsbox + +Add a splash/tabset to the top of a splash/box. + + +Configuration items: + +=over 4 + + +=item tabs + +List of hash references containing text/link entries, as per +menu buttons. + + +=item select + +Flag to default the style to splash.select (select == true value) or +splash.unselect (select == false value). + + +=item content + + content. + + +=item width + +Width in absolute pixels (e.g. '100') or as a percentage (e.g. '50%'). + +=item border + +Border width (default: 0) + + +=item invert + +Flag to invert to hang down instead of sitting +upright. + + +=back + +The following items default to the relevant style values: + +=over 4 + + +=item col (style.col.text) + + +=item fill (style.col.fill) + + +=item tabalign (style.tab.align) + + +=item tablocate (style.tab.locate) + + +=back + + +=head2 splash/tabspanel + +As per splash/tabsbox, but attached to a splash/panel instead of a +splash/box. + + +=head1 EXAMPLES + +See the examples in the 'examples' sub-directory of the installation +for comprehensive examples showing use of the Splash! library. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.69, 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::Library::HTML|Template::Library::HTML> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual.pod b/lib/Template/Manual.pod new file mode 100644 index 0000000..8775a5b --- /dev/null +++ b/lib/Template/Manual.pod @@ -0,0 +1,180 @@ +#============================================================= -*-perl-*- +# +# Template::Manual +# +# DESCRIPTION +# This is the comprehensive user guide and reference manual for the +# Template Toolkit. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual - User guide and reference manual for the Template Toolkit + +=head1 DESCRIPTION + +This is the comprehensive user guide and reference manual for the +Template Toolkit. + +=over 4 + +=item L<Template::Manual::Intro|Template::Manual::Intro> + +Introduction to the Template Toolkit + +This section provides a general introduction to the Template Toolkit, +giving a quick overview of features, examples of template directives +and use of the Template.pm module. It also described the basic concept +underlying the toolkit: the separation of presentation elements from +application logic and data. + +=item L<Template::Manual::Syntax|Template::Manual::Syntax> + +Directive syntax, structure and semantics + +This section describes the syntax, structure and semantics of the +Template Toolkit directives and general presentation language. + +=item L<Template::Manual::Directives|Template::Manual::Directives> + +Template directives + +This section provides a reference of all Template Toolkit directives, +complete with examples of use. + +=item L<Template::Manual::Variables|Template::Manual::Variables> + +Template variables and code bindings + +This section describes the different ways in which Perl data can be +bound to template variables and accessed via Template Toolkit +directives. + +=item L<Template::Manual::VMethods|Template::Manual::VMethods> + +Virtual Methods + +The Template Toolkit provides virtual methods for manipulating variable +values. Most of them are analogous to regular Perl functions of the +same names. This section describes the different virtual methods that +can be applied to scalar, list and hash values. + +=item L<Template::Manual::Config|Template::Manual::Config> + +Configuration options + +This section contains details of all the configuration options that can +be used to customise the behaviour and extend the features of the +Template Toolkit. + +=item L<Template::Manual::Filters|Template::Manual::Filters> + +Standard filters + +This section lists all the standard filters distributed with the +Template Toolkit for post-processing output. + +=item L<Template::Manual::Plugins|Template::Manual::Plugins> + +Standard plugins + +This section lists the standard plugins which can be used to extend the +runtime functionality of the Template Toolkit. The plugins are +distributed with the Template Toolkit but may required additional +modules from CPAN. + +=item L<Template::Manual::Internals|Template::Manual::Internals> + +Template Toolkit internals + +This document provides an overview of the internal architecture of the +Template Toolkit. It is a work in progress and is far from complete, +currently providing little more than an overview of how the major +components fit together. Nevertheless, it's a good starting point for +anyone wishing to delve into the source code to find out how it all +works. + +=item L<Template::Manual::Views|Template::Manual::Views> + +Template Toolkit views (experimental) + +This section describes dynamic views: a powerful but experimental new +feature in version 2.01 of the Template Toolkit. + +=item L<Template::Manual::Refs|Template::Manual::Refs> + +Related modules, projects and other resources + +This section provides references to external modules, projects and +other resources related to the Template Toolkit. + +=item L<Template::Manual::Credits|Template::Manual::Credits> + +Author and contributor credits + +This section provides a brief history of the Template Toolkit and +details the primary author and numerous other people who have +contributed to it. + + + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Config.pod b/lib/Template/Manual/Config.pod new file mode 100644 index 0000000..5020556 --- /dev/null +++ b/lib/Template/Manual/Config.pod @@ -0,0 +1,2122 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Config +# +# DESCRIPTION +# This section contains details of all the configuration options that +# can be used to customise the behaviour and extend the features of +# the Template Toolkit. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Config - Configuration options + +=head1 DESCRIPTION + +This section contains details of all the configuration options that can +be used to customise the behaviour and extend the features of the +Template Toolkit. + +=head2 Template Style and Parsing Options + +=over 4 + + + +=item START_TAG, END_TAG + +The START_TAG and END_TAG options are used to specify character +sequences or regular expressions that mark the start and end of a +template directive. The default values for START_TAG and END_TAG are +'[%' and '%]' respectively, giving us the familiar directive style: + + [% example %] + +Any Perl regex characters can be used and therefore should be escaped +(or use the Perl C<quotemeta> function) if they are intended to +represent literal characters. + + my $template = Template->new({ + START_TAG => quotemeta('<+'), + END_TAG => quotemeta('+>'), + }); + +example: + + <+ INCLUDE foobar +> + +The TAGS directive can also be used to set the START_TAG and END_TAG values +on a per-template file basis. + + [% TAGS <+ +> %] + + + + + + +=item TAG_STYLE + +The TAG_STYLE option can be used to set both START_TAG and END_TAG +according to pre-defined tag styles. + + my $template = Template->new({ + TAG_STYLE => 'star', + }); + +Available styles are: + + template [% ... %] (default) + template1 [% ... %] or %% ... %% (TT version 1) + metatext %% ... %% (Text::MetaText) + star [* ... *] (TT alternate) + php <? ... ?> (PHP) + asp <% ... %> (ASP) + mason <% ... > (HTML::Mason) + html <!-- ... --> (HTML comments) + +Any values specified for START_TAG and/or END_TAG will over-ride +those defined by a TAG_STYLE. + +The TAGS directive may also be used to set a TAG_STYLE + + [% TAGS html %] + <!-- INCLUDE header --> + + + + + + +=item PRE_CHOMP, POST_CHOMP + +Anything outside a directive tag is considered plain text and is +generally passed through unaltered (but see the INTERPOLATE option). +This includes all whitespace and newlines characters surrounding +directive tags. Directives that don't generate any output will leave +gaps in the output document. + +Example: + + Foo + [% a = 10 %] + Bar + +Output: + + Foo + + Bar + +The PRE_CHOMP and POST_CHOMP options can help to clean up some of this +extraneous whitespace. Both are disabled by default. + + my $template = Template->new({ + PRE_CHOMP => 1, + POST_CHOMP => 1, + }); + +With PRE_CHOMP set to 1, the newline and whitespace preceding a directive +at the start of a line will be deleted. This has the effect of +concatenating a line that starts with a directive onto the end of the +previous line. + + Foo <----------. + | + ,---(PRE_CHOMP)----' + | + `-- [% a = 10 %] --. + | + ,---(POST_CHOMP)---' + | + `-> Bar + +With POST_CHOMP set to 1, any whitespace after a directive up to and +including the newline will be deleted. This has the effect of joining +a line that ends with a directive onto the start of the next line. + +If PRE_CHOMP or POST_CHOMP is set to 2, then instead of removing all +the whitespace, the whitespace will be collapsed to a single space. +This is useful for HTML, where (usually) a contiguous block of +whitespace is rendered the same as a single space. + +You may use the CHOMP_NONE, CHOMP_ALL, and CHOMP_COLLAPSE constants +from the Template::Constants module to deactivate chomping, remove +all whitespace, or collapse whitespace to a single space. + +PRE_CHOMP and POST_CHOMP can be activated for individual directives by +placing a '-' immediately at the start and/or end of the directive. + + [% FOREACH user = userlist %] + [%- user -%] + [% END %] + +The '-' characters activate both PRE_CHOMP and POST_CHOMP for the one +directive '[%- name -%]'. Thus, the template will be processed as if +written: + + [% FOREACH user = userlist %][% user %][% END %] + +Note that this is the same as if PRE_CHOMP and POST_CHOMP were set +to CHOMP_ALL; the only way to get the CHOMP_COLLAPSE behavior is +to set PRE_CHOMP or POST_CHOMP accordingly. If PRE_CHOMP or POST_CHOMP +is already set to CHOMP_COLLAPSE, using '-' will give you CHOMP_COLLAPSE +behavior, not CHOMP_ALL behavior. + +Similarly, '+' characters can be used to disable PRE_CHOMP or +POST_CHOMP (i.e. leave the whitespace/newline intact) options on a +per-directive basis. + + [% FOREACH user = userlist %] + User: [% user +%] + [% END %] + +With POST_CHOMP enabled, the above example would be parsed as if written: + + [% FOREACH user = userlist %]User: [% user %] + [% END %] + + + + + +=item TRIM + +The TRIM option can be set to have any leading and trailing whitespace +automatically removed from the output of all template files and BLOCKs. + +By example, the following BLOCK definition + + [% BLOCK foo %] + Line 1 of foo + [% END %] + +will be processed is as "\nLine 1 of foo\n". When INCLUDEd, the surrounding +newlines will also be introduced. + + before + [% INCLUDE foo %] + after + +output: + before + + Line 1 of foo + + after + +With the TRIM option set to any true value, the leading and trailing +newlines (which count as whitespace) will be removed from the output +of the BLOCK. + + before + Line 1 of foo + after + +The TRIM option is disabled (0) by default. + + + + + +=item INTERPOLATE + +The INTERPOLATE flag, when set to any true value will cause variable +references in plain text (i.e. not surrounded by START_TAG and END_TAG) +to be recognised and interpolated accordingly. + + my $template = Template->new({ + INTERPOLATE => 1, + }); + +Variables should be prefixed by a '$' to identify them. Curly braces +can be used in the familiar Perl/shell style to explicitly scope the +variable name where required. + + # INTERPOLATE => 0 + <a href="http://[% server %]/[% help %]"> + <img src="[% images %]/help.gif"></a> + [% myorg.name %] + + # INTERPOLATE => 1 + <a href="http://$server/$help"> + <img src="$images/help.gif"></a> + $myorg.name + + # explicit scoping with { } + <img src="$images/${icon.next}.gif"> + +Note that a limitation in Perl's regex engine restricts the maximum length +of an interpolated template to around 32 kilobytes or possibly less. Files +that exceed this limit in size will typically cause Perl to dump core with +a segmentation fault. If you routinely process templates of this size +then you should disable INTERPOLATE or split the templates in several +smaller files or blocks which can then be joined backed together via +PROCESS or INCLUDE. + + + + + + + +=item ANYCASE + +By default, directive keywords should be expressed in UPPER CASE. The +ANYCASE option can be set to allow directive keywords to be specified +in any case. + + # ANYCASE => 0 (default) + [% INCLUDE foobar %] # OK + [% include foobar %] # ERROR + [% include = 10 %] # OK, 'include' is a variable + + # ANYCASE => 1 + [% INCLUDE foobar %] # OK + [% include foobar %] # OK + [% include = 10 %] # ERROR, 'include' is reserved word + +One side-effect of enabling ANYCASE is that you cannot use a variable +of the same name as a reserved word, regardless of case. The reserved +words are currently: + + GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER + IF UNLESS ELSE ELSIF FOR FOREACH WHILE SWITCH CASE + USE PLUGIN FILTER MACRO PERL RAWPERL BLOCK META + TRY THROW CATCH FINAL NEXT LAST BREAK RETURN STOP + CLEAR TO STEP AND OR NOT MOD DIV END + + +The only lower case reserved words that cannot be used for variables, +regardless of the ANYCASE option, are the operators: + + and or not mod div + + + + + + +=back + +=head2 Template Files and Blocks + +=over 4 + + + +=item INCLUDE_PATH + +The INCLUDE_PATH is used to specify one or more directories in which +template files are located. When a template is requested that isn't +defined locally as a BLOCK, each of the INCLUDE_PATH directories is +searched in turn to locate the template file. Multiple directories +can be specified as a reference to a list or as a single string where +each directory is delimited by ':'. + + my $template = Template->new({ + INCLUDE_PATH => '/usr/local/templates', + }); + + my $template = Template->new({ + INCLUDE_PATH => '/usr/local/templates:/tmp/my/templates', + }); + + my $template = Template->new({ + INCLUDE_PATH => [ '/usr/local/templates', + '/tmp/my/templates' ], + }); + +On Win32 systems, a little extra magic is invoked, ignoring delimiters +that have ':' followed by a '/' or '\'. This avoids confusion when using +directory names like 'C:\Blah Blah'. + +When specified as a list, the INCLUDE_PATH path can contain elements +which dynamically generate a list of INCLUDE_PATH directories. These +generator elements can be specified as a reference to a subroutine or +an object which implements a paths() method. + + my $template = Template->new({ + INCLUDE_PATH => [ '/usr/local/templates', + \&incpath_generator, + My::IncPath::Generator->new( ... ) ], + }); + +Each time a template is requested and the INCLUDE_PATH examined, the +subroutine or object method will be called. A reference to a list of +directories should be returned. Generator subroutines should report +errors using die(). Generator objects should return undef and make an +error available via its error() method. + +For example: + + sub incpath_generator { + + # ...some code... + + if ($all_is_well) { + return \@list_of_directories; + } + else { + die "cannot generate INCLUDE_PATH...\n"; + } + } + +or: + + package My::IncPath::Generator; + + # Template::Base (or Class::Base) provides error() method + use Template::Base; + use base qw( Template::Base ); + + sub paths { + my $self = shift; + + # ...some code... + + if ($all_is_well) { + return \@list_of_directories; + } + else { + return $self->error("cannot generate INCLUDE_PATH...\n"); + } + } + + 1; + + + + + +=item DELIMITER + +Used to provide an alternative delimiter character sequence for +separating paths specified in the INCLUDE_PATH. The default +value for DELIMITER is ':'. + + # tolerate Silly Billy's file system conventions + my $template = Template->new({ + DELIMITER => '; ', + INCLUDE_PATH => 'C:/HERE/NOW; D:/THERE/THEN', + }); + + # better solution: install Linux! :-) + +On Win32 systems, the default delimiter is a little more intelligent, +splitting paths only on ':' characters that aren't followed by a '/'. +This means that the following should work as planned, splitting the +INCLUDE_PATH into 2 separate directories, C:/foo and C:/bar. + + # on Win32 only + my $template = Template->new({ + INCLUDE_PATH => 'C:/Foo:C:/Bar' + }); + +However, if you're using Win32 then it's recommended that you +explicitly set the DELIMITER character to something else (e.g. ';') +rather than rely on this subtle magic. + + + + +=item ABSOLUTE + +The ABSOLUTE flag is used to indicate if templates specified with +absolute filenames (e.g. '/foo/bar') should be processed. It is +disabled by default and any attempt to load a template by such a +name will cause a 'file' exception to be raised. + + my $template = Template->new({ + ABSOLUTE => 1, + }); + + # this is why it's disabled by default + [% INSERT /etc/passwd %] + +On Win32 systems, the regular expression for matching absolute +pathnames is tweaked slightly to also detect filenames that start +with a driver letter and colon, such as: + + C:/Foo/Bar + + + + + + +=item RELATIVE + +The RELATIVE flag is used to indicate if templates specified with +filenames relative to the current directory (e.g. './foo/bar' or +'../../some/where/else') should be loaded. It is also disabled by +default, and will raise a 'file' error if such template names are +encountered. + + my $template = Template->new({ + RELATIVE => 1, + }); + + [% INCLUDE ../logs/error.log %] + + + + + +=item DEFAULT + +The DEFAULT option can be used to specify a default template which should +be used whenever a specified template can't be found in the INCLUDE_PATH. + + my $template = Template->new({ + DEFAULT => 'notfound.html', + }); + +If a non-existant template is requested through the Template process() +method, or by an INCLUDE, PROCESS or WRAPPER directive, then the +DEFAULT template will instead be processed, if defined. Note that the +DEFAULT template is not used when templates are specified with +absolute or relative filenames, or as a reference to a input file +handle or text string. + + + + + +=item BLOCKS + +The BLOCKS option can be used to pre-define a default set of template +blocks. These should be specified as a reference to a hash array +mapping template names to template text, subroutines or Template::Document +objects. + + my $template = Template->new({ + BLOCKS => { + header => 'The Header. [% title %]', + footer => sub { return $some_output_text }, + another => Template::Document->new({ ... }), + }, + }); + + + + +=item AUTO_RESET + +The AUTO_RESET option is set by default and causes the local BLOCKS +cache for the Template::Context object to be reset on each call to the +Template process() method. This ensures that any BLOCKs defined +within a template will only persist until that template is finished +processing. This prevents BLOCKs defined in one processing request +from interfering with other independent requests subsequently +processed by the same context object. + +The BLOCKS item may be used to specify a default set of block definitions +for the Template::Context object. Subsequent BLOCK definitions in templates +will over-ride these but they will be reinstated on each reset if AUTO_RESET +is enabled (default), or if the Template::Context reset() method is called. + + + + + + + + + +=item RECURSION + +The template processor will raise a file exception if it detects +direct or indirect recursion into a template. Setting this option to +any true value will allow templates to include each other recursively. + + + +=back + +=head2 Template Variables + +=over 4 + +=item VARIABLES, PRE_DEFINE + +The VARIABLES option (or PRE_DEFINE - they're equivalent) can be used +to specify a hash array of template variables that should be used to +pre-initialise the stash when it is created. These items are ignored +if the STASH item is defined. + + my $template = Template->new({ + VARIABLES => { + title => 'A Demo Page', + author => 'Joe Random Hacker', + version => 3.14, + }, + }; + +or + + my $template = Template->new({ + PRE_DEFINE => { + title => 'A Demo Page', + author => 'Joe Random Hacker', + version => 3.14, + }, + }; + + + + +=item CONSTANTS + +The CONSTANTS option can be used to specify a hash array of template +variables that are compile-time constants. These variables are +resolved once when the template is compiled, and thus don't require +further resolution at runtime. This results in significantly faster +processing of the compiled templates and can be used for variables that +don't change from one request to the next. + + my $template = Template->new({ + CONSTANTS => { + title => 'A Demo Page', + author => 'Joe Random Hacker', + version => 3.14, + }, + }; + +=item CONSTANT_NAMESPACE + +Constant variables are accessed via the 'constants' namespace by +default. + + [% constants.title %] + +The CONSTANTS_NAMESPACE option can be set to specify an alternate +namespace. + + my $template = Template->new({ + CONSTANTS => { + title => 'A Demo Page', + # ...etc... + }, + CONSTANTS_NAMESPACE => 'const', + }; + +In this case the constants would then be accessed as: + + [% const.title %] + +=item NAMESPACE + +The constant folding mechanism described above is an example of a +namespace handler. Namespace handlers can be defined to provide +alternate parsing mechanisms for variables in different namespaces. + +Under the hood, the Template module converts a constructor configuration +such as: + + my $template = Template->new({ + CONSTANTS => { + title => 'A Demo Page', + # ...etc... + }, + CONSTANTS_NAMESPACE => 'const', + }; + +into one like: + + my $template = Template->new({ + NAMESPACE => { + const => Template:::Namespace::Constants->new({ + title => 'A Demo Page', + # ...etc... + }), + }, + }; + +You can use this mechanism to define multiple constant namespaces, or +to install custom handlers of your own. + + my $template = Template->new({ + NAMESPACE => { + site => Template:::Namespace::Constants->new({ + title => "Wardley's Widgets", + version => 2.718, + }), + author => Template:::Namespace::Constants->new({ + name => 'Andy Wardley', + email => 'abw@andywardley.com', + }), + voodoo => My::Namespace::Handler->new( ... ), + }, + }; + +Now you have 2 constant namespaces, for example: + + [% site.title %] + [% author.name %] + +as well as your own custom namespace handler installed for the 'voodoo' +namespace. + + [% voodoo.magic %] + +See L<Template::Namespace::Constants|Template::Namespace::Constants> +for an example of what a namespace handler looks like on the inside. + + + + + +=back + +=head2 Template Processing Options + + +The following options are used to specify any additional templates +that should be processed before, after, around or instead of the +template passed as the first argument to the Template process() +method. These options can be perform various useful tasks such as +adding standard headers or footers to all pages, wrapping page output +in other templates, pre-defining variables or performing +initialisation or cleanup tasks, automatically generating page summary +information, navigation elements, and so on. + +The task of processing the template is delegated internally to the +Template::Service module which, unsurprisingly, also has a process() +method. Any templates defined by the PRE_PROCESS option are processed +first and any output generated is added to the output buffer. Then +the main template is processed, or if one or more PROCESS templates +are defined then they are instead processed in turn. In this case, +one of the PROCESS templates is responsible for processing the main +template, by a directive such as: + + [% PROCESS $template %] + +The output of processing the main template or the PROCESS template(s) +is then wrapped in any WRAPPER templates, if defined. WRAPPER +templates don't need to worry about explicitly processing the template +because it will have been done for them already. Instead WRAPPER +templates access the content they are wrapping via the 'content' +variable. + + wrapper before + [% content %] + wrapper after + +This output generated from processing the main template, and/or any +PROCESS or WRAPPER templates is added to the output buffer. Finally, +any POST_PROCESS templates are processed and their output is also +added to the output buffer which is then returned. + +If the main template throws an exception during processing then any +relevant template(s) defined via the ERROR option will be processed +instead. If defined and successfully processed, the output from the +error template will be added to the output buffer in place of the +template that generated the error and processing will continue, +applying any WRAPPER and POST_PROCESS templates. If no relevant ERROR +option is defined, or if the error occurs in one of the PRE_PROCESS, +WRAPPER or POST_PROCESS templates, then the process will terminate +immediately and the error will be returned. + + + +=over 4 + + + +=item PRE_PROCESS, POST_PROCESS + +These values may be set to contain the name(s) of template files +(relative to INCLUDE_PATH) which should be processed immediately +before and/or after each template. These do not get added to +templates processed into a document via directives such as INCLUDE, +PROCESS, WRAPPER etc. + + my $template = Template->new({ + PRE_PROCESS => 'header', + POST_PROCESS => 'footer', + }; + +Multiple templates may be specified as a reference to a list. Each is +processed in the order defined. + + my $template = Template->new({ + PRE_PROCESS => [ 'config', 'header' ], + POST_PROCESS => 'footer', + }; + +Alternately, multiple template may be specified as a single string, +delimited by ':'. This delimiter string can be changed via the +DELIMITER option. + + my $template = Template->new({ + PRE_PROCESS => 'config:header', + POST_PROCESS => 'footer', + }; + +The PRE_PROCESS and POST_PROCESS templates are evaluated in the same +variable context as the main document and may define or update +variables for subsequent use. + +config: + + [% # set some site-wide variables + bgcolor = '#ffffff' + version = 2.718 + %] + +header: + + [% DEFAULT title = 'My Funky Web Site' %] + <html> + <head> + <title>[% title %]</title> + </head> + <body bgcolor="[% bgcolor %]"> + +footer: + + <hr> + Version [% version %] + </body> + </html> + +The Template::Document object representing the main template being processed +is available within PRE_PROCESS and POST_PROCESS templates as the 'template' +variable. Metadata items defined via the META directive may be accessed +accordingly. + + $template->process('mydoc.html', $vars); + +mydoc.html: + + [% META title = 'My Document Title' %] + blah blah blah + ... + +header: + + <html> + <head> + <title>[% template.title %]</title></head> + <body bgcolor="[% bgcolor %]"> + + + + + + + + + + + + + + +=item PROCESS + +The PROCESS option may be set to contain the name(s) of template files +(relative to INCLUDE_PATH) which should be processed instead of the +main template passed to the Template process() method. This can +be used to apply consistent wrappers around all templates, similar to +the use of PRE_PROCESS and POST_PROCESS templates. + + my $template = Template->new({ + PROCESS => 'content', + }; + + # processes 'content' instead of 'foo.html' + $template->process('foo.html'); + +A reference to the original template is available in the 'template' +variable. Metadata items can be inspected and the template can be +processed by specifying it as a variable reference (i.e. prefixed by +'$') to an INCLUDE, PROCESS or WRAPPER directive. + +content: + + <html> + <head> + <title>[% template.title %]</title> + </head> + + <body> + [% PROCESS $template %] + <hr> + © Copyright [% template.copyright %] + </body> + </html> + +foo.html: + + [% META + title = 'The Foo Page' + author = 'Fred Foo' + copyright = '2000 Fred Foo' + %] + <h1>[% template.title %]</h1> + Welcome to the Foo Page, blah blah blah + +output: + + <html> + <head> + <title>The Foo Page</title> + </head> + + <body> + <h1>The Foo Page</h1> + Welcome to the Foo Page, blah blah blah + <hr> + © Copyright 2000 Fred Foo + </body> + </html> + + + + + + + +=item WRAPPER + +The WRAPPER option can be used to specify one or more templates which +should be used to wrap around the output of the main page template. +The main template is processed first (or any PROCESS template(s)) and +the output generated is then passed as the 'content' variable to the +WRAPPER template(s) as they are processed. + + my $template = Template->new({ + WRAPPER => 'wrapper', + }; + + # process 'foo' then wrap in 'wrapper' + $template->process('foo', { message => 'Hello World!' }); + +wrapper: + + <wrapper> + [% content %] + </wrapper> + +foo: + + This is the foo file! + Message: [% message %] + +The output generated from this example is: + + <wrapper> + This is the foo file! + Message: Hello World! + </wrapper> + +You can specify more than one WRAPPER template by setting the value to +be a reference to a list of templates. The WRAPPER templates will be +processed in reverse order with the output of each being passed to the +next (or previous, depending on how you look at it) as the 'content' +variable. It sounds complicated, but the end result is that it just +"Does The Right Thing" to make wrapper templates nest in the order you +specify. + + my $template = Template->new({ + WRAPPER => [ 'outer', 'inner' ], + }; + + # process 'foo' then wrap in 'inner', then in 'outer' + $template->process('foo', { message => 'Hello World!' }); + +outer: + + <outer> + [% content %] + </outer> + +inner: + + <inner> + [% content %] + </inner> + +The output generated is then: + + <outer> + <inner> + This is the foo file! + Message: Hello World! + </inner> + </outer> + +One side-effect of the "inside-out" processing of the WRAPPER +configuration item (and also the WRAPPER directive) is that any +variables set in the template being wrapped will be visible to the +template doing the wrapping, but not the other way around. + +You can use this to good effect in allowing page templates to set +pre-defined values which are then used in the wrapper templates. For +example, our main page template 'foo' might look like this: + +foo: + + [% page = { + title = 'Foo Page' + subtitle = 'Everything There is to Know About Foo' + author = 'Frank Oliver Octagon' + } + %] + + <p> + Welcome to the page that tells you everything about foo + blah blah blah... + </p> + +The 'foo' template is processed before the wrapper template meaning +that the 'page' data structure will be defined for use in the wrapper +template. + +wrapper: + + <html> + <head> + <title>[% page.title %]</title> + </head> + <body> + <h1>[% page.title %]</h1> + <h2>[% page.subtitle %]</h1> + <h3>by [% page.author %]</h3> + + [% content %] + </body> + </html> + +It achieves the same effect as defining META items which are then +accessed via the 'template' variable (which you are still free to +use within WRAPPER templates), but gives you more flexibility in +the type and complexity of data that you can define. + + + + + +=item ERROR + +The ERROR (or ERRORS if you prefer) configuration item can be used to +name a single template or specify a hash array mapping exception types +to templates which should be used for error handling. If an uncaught +exception is raised from within a template then the appropriate error +template will instead be processed. + +If specified as a single value then that template will be processed +for all uncaught exceptions. + + my $template = Template->new({ + ERROR => 'error.html' + }); + +If the ERROR item is a hash reference the keys are assumed to be +exception types and the relevant template for a given exception will +be selected. A 'default' template may be provided for the general +case. Note that 'ERROR' can be pluralised to 'ERRORS' if you find +it more appropriate in this case. + + my $template = Template->new({ + ERRORS => { + user => 'user/index.html', + dbi => 'error/database', + default => 'error/default', + }, + }); + +In this example, any 'user' exceptions thrown will cause the +'user/index.html' template to be processed, 'dbi' errors are handled +by 'error/database' and all others by the 'error/default' template. +Any PRE_PROCESS and/or POST_PROCESS templates will also be applied +to these error templates. + +Note that exception types are hierarchical and a 'foo' handler will +catch all 'foo.*' errors (e.g. foo.bar, foo.bar.baz) if a more +specific handler isn't defined. Be sure to quote any exception types +that contain periods to prevent Perl concatenating them into a single +string (i.e. C<user.passwd> is parsed as 'user'.'passwd'). + + my $template = Template->new({ + ERROR => { + 'user.login' => 'user/login.html', + 'user.passwd' => 'user/badpasswd.html', + 'user' => 'user/index.html', + 'default' => 'error/default', + }, + }); + +In this example, any template processed by the $template object, or +other templates or code called from within, can raise a 'user.login' +exception and have the service redirect to the 'user/login.html' +template. Similarly, a 'user.passwd' exception has a specific +handling template, 'user/badpasswd.html', while all other 'user' or +'user.*' exceptions cause a redirection to the 'user/index.html' page. +All other exception types are handled by 'error/default'. + + +Exceptions can be raised in a template using the THROW directive, + + [% THROW user.login 'no user id: please login' %] + +or by calling the throw() method on the current Template::Context object, + + $context->throw('user.passwd', 'Incorrect Password'); + $context->throw('Incorrect Password'); # type 'undef' + +or from Perl code by calling die() with a Template::Exception object, + + die (Template::Exception->new('user.denied', 'Invalid User ID')); + +or by simply calling die() with an error string. This is +automagically caught and converted to an exception of 'undef' +type which can then be handled in the usual way. + + die "I'm sorry Dave, I can't do that"; + + + + + + +=back + +=head2 Template Runtime Options + +=over 4 + + + + +=item EVAL_PERL + +This flag is used to indicate if PERL and/or RAWPERL blocks should be +evaluated. By default, it is disabled and any PERL or RAWPERL blocks +encountered will raise exceptions of type 'perl' with the message +'EVAL_PERL not set'. Note however that any RAWPERL blocks should +always contain valid Perl code, regardless of the EVAL_PERL flag. The +parser will fail to compile templates that contain invalid Perl code +in RAWPERL blocks and will throw a 'file' exception. + +When using compiled templates (see +L<COMPILE_EXT|Template::Manual::Config/Caching_and_Compiling_Options> and +L<COMPILE_DIR|Template::Manual::Config/Caching_and_Compiling_Options>), +the EVAL_PERL has an affect when the template is compiled, and again +when the templates is subsequently processed, possibly in a different +context to the one that compiled it. + +If the EVAL_PERL is set when a template is compiled, then all PERL and +RAWPERL blocks will be included in the compiled template. If the +EVAL_PERL option isn't set, then Perl code will be generated which +B<always> throws a 'perl' exception with the message 'EVAL_PERL not +set' B<whenever> the compiled template code is run. + +Thus, you must have EVAL_PERL set if you want your compiled templates +to include PERL and RAWPERL blocks. + +At some point in the future, using a different invocation of the +Template Toolkit, you may come to process such a pre-compiled +template. Assuming the EVAL_PERL option was set at the time the +template was compiled, then the output of any RAWPERL blocks will be +included in the compiled template and will get executed when the +template is processed. This will happen regardless of the runtime +EVAL_PERL status. + +Regular PERL blocks are a little more cautious, however. If the +EVAL_PERL flag isn't set for the I<current> context, that is, the +one which is trying to process it, then it will throw the familiar 'perl' +exception with the message, 'EVAL_PERL not set'. + +Thus you can compile templates to include PERL blocks, but optionally +disable them when you process them later. Note however that it is +possible for a PERL block to contain a Perl "BEGIN { # some code }" +block which will always get run regardless of the runtime EVAL_PERL +status. Thus, if you set EVAL_PERL when compiling templates, it is +assumed that you trust the templates to Do The Right Thing. Otherwise +you must accept the fact that there's no bulletproof way to prevent +any included code from trampling around in the living room of the +runtime environment, making a real nuisance of itself if it really +wants to. If you don't like the idea of such uninvited guests causing +a bother, then you can accept the default and keep EVAL_PERL disabled. + + + + + + + +=item OUTPUT + +Default output location or handler. This may be specified as one of: +a file name (relative to OUTPUT_PATH, if defined, or the current +working directory if not specified absolutely); a file handle +(e.g. GLOB or IO::Handle) opened for writing; a reference to a text +string to which the output is appended (the string isn't cleared); a +reference to a subroutine which is called, passing the output text as +an argument; as a reference to an array, onto which the content will be +push()ed; or as a reference to any object that supports the print() +method. This latter option includes the Apache::Request object which +is passed as the argument to Apache/mod_perl handlers. + +example 1 (file name): + + my $template = Template->new({ + OUTPUT => "/tmp/foo", + }); + +example 2 (text string): + + my $output = ''; + + my $template = Template->new({ + OUTPUT => \$output, + }); + +example 3 (file handle): + + open (TOUT, "> $file") || die "$file: $!\n"; + + my $template = Template->new({ + OUTPUT => \*TOUT, + }); + +example 4 (subroutine): + + sub output { my $out = shift; print "OUTPUT: $out" } + + my $template = Template->new({ + OUTPUT => \&output, + }); + +example 5 (array reference): + + my $template = Template->new({ + OUTPUT => \@output, + }) + +example 6 (Apache/mod_perl handler): + + sub handler { + my $r = shift; + + my $t = Template->new({ + OUTPUT => $r, + }); + ... + } + +The default OUTPUT location be overridden by passing a third parameter +to the Template process() method. This can be specified as any of the +above argument types. + + $t->process($file, $vars, "/tmp/foo"); + $t->process($file, $vars, "bar"); + $t->process($file, $vars, \*MYGLOB); + $t->process($file, $vars, \@output); + $t->process($file, $vars, $r); # Apache::Request + ... + + + + + + + + +=item OUTPUT_PATH + +The OUTPUT_PATH allows a directory to be specified into which output +files should be written. An output file can be specified by the +OUTPUT option, or passed by name as the third parameter to the +Template process() method. + + my $template = Template->new({ + INCLUDE_PATH => "/tmp/src", + OUTPUT_PATH => "/tmp/dest", + }); + + my $vars = { + ... + }; + + foreach my $file ('foo.html', 'bar.html') { + $template->process($file, $vars, $file) + || die $template->error(); + } + +This example will read the input files '/tmp/src/foo.html' and +'/tmp/src/bar.html' and write the processed output to '/tmp/dest/foo.html' +and '/tmp/dest/bar.html', respectively. + + + + + + + + +=item DEBUG + +The DEBUG option can be used to enable debugging within the various +different modules that comprise the Template Toolkit. The +L<Template::Constants|Template::Constants> module defines a set of +DEBUG_XXXX constants which can be combined using the logical OR +operator, '|'. + + use Template::Constants qw( :debug ); + + my $template = Template->new({ + DEBUG => DEBUG_PARSER | DEBUG_PROVIDER, + }); + +For convenience, you can also provide a string containing a list +of lower case debug options, separated by any non-word characters. + + my $template = Template->new({ + DEBUG => 'parser, provider', + }); + +The following DEBUG_XXXX flags can be used: + +=over 4 + +=item DEBUG_SERVICE + +Enables general debugging messages for the +L<Template::Service|Template::Service> module. + +=item DEBUG_CONTEXT + +Enables general debugging messages for the +L<Template::Context|Template::Context> module. + +=item DEBUG_PROVIDER + +Enables general debugging messages for the +L<Template::Provider|Template::Provider> module. + +=item DEBUG_PLUGINS + +Enables general debugging messages for the +L<Template::Plugins|Template::Plugins> module. + +=item DEBUG_FILTERS + +Enables general debugging messages for the +L<Template::Filters|Template::Filters> module. + +=item DEBUG_PARSER + +This flag causes the L<Template::Parser|Template::Parser> to generate +debugging messages that show the Perl code generated by parsing and +compiling each template. + +=item DEBUG_UNDEF + +This option causes the Template Toolkit to throw an 'undef' error +whenever it encounters an undefined variable value. + +=item DEBUG_DIRS + +This option causes the Template Toolkit to generate comments +indicating the source file, line and original text of each directive +in the template. These comments are embedded in the template output +using the format defined in the DEBUG_FORMAT configuration item, or a +simple default format if unspecified. + +For example, the following template fragment: + + + Hello World + +would generate this output: + + ## input text line 1 : ## + Hello + ## input text line 2 : World ## + World + +=item DEBUG_ALL + +Enables all debugging messages. + +=item DEBUG_CALLER + +This option causes all debug messages that aren't newline terminated +to have the file name and line number of the caller appended to them. + + +=back + +=item DEBUG_FORMAT + +The DEBUG_FORMAT option can be used to specify a format string for the +debugging messages generated via the DEBUG_DIRS option described +above. Any occurances of C<$file>, C<$line> or C<$text> will be +replaced with the current file name, line or directive text, +respectively. Notice how the format is single quoted to prevent Perl +from interpolating those tokens as variables. + + my $template = Template->new({ + DEBUG => 'dirs', + DEBUG_FORMAT => '<!-- $file line $line : [% $text %] -->', + }); + +The following template fragment: + + [% foo = 'World' %] + Hello [% foo %] + +would then generate this output: + + <!-- input text line 2 : [% foo = 'World' %] --> + Hello <!-- input text line 3 : [% foo %] -->World + +The DEBUG directive can also be used to set a debug format within +a template. + + [% DEBUG format '<!-- $file line $line : [% $text %] -->' %] + + +=back + +=head2 Caching and Compiling Options + +=over 4 + + + +=item CACHE_SIZE + +The Template::Provider module caches compiled templates to avoid the need +to re-parse template files or blocks each time they are used. The CACHE_SIZE +option is used to limit the number of compiled templates that the module +should cache. + +By default, the CACHE_SIZE is undefined and all compiled templates are +cached. When set to any positive value, the cache will be limited to +storing no more than that number of compiled templates. When a new +template is loaded and compiled and the cache is full (i.e. the number +of entries == CACHE_SIZE), the least recently used compiled template +is discarded to make room for the new one. + +The CACHE_SIZE can be set to 0 to disable caching altogether. + + my $template = Template->new({ + CACHE_SIZE => 64, # only cache 64 compiled templates + }); + + my $template = Template->new({ + CACHE_SIZE => 0, # don't cache any compiled templates + }); + + + + + + +=item COMPILE_EXT + +From version 2 onwards, the Template Toolkit has the ability to +compile templates to Perl code and save them to disk for subsequent +use (i.e. cache persistence). The COMPILE_EXT option may be +provided to specify a filename extension for compiled template files. +It is undefined by default and no attempt will be made to read or write +any compiled template files. + + my $template = Template->new({ + COMPILE_EXT => '.ttc', + }); + +If COMPILE_EXT is defined (and COMPILE_DIR isn't, see below) then compiled +template files with the COMPILE_EXT extension will be written to the same +directory from which the source template files were loaded. + +Compiling and subsequent reuse of templates happens automatically +whenever the COMPILE_EXT or COMPILE_DIR options are set. The Template +Toolkit will automatically reload and reuse compiled files when it +finds them on disk. If the corresponding source file has been modified +since the compiled version as written, then it will load and re-compile +the source and write a new compiled version to disk. + +This form of cache persistence offers significant benefits in terms of +time and resources required to reload templates. Compiled templates can +be reloaded by a simple call to Perl's require(), leaving Perl to handle +all the parsing and compilation. This is a Good Thing. + +=item COMPILE_DIR + +The COMPILE_DIR option is used to specify an alternate directory root +under which compiled template files should be saved. + + my $template = Template->new({ + COMPILE_DIR => '/tmp/ttc', + }); + +The COMPILE_EXT option may also be specified to have a consistent file +extension added to these files. + + my $template1 = Template->new({ + COMPILE_DIR => '/tmp/ttc', + COMPILE_EXT => '.ttc1', + }); + + my $template2 = Template->new({ + COMPILE_DIR => '/tmp/ttc', + COMPILE_EXT => '.ttc2', + }); + + +When COMPILE_EXT is undefined, the compiled template files have the +same name as the original template files, but reside in a different +directory tree. + +Each directory in the INCLUDE_PATH is replicated in full beneath the +COMPILE_DIR directory. This example: + + my $template = Template->new({ + COMPILE_DIR => '/tmp/ttc', + INCLUDE_PATH => '/home/abw/templates:/usr/share/templates', + }); + +would create the following directory structure: + + /tmp/ttc/home/abw/templates/ + /tmp/ttc/usr/share/templates/ + +Files loaded from different INCLUDE_PATH directories will have their +compiled forms save in the relevant COMPILE_DIR directory. + +On Win32 platforms a filename may by prefixed by a drive letter and +colon. e.g. + + C:/My Templates/header + +The colon will be silently stripped from the filename when it is added +to the COMPILE_DIR value(s) to prevent illegal filename being generated. +Any colon in COMPILE_DIR elements will be left intact. For example: + + # Win32 only + my $template = Template->new({ + DELIMITER => ';', + COMPILE_DIR => 'C:/TT2/Cache', + INCLUDE_PATH => 'C:/TT2/Templates;D:/My Templates', + }); + +This would create the following cache directories: + + C:/TT2/Cache/C/TT2/Templates + C:/TT2/Cache/D/My Templates + + +=back + +=head2 Plugins and Filters + +=over 4 + + + +=item PLUGINS + +The PLUGINS options can be used to provide a reference to a hash array +that maps plugin names to Perl module names. A number of standard +plugins are defined (e.g. 'table', 'cgi', 'dbi', etc.) which map to +their corresponding Template::Plugin::* counterparts. These can be +redefined by values in the PLUGINS hash. + + my $template = Template->new({ + PLUGINS => { + cgi => 'MyOrg::Template::Plugin::CGI', + foo => 'MyOrg::Template::Plugin::Foo', + bar => 'MyOrg::Template::Plugin::Bar', + }, + }); + +The USE directive is used to create plugin objects and does so by +calling the plugin() method on the current Template::Context object. +If the plugin name is defined in the PLUGINS hash then the +corresponding Perl module is loaded via require(). The context then +calls the load() class method which should return the class name +(default and general case) or a prototype object against which the +new() method can be called to instantiate individual plugin objects. + +If the plugin name is not defined in the PLUGINS hash then the PLUGIN_BASE +and/or LOAD_PERL options come into effect. + + + + + +=item PLUGIN_BASE + +If a plugin is not defined in the PLUGINS hash then the PLUGIN_BASE is used +to attempt to construct a correct Perl module name which can be successfully +loaded. + +The PLUGIN_BASE can be specified as a single value or as a reference +to an array of multiple values. The default PLUGIN_BASE value, +'Template::Plugin', is always added the the end of the PLUGIN_BASE +list (a single value is first converted to a list). Each value should +contain a Perl package name to which the requested plugin name is +appended. + +example 1: + + my $template = Template->new({ + PLUGIN_BASE => 'MyOrg::Template::Plugin', + }); + + [% USE Foo %] # => MyOrg::Template::Plugin::Foo + or Template::Plugin::Foo + +example 2: + + my $template = Template->new({ + PLUGIN_BASE => [ 'MyOrg::Template::Plugin', + 'YourOrg::Template::Plugin' ], + }); + + [% USE Foo %] # => MyOrg::Template::Plugin::Foo + or YourOrg::Template::Plugin::Foo + or Template::Plugin::Foo + + + + + + +=item LOAD_PERL + +If a plugin cannot be loaded using the PLUGINS or PLUGIN_BASE +approaches then the provider can make a final attempt to load the +module without prepending any prefix to the module path. This allows +regular Perl modules (i.e. those that don't reside in the +Template::Plugin or some other such namespace) to be loaded and used +as plugins. + +By default, the LOAD_PERL option is set to 0 and no attempt will be made +to load any Perl modules that aren't named explicitly in the PLUGINS +hash or reside in a package as named by one of the PLUGIN_BASE +components. + +Plugins loaded using the PLUGINS or PLUGIN_BASE receive a reference to +the current context object as the first argument to the new() +constructor. Modules loaded using LOAD_PERL are assumed to not +conform to the plugin interface. They must provide a new() class +method for instantiating objects but it will not receive a reference +to the context as the first argument. Plugin modules should provide a +load() class method (or inherit the default one from the +Template::Plugin base class) which is called the first time the plugin +is loaded. Regular Perl modules need not. In all other respects, +regular Perl objects and Template Toolkit plugins are identical. + +If a particular Perl module does not conform to the common, but not +unilateral, new() constructor convention then a simple plugin wrapper +can be written to interface to it. + + + + +=item FILTERS + +The FILTERS option can be used to specify custom filters which can +then be used with the FILTER directive like any other. These are +added to the standard filters which are available by default. Filters +specified via this option will mask any standard filters of the same +name. + +The FILTERS option should be specified as a reference to a hash array +in which each key represents the name of a filter. The corresponding +value should contain a reference to an array containing a subroutine +reference and a flag which indicates if the filter is static (0) or +dynamic (1). A filter may also be specified as a solitary subroutine +reference and is assumed to be static. + + $template = Template->new({ + FILTERS => { + 'sfilt1' => \&static_filter, # static + 'sfilt2' => [ \&static_filter, 0 ], # same as above + 'dfilt1' => [ \&dyanamic_filter_factory, 1 ], + }, + }); + +Additional filters can be specified at any time by calling the +define_filter() method on the current Template::Context object. +The method accepts a filter name, a reference to a filter +subroutine and an optional flag to indicate if the filter is +dynamic. + + my $context = $template->context(); + $context->define_filter('new_html', \&new_html); + $context->define_filter('new_repeat', \&new_repeat, 1); + +Static filters are those where a single subroutine reference is used +for all invocations of a particular filter. Filters that don't accept +any configuration parameters (e.g. 'html') can be implemented +statically. The subroutine reference is simply returned when that +particular filter is requested. The subroutine is called to filter +the output of a template block which is passed as the only argument. +The subroutine should return the modified text. + + sub static_filter { + my $text = shift; + # do something to modify $text... + return $text; + } + +The following template fragment: + + [% FILTER sfilt1 %] + Blah blah blah. + [% END %] + +is approximately equivalent to: + + &static_filter("\nBlah blah blah.\n"); + +Filters that can accept parameters (e.g. 'truncate') should be +implemented dynamically. In this case, the subroutine is taken to be +a filter 'factory' that is called to create a unique filter subroutine +each time one is requested. A reference to the current +Template::Context object is passed as the first parameter, followed by +any additional parameters specified. The subroutine should return +another subroutine reference (usually a closure) which implements the +filter. + + sub dynamic_filter_factory { + my ($context, @args) = @_; + + return sub { + my $text = shift; + # do something to modify $text... + return $text; + } + } + +The following template fragment: + + [% FILTER dfilt1(123, 456) %] + Blah blah blah + [% END %] + +is approximately equivalent to: + + my $filter = &dynamic_filter_factory($context, 123, 456); + &$filter("\nBlah blah blah.\n"); + +See the FILTER directive for further examples. + + +=back + +=head2 Compatibility, Customisation and Extension + +=over 4 + + + +=item V1DOLLAR + +In version 1 of the Template Toolkit, an optional leading '$' could be placed +on any template variable and would be silently ignored. + + # VERSION 1 + [% $foo %] === [% foo %] + [% $hash.$key %] === [% hash.key %] + +To interpolate a variable value the '${' ... '}' construct was used. +Typically, one would do this to index into a hash array when the key +value was stored in a variable. + +example: + + my $vars = { + users => { + aba => { name => 'Alan Aardvark', ... }, + abw => { name => 'Andy Wardley', ... }, + ... + }, + uid => 'aba', + ... + }; + + $template->process('user/home.html', $vars) + || die $template->error(), "\n"; + +'user/home.html': + + [% user = users.${uid} %] # users.aba + Name: [% user.name %] # Alan Aardvark + +This was inconsistent with double quoted strings and also the +INTERPOLATE mode, where a leading '$' in text was enough to indicate a +variable for interpolation, and the additional curly braces were used +to delimit variable names where necessary. Note that this use is +consistent with UNIX and Perl conventions, among others. + + # double quoted string interpolation + [% name = "$title ${user.name}" %] + + # INTERPOLATE = 1 + <img src="$images/help.gif"></a> + <img src="$images/${icon.next}.gif"> + +For version 2, these inconsistencies have been removed and the syntax +clarified. A leading '$' on a variable is now used exclusively to +indicate that the variable name should be interpolated +(e.g. subsituted for its value) before being used. The earlier example +from version 1: + + # VERSION 1 + [% user = users.${uid} %] + Name: [% user.name %] + +can now be simplified in version 2 as: + + # VERSION 2 + [% user = users.$uid %] + Name: [% user.name %] + +The leading dollar is no longer ignored and has the same effect of +interpolation as '${' ... '}' in version 1. The curly braces may +still be used to explicitly scope the interpolated variable name +where necessary. + +e.g. + + [% user = users.${me.id} %] + Name: [% user.name %] + +The rule applies for all variables, both within directives and in +plain text if processed with the INTERPOLATE option. This means that +you should no longer (if you ever did) add a leading '$' to a variable +inside a directive, unless you explicitly want it to be interpolated. + +One obvious side-effect is that any version 1 templates with variables +using a leading '$' will no longer be processed as expected. Given +the following variable definitions, + + [% foo = 'bar' + bar = 'baz' + %] + +version 1 would interpret the following as: + + # VERSION 1 + [% $foo %] => [% GET foo %] => bar + +whereas version 2 interprets it as: + + # VERSION 2 + [% $foo %] => [% GET $foo %] => [% GET bar %] => baz + +In version 1, the '$' is ignored and the value for the variable 'foo' is +retrieved and printed. In version 2, the variable '$foo' is first interpolated +to give the variable name 'bar' whose value is then retrieved and printed. + +The use of the optional '$' has never been strongly recommended, but +to assist in backwards compatibility with any version 1 templates that +may rely on this "feature", the V1DOLLAR option can be set to 1 +(default: 0) to revert the behaviour and have leading '$' characters +ignored. + + my $template = Template->new({ + V1DOLLAR => 1, + }); + + + + +=item LOAD_TEMPLATES + +The LOAD_TEMPLATE option can be used to provide a reference to a list +of Template::Provider objects or sub-classes thereof which will take +responsibility for loading and compiling templates. + + my $template = Template->new({ + LOAD_TEMPLATES => [ + MyOrg::Template::Provider->new({ ... }), + Template::Provider->new({ ... }), + ], + }); + +When a PROCESS, INCLUDE or WRAPPER directive is encountered, the named +template may refer to a locally defined BLOCK or a file relative to +the INCLUDE_PATH (or an absolute or relative path if the appropriate +ABSOLUTE or RELATIVE options are set). If a BLOCK definition can't be +found (see the Template::Context template() method for a discussion of +BLOCK locality) then each of the LOAD_TEMPLATES provider objects is +queried in turn via the fetch() method to see if it can supply the +required template. Each provider can return a compiled template, an +error, or decline to service the request in which case the +responsibility is passed to the next provider. If none of the +providers can service the request then a 'not found' error is +returned. The same basic provider mechanism is also used for the +INSERT directive but it bypasses any BLOCK definitions and doesn't +attempt is to parse or process the contents of the template file. + +This is an implementation of the 'Chain of Responsibility' +design pattern as described in +"Design Patterns", Erich Gamma, Richard Helm, Ralph Johnson, John +Vlissides), Addision-Wesley, ISBN 0-201-63361-2, page 223 +. + +If LOAD_TEMPLATES is undefined, a single default provider will be +instantiated using the current configuration parameters. For example, +the Template::Provider INCLUDE_PATH option can be specified in the Template configuration and will be correctly passed to the provider's +constructor method. + + my $template = Template->new({ + INCLUDE_PATH => '/here:/there', + }); + + + + + +=item LOAD_PLUGINS + +The LOAD_PLUGINS options can be used to specify a list of provider +objects (i.e. they implement the fetch() method) which are responsible +for loading and instantiating template plugin objects. The +Template::Content plugin() method queries each provider in turn in a +"Chain of Responsibility" as per the template() and filter() methods. + + my $template = Template->new({ + LOAD_PLUGINS => [ + MyOrg::Template::Plugins->new({ ... }), + Template::Plugins->new({ ... }), + ], + }); + +By default, a single Template::Plugins object is created using the +current configuration hash. Configuration items destined for the +Template::Plugins constructor may be added to the Template +constructor. + + my $template = Template->new({ + PLUGIN_BASE => 'MyOrg::Template::Plugins', + LOAD_PERL => 1, + }); + + + + + +=item LOAD_FILTERS + +The LOAD_FILTERS option can be used to specify a list of provider +objects (i.e. they implement the fetch() method) which are responsible +for returning and/or creating filter subroutines. The +Template::Context filter() method queries each provider in turn in a +"Chain of Responsibility" as per the template() and plugin() methods. + + my $template = Template->new({ + LOAD_FILTERS => [ + MyTemplate::Filters->new(), + Template::Filters->new(), + ], + }); + +By default, a single Template::Filters object is created for the +LOAD_FILTERS list. + + + + + +=item TOLERANT + +The TOLERANT flag is used by the various Template Toolkit provider +modules (Template::Provider, Template::Plugins, Template::Filters) to +control their behaviour when errors are encountered. By default, any +errors are reported as such, with the request for the particular +resource (template, plugin, filter) being denied and an exception +raised. When the TOLERANT flag is set to any true values, errors will +be silently ignored and the provider will instead return +STATUS_DECLINED. This allows a subsequent provider to take +responsibility for providing the resource, rather than failing the +request outright. If all providers decline to service the request, +either through tolerated failure or a genuine disinclination to +comply, then a 'E<lt>resourceE<gt> not found' exception is raised. + + + + + + +=item SERVICE + +A reference to a Template::Service object, or sub-class thereof, to which +the Template module should delegate. If unspecified, a Template::Service +object is automatically created using the current configuration hash. + + my $template = Template->new({ + SERVICE => MyOrg::Template::Service->new({ ... }), + }); + + + + + +=item CONTEXT + +A reference to a Template::Context object which is used to define a +specific environment in which template are processed. A Template::Context +object is passed as the only parameter to the Perl subroutines that +represent "compiled" template documents. Template subroutines make +callbacks into the context object to access Template Toolkit functionality, +for example, to to INCLUDE or PROCESS another template (include() and +process() methods, respectively), to USE a plugin (plugin()) or +instantiate a filter (filter()) or to access the stash (stash()) which +manages variable definitions via the get() and set() methods. + + my $template = Template->new({ + CONTEXT => MyOrg::Template::Context->new({ ... }), + }); + + + +=item STASH + +A reference to a Template::Stash object or sub-class which will take +responsibility for managing template variables. + + my $stash = MyOrg::Template::Stash->new({ ... }); + my $template = Template->new({ + STASH => $stash, + }); + +If unspecified, a default stash object is created using the VARIABLES +configuration item to initialise the stash variables. These may also +be specified as the PRE_DEFINE option for backwards compatibility with +version 1. + + my $template = Template->new({ + VARIABLES => { + id => 'abw', + name => 'Andy Wardley', + }, + }; + + + + + +=item PARSER + +The Template::Parser module implements a parser object for compiling +templates into Perl code which can then be executed. A default object +of this class is created automatically and then used by the +Template::Provider whenever a template is loaded and requires +compilation. The PARSER option can be used to provide a reference to +an alternate parser object. + + my $template = Template->new({ + PARSER => MyOrg::Template::Parser->new({ ... }), + }); + + + + + +=item GRAMMAR + +The GRAMMAR configuration item can be used to specify an alternate +grammar for the parser. This allows a modified or entirely new +template language to be constructed and used by the Template Toolkit. + +Source templates are compiled to Perl code by the Template::Parser +using the Template::Grammar (by default) to define the language +structure and semantics. Compiled templates are thus inherently +"compatible" with each other and there is nothing to prevent any +number of different template languages being compiled and used within +the same Template Toolkit processing environment (other than the usual +time and memory constraints). + +The Template::Grammar file is constructed from a YACC like grammar +(using Parse::YAPP) and a skeleton module template. These files are +provided, along with a small script to rebuild the grammar, in the +'parser' sub-directory of the distribution. You don't have to know or +worry about these unless you want to hack on the template language or +define your own variant. There is a README file in the same directory +which provides some small guidance but it is assumed that you know +what you're doing if you venture herein. If you grok LALR parsers, +then you should find it comfortably familiar. + +By default, an instance of the default Template::Grammar will be +created and used automatically if a GRAMMAR item isn't specified. + + use MyOrg::Template::Grammar; + + my $template = Template->new({ + GRAMMAR = MyOrg::Template::Grammar->new(); + }); + + + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Credits.pod b/lib/Template/Manual/Credits.pod new file mode 100644 index 0000000..64999ac --- /dev/null +++ b/lib/Template/Manual/Credits.pod @@ -0,0 +1,188 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Credits +# +# DESCRIPTION +# This section provides a brief history of the Template Toolkit and +# details the primary author and numerous other people who have +# contributed to it. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Credits - Author and contributor credits + +=head1 DESCRIPTION + +This section provides a brief history of the Template Toolkit and +details the primary author and numerous other people who have +contributed to it. + +=head1 HISTORY + +The Template Toolkit began its life as the Text::MetaText module, +originally released to CPAN around 1996. This itself was the public +manifestation of an earlier template processing system I developed +while working at Peritas (now Knowledge Pool - + http://www.knowledgepool.com/) + +Text::MetaText was the prototype - the one we always planned to throw +away. It did the job well, showing us what worked and what didn't, what +was good and what was bad, and gave us some ideas about what could be +done better, given the chance to start again from scratch. + +Some time late in 1998 I threw away the prototype and started work on +the Template Toolkit. By then I was working at Canon Research Centre +Europe Ltd. (http://www.cre.canon.co.uk), involved in a general +research programme related to web publishing and dynamic content +generation. The first alpha release was in June 1999, followed by +numerous more alpha and beta releases culminating in 1.00 being +released on 2nd December 1999. + +A month or so later, work had begun on version 2.00. The plan was to +get the template language relatively stable in version 1.00 and not +worry too much about performance or other internal matters. Then, +version 2.00 would follow to improve performance, clean up the +architecture and fix anything that, with the benefit of hindsight, we +thought could be improved. As it happens, me starting work on version +2.00 coincided with Doug Steinwand sending me his parser variant which +compiled templates to Perl code, giving a major performance boost. +As well as the speedups, there are a whole host of significant new +features in version 2.00, and a greatly improved internal architecture. +Apart from a few minor "fixups" the template directives and language +have remained the same as in version 1.00 + +Version 2.00 was available in beta release form in July 2000, just +in time for the 4th Perl Conference where version 1.00 was awarded +"Best New Perl Module". After another extended beta release period, +version 2.00 was released on 1st December 2000. + + + + +=head1 CONTRIBUTORS + +Many people have contributed ideas, inspiration, fixes and features to +the Template Toolkit. Their efforts continue to be very much appreciated. +Please let me know if you think anyone is missing from this list. + + Chuck Adams <scrytch@uswest.net> + Stephen Adkins <stephen.adkins@officevision.com> + Ivan Adzhubey <iadzhubey@rics.bwh.harvard.edu> + Mark Anderson <mda@discerning.com> + Bradley Baetz <bbaetz@student.usyd.edu.au> + Thierry-Michel Barral <kktos@electron-libre.com> + Craig Barratt <craig@arraycomm.com> + Stas Bekman <stas@stason.org> + Tony Bowden <tony-tt@kasei.com> + Neil Bowers <neilb@cre.canon.co.uk> + Leon Brocard <acme@astray.com> + Lyle Brooks <brooks@deseret.com> + Dave Cash <dave@gnofn.org> + Piers Cawley <pdcawley@bofh.org.uk> + Darren Chamberlain <dlc@users.sourceforge.net> + Eric Cholet <cholet@logilune.com> + Dave Cross <dave@dave.org.uk> + Chris Dean <ctdean@babycenter.com> + Francois Desarmenien <desar@club-internet.fr> + Horst Dumcke <hdumcke@cisco.com> + Mark Fowler <mark@indicosoftware.com> + Michael Fowler <michael@shoebox.net> + Axel Gerstmair <anthill@web.de> + Dylan William Hardison <dylanwh@tampabay.rr.com> + Perrin Harkins <pharkins@etoys.com> + Bryce Harrington <bryce@osdl.org> + Dave Hodgkinson <daveh@davehodgkinson.com> + Harald Joerg <Harald.Joerg@fujitsu-siemens.com> + Colin Johnson <colin@knowledgepool.com> + Vivek Khera <khera@kciLink.com> + Rafael Kitover <caelum@debian.org> + Ivan Kurmanov <http://www.ahinea.com> + Hans von Lengerke <hans@lengerke.org> + Jonas Liljegren <jonas@paranormal.se> + Simon Luff <simon@sports.com> + Paul Makepeace <Paul.Makepeace@realprogrammers.com> + Gervase Markham <gerv@mozilla.org> + Simon Matthews <sam@knowledgepool.com> + Robert McArthur <mcarthur@dstc.edu.au> + Craig McLane <mclanec@oxy.edu> + Leslie Michael Orchard <deus_x@ninjacode.com> + Eugene Miretskiy <eugene@invision.net> + Tatsuhiko Miyagawa <miyagawa@edge.co.jp> + Keith G. Murphy <keithmur@mindspring.com> + Chris Nandor <pudge@pobox.com> + Briac Pilpré <briac@pilpre.com> + Martin Portman <mrp@cre.canon.co.uk> + Slaven Rezic <slaven.rezic@berlin.de> + Christian Schaffner <schaffner@eeh.ee.ethz.ch> + Randal L. Schwartz <merlyn@stonehenge.com> + Paul Sharpe <paul@miraclefish.com> + Ville Skyttä <ville.skytta@iki.fi> + Doug Steinwand <dsteinwand@etoys.com> + Michael Stevens <michael@etla.org> + Drew Taylor <dtaylor@vialogix.com> + Swen Thuemmler <Swen.Thuemmler@paderlinx.de> + Richard Tietjen <Richard_Tietjen@mcgraw-hill.com> + Stathy G. Touloumis <stathy.touloumis@edventions.com> + Jim Vaughan <jim@mrjim.com> + Simon Wilcox <simonw@simonwilcox.co.uk> + Chris Winters <cwinters@intes.net> + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Directives.pod b/lib/Template/Manual/Directives.pod new file mode 100644 index 0000000..3b8af3e --- /dev/null +++ b/lib/Template/Manual/Directives.pod @@ -0,0 +1,2179 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Directives +# +# DESCRIPTION +# This section provides a reference of all Template Toolkit +# directives, complete with examples of use. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Directives - Template directives + +=head1 DESCRIPTION + +This section provides a reference of all Template Toolkit directives, +complete with examples of use. + +=head2 Accessing and Updating Template Variables + +=over 4 + + +=item GET + +The GET directive retrieves and outputs the value of the named variable. + + [% GET foo %] + +The GET keyword is optional. A variable can be specified in a directive +tag by itself. + + [% foo %] + +The variable can have an unlimited number of elements, each separated +by a dot '.'. Each element can have arguments specified within +parentheses. + + [% foo %] + [% bar.baz %] + [% biz.baz(10) %] + ...etc... + +See L<Template::Manual::Variables> for a full discussion on template +variables. + +You can also specify expressions using the logical (and, or, not, ?:) and +mathematic operators (+ - * / % mod div). + + [% template.title or default.title %] + + [% score * 100 %] + + [% order.nitems ? checkout(order.total) : 'no items' %] + +The 'div' operator returns the integer result of division. Both '%' and +'mod' return the modulus (i.e. remainder) of division. 'mod' is provided +as an alias for '%' for backwards compatibility with version 1. + + [% 15 / 6 %] # 2.5 + [% 15 div 6 %] # 2 + [% 15 mod 6 %] # 3 + + + +=item CALL + +The CALL directive is similar to GET in evaluating the variable named, +but doesn't print the result returned. This can be useful when a +variable is bound to a sub-routine or object method which you want to +call but aren't interested in the value returned. + + [% CALL dbi.disconnect %] + + [% CALL inc_page_counter(page_count) %] + + + + +=item SET + +The SET directive allows you to assign new values to existing variables +or create new temporary variables. + + [% SET title = 'Hello World' %] + +The SET keyword is also optional. + + [% title = 'Hello World' %] + +Variables may be assigned the values of other variables, unquoted +numbers (digits), literal text ('single quotes') or quoted text +("double quotes"). In the latter case, any variable references within +the text will be interpolated when the string is evaluated. Variables +should be prefixed by '$', using curly braces to explicitly scope +the variable name where necessary. + + [% foo = 'Foo' %] # literal value 'Foo' + [% bar = foo %] # value of variable 'foo' + [% cost = '$100' %] # literal value '$100' + [% item = "$bar: ${cost}.00" %] # value "Foo: $100.00" + +Multiple variables may be assigned in the same directive and are +evaluated in the order specified. Thus, the above could have been +written: + + [% foo = 'Foo' + bar = foo + cost = '$100' + item = "$bar: ${cost}.00" + %] + +Simple expressions can also be used, as per GET. + + [% ten = 10 + twenty = 20 + thirty = twenty + ten + forty = 2 * twenty + fifty = 100 div 2 + six = twenty mod 7 + %] + +You can concatenate strings together using the ' _ ' operator. In Perl 5, +the '.' is used for string concatenation, but in Perl 6, as in the Template +Toolkit, the '.' will be used as the method calling operator and ' _ ' will +be used for string concatenation. Note that the operator must be +specified with surrounding whitespace which, as Larry says, is construed as +a feature: + + [% copyright = '(C) Copyright' _ year _ ' ' _ author %] + +You can, of course, achieve a similar effect with double quoted string +interpolation. + + [% copyright = "(C) Copyright $year $author" %] + + + + + +=item DEFAULT + +The DEFAULT directive is similar to SET but only updates variables +that are currently undefined or have no "true" value (in the Perl +sense). + + [% DEFAULT + name = 'John Doe' + id = 'jdoe' + %] + +This can be particularly useful in common template components to +ensure that some sensible default are provided for otherwise +undefined variables. + + [% DEFAULT + title = 'Hello World' + bgcol = '#ffffff' + %] + <html> + <head> + <title>[% title %]</title> + </head> + + <body bgcolor="[% bgcol %]"> + + +=back + +=head2 Processing Other Template Files and Blocks + +=over 4 + + +=item INSERT + +The INSERT directive is used to insert the contents of an external file +at the current position. + + [% INSERT myfile %] + +No attempt to parse or process the file is made. The contents, +possibly including any embedded template directives, are inserted +intact. + +The filename specified should be relative to one of the INCLUDE_PATH +directories. Absolute (i.e. starting with C</>) and relative +(i.e. starting with C<.>) filenames may be used if the ABSOLUTE and +RELATIVE options are set, respectively. Both these options are +disabled by default. + + my $template = Template->new({ + INCLUDE_PATH => '/here:/there', + }); + + $template->process('myfile'); + +'myfile': + + [% INSERT foo %] # looks for /here/foo then /there/foo + [% INSERT /etc/passwd %] # file error: ABSOLUTE not set + [% INSERT ../secret %] # file error: RELATIVE not set + +For convenience, the filename does not need to be quoted as long as it +contains only alphanumeric characters, underscores, dots or forward +slashes. Names containing any other characters should be quoted. + + [% INSERT misc/legalese.txt %] + [% INSERT 'dos98/Program Files/stupid' %] + +To evaluate a variable to specify a filename, you should explicitly +prefix it with a '$' or use double-quoted string interpolation. + + [% language = 'en' + legalese = 'misc/legalese.txt' + %] + + [% INSERT $legalese %] # 'misc/legalese.txt' + [% INSERT "$language/$legalese" %] # 'en/misc/legalese.txt' + +Multiple files can be specified using '+' as a delimiter. All files +should be unquoted names or quoted strings. Any variables should be +interpolated into double-quoted strings. + + [% INSERT legalese.txt + warning.txt %] + [% INSERT "$legalese" + warning.txt %] # requires quoting + + + + + + + + + + + + + + + + + +=item INCLUDE + +The INCLUDE directive is used to process and include the output of +another template file or block. + + [% INCLUDE header %] + +If a BLOCK of the specified name is defined in the same file, or in a file +from which the current template has been called (i.e. a parent template) +then it will be used in preference to any file of the same name. + + [% INCLUDE table %] # uses BLOCK defined below + + [% BLOCK table %] + <table> + ... + </table> + [% END %] + +If a BLOCK definition is not currently visible then the template name +should be a file relative to one of the INCLUDE_PATH directories, or +an absolute or relative file name if the ABSOLUTE/RELATIVE options are +appropriately enabled. The INCLUDE directive automatically quotes the +filename specified, as per INSERT described above. When a variable +contains the name of the template for the INCLUDE directive, it should +be explicitly prefixed by '$' or double-quoted + + [% myheader = 'my/misc/header' %] + [% INCLUDE myheader %] # 'myheader' + [% INCLUDE $myheader %] # 'my/misc/header' + [% INCLUDE "$myheader" %] # 'my/misc/header' + +Any template directives embedded within the file will be processed +accordingly. All variables currently defined will be visible and +accessible from within the included template. + + [% title = 'Hello World' %] + [% INCLUDE header %] + <body> + ... + +'header': + + <html> + <title>[% title %]</title> + +output: + + <html> + <title>Hello World</title> + <body> + ... + +Local variable definitions may be specified after the template name, +temporarily masking any existing variables. Insignificant whitespace +is ignored within directives so you can add variable definitions on the +same line, the next line or split across several line with comments +interspersed, if you prefer. + + [% INCLUDE table %] + + [% INCLUDE table title="Active Projects" %] + + [% INCLUDE table + title = "Active Projects" + bgcolor = "#80ff00" # chartreuse + border = 2 + %] + +The INCLUDE directive localises (i.e. copies) all variables before +processing the template. Any changes made within the included +template will not affect variables in the including template. + + [% foo = 10 %] + + foo is originally [% foo %] + [% INCLUDE bar %] + foo is still [% foo %] + + [% BLOCK bar %] + foo was [% foo %] + [% foo = 20 %] + foo is now [% foo %] + [% END %] + +output: + foo is originally 10 + foo was 10 + foo is now 20 + foo is still 10 + +Technical Note: the localisation of the stash (that is, the process by +which variables are copied before an INCLUDE to prevent being +overwritten) is only skin deep. The top-level variable namespace +(hash) is copied, but no attempt is made to perform a deep-copy of +other structures (hashes, arrays, objects, etc.) Therefore, a 'foo' +variable referencing a hash will be copied to create a new 'foo' +variable but which points to the same hash array. Thus, if you update +compound variables (e.g. foo.bar) then you will change the original +copy, regardless of any stash localisation. If you're not worried +about preserving variable values, or you trust the templates you're +including then you might prefer to use the PROCESS directive which is +faster by virtue of not performing any localisation. + +From version 2.04 onwards, you can specify dotted variables as "local" +variables to an INCLUDE directive. However, be aware that because of +the localisation issues explained above (if you skipped the previous +Technical Note above then you might want to go back and read it or +skip this section too), the variables might not actualy be "local". +If the first element of the variable name already references a hash +array then the variable update will affect the original variable. + + [% foo = { + bar = 'Baz' + } + %] + + [% INCLUDE somefile foo.bar='Boz' %] + + [% foo.bar %] # Boz + +This behaviour can be a little unpredictable (and may well be improved +upon in a future version). If you know what you're doing with it and +you're sure that the variables in question are defined (nor not) as you +expect them to be, then you can rely on this feature to implement some +powerful "global" data sharing techniques. Otherwise, you might prefer +to steer well clear and always pass simple (undotted) variables as +parameters to INCLUDE and other similar directives. + +If you want to process several templates in one go then you can +specify each of their names (quoted or unquoted names only, no unquoted +'$variables') joined together by '+'. The INCLUDE directive +will then process them in order. + + [% INCLUDE html/header + "site/$header" + site/menu + title = "My Groovy Web Site" + %] + +The variable stash is localised once and then the templates specified +are processed in order, all within that same variable context. This +makes it slightly faster than specifying several separate INCLUDE +directives (because you only clone the variable stash once instead of +n times), but not quite as "safe" because any variable changes in the +first file will be visible in the second, third and so on. This +might be what you want, of course, but then again, it might not. + + + +=item PROCESS + +The PROCESS directive is similar to INCLUDE but does not perform any +localisation of variables before processing the template. Any changes +made to variables within the included template will be visible in the +including template. + + [% foo = 10 %] + + foo is [% foo %] + [% PROCESS bar %] + foo is [% foo %] + + [% BLOCK bar %] + [% foo = 20 %] + changed foo to [% foo %] + [% END %] + +output: + + foo is 10 + changed foo to 20 + foo is 20 + +Parameters may be specified in the PROCESS directive, but these too will +become visible changes to current variable values. + + [% foo = 10 %] + foo is [% foo %] + [% PROCESS bar + foo = 20 + %] + foo is [% foo %] + + [% BLOCK bar %] + this is bar, foo is [% foo %] + [% END %] + +output: + + foo is 10 + this is bar, foo is 20 + foo is 20 + +The PROCESS directive is slightly faster than INCLUDE because it +avoids the need to localise (i.e. copy) the variable stash before +processing the template. As with INSERT and INCLUDE, the first +parameter does not need to be quoted as long as it contains only +alphanumeric characters, underscores, periods or forward slashes. +A '$' prefix can be used to explicitly indicate a variable which +should be interpolated to provide the template name: + + [% myheader = 'my/misc/header' %] + [% PROCESS myheader %] # 'myheader' + [% PROCESS $myheader %] # 'my/misc/header' + +As with INCLUDE, multiple templates can be specified, delimited by +'+', and are processed in order. + + [% PROCESS html/header + my/header %] + + + + + +=item WRAPPER + +It's not unusual to find yourself adding common headers and footers to +pages or sub-sections within a page. Something like this: + + [% INCLUDE section/header + title = 'Quantum Mechanics' + %] + Quantum mechanics is a very interesting subject wish + should prove easy for the layman to fully comprehend. + [% INCLUDE section/footer %] + + [% INCLUDE section/header + title = 'Desktop Nuclear Fusion for under $50' + %] + This describes a simple device which generates significant + sustainable electrical power from common tap water by process + of nuclear fusion. + [% INCLUDE section/footer %] + +The individual template components being included might look like these: + +section/header: + + <p> + <h2>[% title %]</h2> + +section/footer: + + </p> + +The WRAPPER directive provides a way of simplifying this a little. It +encloses a block up to a matching END directive, which is first +processed to generate some output. This is then passed to the named +template file or BLOCK as the 'content' variable. + + [% WRAPPER section + title = 'Quantum Mechanics' + %] + Quantum mechanics is a very interesting subject wish + should prove easy for the layman to fully comprehend. + [% END %] + + [% WRAPPER section + title = 'Desktop Nuclear Fusion for under $50' + %] + This describes a simple device which generates significant + sustainable electrical power from common tap water by process + of nuclear fusion. + [% END %] + +The single 'section' template can then be defined as: + + <p> + <h2>[% title %]</h2> + [% content %] + </p> + +Like other block directives, it can be used in side-effect notation: + + [% INSERT legalese.txt WRAPPER big_bold_table %] + +It's also possible to specify multiple templates to a WRAPPER directive. +The specification order indicates outermost to innermost wrapper templates. +For example, given the following template block definitions: + + [% BLOCK bold %]<b>[% content %]</b>[% END %] + [% BLOCK italic %]<i>[% content %]</i>[% END %] + +the directive + + [% WRAPPER bold+italic %]Hello World[% END %] + +would generate the following output: + + <b><i>Hello World</i></b> + + + + + + + + + + + + + + + + + + +=item BLOCK + +The BLOCK ... END construct can be used to define template component +blocks which can be processed with the INCLUDE, PROCESS and WRAPPER +directives. + + [% BLOCK tabrow %] + <tr><td>[% name %]<td><td>[% email %]</td></tr> + [% END %] + + <table> + [% PROCESS tabrow name='Fred' email='fred@nowhere.com' %] + [% PROCESS tabrow name='Alan' email='alan@nowhere.com' %] + </table> + +A BLOCK definition can be used before it is defined, as long as the +definition resides in the same file. The block definition itself does +not generate any output. + + [% PROCESS tmpblk %] + + [% BLOCK tmpblk %] This is OK [% END %] + +You can use an anonymous BLOCK to capture the output of a template +fragment. + + [% julius = BLOCK %] + And Caesar's spirit, ranging for revenge, + With Ate by his side come hot from hell, + Shall in these confines with a monarch's voice + Cry 'Havoc', and let slip the dogs of war; + That this foul deed shall smell above the earth + With carrion men, groaning for burial. + [% END %] + +Like a named block, it can contain any other template directives which +are processed when the block is defined. The output generated by the +block is then assigned to the variable 'julius'. + +Anonymous BLOCKs can also be used to define block macros. The +enclosing block is processed each time the macro is called. + + [% MACRO locate BLOCK %] + The [% animal %] sat on the [% place %]. + [% END %] + + [% locate(animal='cat', place='mat') %] # The cat sat on the mat + [% locate(animal='dog', place='log') %] # The dog sat on the log + + + +=back + +=head2 Conditional Processing + +=over 4 + + +=item IF / UNLESS / ELSIF / ELSE + +The IF and UNLESS directives can be used to process or ignore a +block based on some run-time condition. + + [% IF frames %] + [% INCLUDE frameset %] + [% END %] + + [% UNLESS text_mode %] + [% INCLUDE biglogo %] + [% END %] + +Multiple conditions may be joined with ELSIF and/or ELSE blocks. + + [% IF age < 10 %] + Hello [% name %], does your mother know you're + using her AOL account? + [% ELSIF age < 18 %] + Sorry, you're not old enough to enter + (and too dumb to lie about your age) + [% ELSE %] + Welcome [% name %]. + [% END %] + +The following conditional and boolean operators may be used: + + == != < <= > >= && || ! and or not + +Note that C<and>, C<or> and C<not> are also provided as aliases for +C<&&>, C<||> and C<!>, respectively. + +Conditions may be arbitrarily complex and are evaluated with the same +precedence as in Perl. Parenthesis may be used to explicitly +determine evaluation order. + + # ridiculously contrived complex example + [% IF (name == 'admin' || uid <= 0) && mode == 'debug' %] + I'm confused. + [% ELSIF more > less %] + That's more or less correct. + [% END %] + + + + + + +=item SWITCH / CASE + +The SWITCH / CASE construct can be used to perform a multi-way +conditional test. The SWITCH directive expects an expression which is +first evaluated and then compared against each CASE statement in turn. +Each CASE directive should contain a single value or a list of values +which should match. CASE may also be left blank or written as [% CASE +DEFAULT %] to specify a default match. Only one CASE matches, there +is no drop-through between CASE statements. + + [% SWITCH myvar %] + [% CASE value1 %] + ... + [% CASE [ value2 value3 ] %] # multiple values + ... + [% CASE myhash.keys %] # ditto + ... + [% CASE %] # default + ... + [% END %] + + + + +=back + +=head2 Loop Processing + +=over 4 + + +=item FOREACH + +The FOREACH directive will iterate through the items in a list, processing +the enclosed block for each one. + + my $vars = { + foo => 'Foo', + items => [ 'one', 'two', 'three' ], + }; + +template: + + Things: + [% FOREACH thing = [ foo 'Bar' "$foo Baz" ] %] + * [% thing %] + [% END %] + + Items: + [% FOREACH i = items %] + * [% i %] + [% END %] + + Stuff: + [% stuff = [ foo "$foo Bar" ] %] + [% FOREACH s = stuff %] + * [% s %] + [% END %] + +output: + + Things: + * Foo + * Bar + * Foo Baz + + Items: + * one + * two + * three + + Stuff: + * Foo + * Foo Bar + +You can use also use 'IN' instead of '=' if you prefer. + + [% FOREACH crook IN government %] + +When the FOREACH directive is used without specifying a target variable, +any iterated values which are hash references will be automatically +imported. + + [% userlist = [ + { id => 'tom', name => 'Thomas' }, + { id => 'dick', name => 'Richard' }, + { id => 'larry', name => 'Lawrence' }, + ] + %] + + [% FOREACH user IN userlist %] + [% user.id %] [% user.name %] + [% END %] + +short form: + + [% FOREACH userlist %] + [% id %] [% name %] + [% END %] + +Note that this particular usage creates a localised variable context +to prevent the imported hash keys from overwriting any existing +variables. The imported definitions and any other variables defined +in such a FOREACH loop will be lost at the end of the loop, when the +previous context and variable values are restored. + +However, under normal operation, the loop variable remains in scope +after the FOREACH loop has ended (caveat: overwriting any variable +previously in scope). This is useful as the loop variable is secretly +an iterator object (see below) and can be used to analyse the last +entry processed by the loop. + +The FOREACH directive can also be used to iterate through the entries +in a hash array. Each entry in the hash is returned in sorted order +(based on the key) as a hash array containing 'key' and 'value' items. + + [% users = { + tom => 'Thomas', + dick => 'Richard', + larry => 'Lawrence', + } + %] + + [% FOREACH u IN users %] + * [% u.key %] : [% u.value %] + [% END %] + +Output: + + * dick : Richard + * larry : Lawrence + * tom : Thomas + +The NEXT directive starts the next iteration in the FOREACH loop. + + [% FOREACH user IN userlist %] + [% NEXT IF user.isguest %] + Name: [% user.name %] Email: [% user.email %] + [% END %] + +The LAST directive can be used to prematurely exit the loop. BREAK is +also provided as an alias for LAST. + + [% FOREACH match IN results.nsort('score').reverse %] + [% LAST IF match.score < 50 %] + [% match.score %] : [% match.url %] + [% END %] + +The FOREACH directive is implemented using the Template::Iterator +module. A reference to the iterator object for a FOREACH directive is +implicitly available in the 'loop' variable. The following methods +can be called on the 'loop' iterator. + + size() number of elements in the list + max() index number of last element (size - 1) + index() index of current iteration from 0 to max() + count() iteration counter from 1 to size() (i.e. index() + 1) + first() true if the current iteration is the first + last() true if the current iteration is the last + prev() return the previous item in the list + next() return the next item in the list + +See L<Template::Iterator> for further details. + +Example: + + [% FOREACH item IN [ 'foo', 'bar', 'baz' ] -%] + [%- "<ul>\n" IF loop.first %] + <li>[% loop.count %]/[% loop.size %]: [% item %] + [%- "</ul>\n" IF loop.last %] + [% END %] + +Output: + + <ul> + <li>1/3: foo + <li>2/3: bar + <li>3/3: baz + </ul> + +Note that the number() method is supported as an alias for count() for +backwards compatibility but may be deprecated in some future version. + +Nested loops will work as expected, with the 'loop' variable correctly +referencing the innermost loop and being restored to any previous +value (i.e. an outer loop) at the end of the loop. + + [% FOREACH group IN grouplist; + # loop => group iterator + "Groups:\n" IF loop.first; + + FOREACH user IN group.userlist; + # loop => user iterator + "$loop.count: $user.name\n"; + END; + + # loop => group iterator + "End of Groups\n" IF loop.last; + END + %] + +The 'iterator' plugin can also be used to explicitly create an +iterator object. This can be useful within nested loops where you +need to keep a reference to the outer iterator within the inner loop. +The iterator plugin effectively allows you to create an iterator by a +name other than 'loop'. See Template::Plugin::Iterator for further +details. + + [% USE giter = iterator(grouplist) %] + + [% FOREACH group IN giter %] + [% FOREACH user IN group.userlist %] + user #[% loop.count %] in + group [% giter.count %] is + named [% user.name %] + [% END %] + [% END %] + + + + +=item WHILE + +The WHILE directive can be used to repeatedly process a template block +while a conditional expression evaluates true. The expression may +be arbitrarily complex as per IF / UNLESS. + + [% WHILE total < 100 %] + ... + [% total = calculate_new_total %] + [% END %] + +An assignment can be enclosed in parenthesis to evaluate the assigned +value. + + [% WHILE (user = get_next_user_record) %] + [% user.name %] + [% END %] + +The NEXT directive can be used to start the next iteration of a +WHILE loop and BREAK can be used to exit the loop, both as per FOREACH. + +The Template Toolkit uses a failsafe counter to prevent runaway WHILE +loops which would otherwise never terminate. If the loop exceeds 1000 +iterations then an 'undef' exception will be thrown, reporting the +error: + + WHILE loop terminated (> 1000 iterations) + +The $Template::Directive::WHILE_MAX variable controls this behaviour +and can be set to a higher value if necessary. + + +=back + +=head2 Filters, Plugins, Macros and Perl + +=over 4 + + +=item FILTER + +The FILTER directive can be used to post-process the output of a +block. A number of standard filters are provided with the Template +Toolkit. The 'html' filter, for example, escapes the 'E<lt>', 'E<gt>' +and '&' characters to prevent them from being interpreted as HTML tags +or entity reference markers. + + [% FILTER html %] + HTML text may have < and > characters embedded + which you want converted to the correct HTML entities. + [% END %] + +output: + + HTML text may have < and > characters embedded + which you want converted to the correct HTML entities. + +The FILTER directive can also follow various other non-block directives. +For example: + + [% INCLUDE mytext FILTER html %] + +The '|' character can also be used as an alias for 'FILTER'. + + [% INCLUDE mytext | html %] + +Multiple filters can be chained together and will be called in sequence. + + [% INCLUDE mytext FILTER html FILTER html_para %] + +or + + [% INCLUDE mytext | html | html_para %] + +Filters come in two flavours, known as 'static' or 'dynamic'. A +static filter is a simple subroutine which accepts a text string as +the only argument and returns the modified text. The 'html' filter is +an example of a static filter, implemented as: + + sub html_filter { + my $text = shift; + for ($text) { + s/&/&/g; + s/</</g; + s/>/>/g; + } + return $text; + } + +Dynamic filters can accept arguments which are specified when the filter +is called from a template. The 'repeat' filter is such an example, +accepting a numerical argument which specifies the number of times +that the input text should be repeated. + + [% FILTER repeat(3) %]blah [% END %] + +output: + + blah blah blah + +These are implemented as filter 'factories'. The factory subroutine +is passed a reference to the current Template::Context object along +with any additional arguments specified. It should then return a +subroutine reference (e.g. a closure) which implements the filter. +The 'repeat' filter factory is implemented like this: + + sub repeat_filter_factory { + my ($context, $iter) = @_; + $iter = 1 unless defined $iter; + + return sub { + my $text = shift; + $text = '' unless defined $text; + return join('\n', $text) x $iter; + } + } + +The FILTERS option, described in L<Template::Manual::Config>, allows +custom filters to be defined when a Template object is instantiated. +The Template::Context define_filter() method allows further filters +to be defined at any time. + +When using a filter, it is possible to assign an alias to it for +further use. This is most useful for dynamic filters that you want +to re-use with the same configuration. + + [% FILTER echo = repeat(2) %] + Is there anybody out there? + [% END %] + + [% FILTER echo %] + Mother, should I build a wall? + [% END %] + +Output: + + Is there anybody out there? + Is there anybody out there? + + Mother, should I build a wall? + Mother, should I build a wall? + +The FILTER directive automatically quotes the name of the filter. As +with INCLUDE et al, you can use a variable to provide the name of the +filter, prefixed by '$'. + + [% myfilter = 'html' %] + [% FILTER $myfilter %] # same as [% FILTER html %] + ... + [% END %] + +A template variable can also be used to define a static filter +subroutine. However, the Template Toolkit will automatically call any +subroutine bound to a variable and use the value returned. Thus, the +above example could be implemented as: + + my $vars = { + myfilter => sub { return 'html' }, + }; + +template: + + [% FILTER $myfilter %] # same as [% FILTER html %] + ... + [% END %] + +To define a template variable that evaluates to a subroutine reference +that can be used by the FILTER directive, you should create a +subroutine that, when called automatically by the Template Toolkit, +returns another subroutine reference which can then be used to perform +the filter operation. Note that only static filters can be +implemented in this way. + + my $vars = { + myfilter => sub { \&my_filter_sub }, + }; + + sub my_filter_sub { + my $text = shift; + # do something + return $text; + } + +template: + + [% FILTER $myfilter %] + ... + [% END %] + +Alternately, you can bless a subroutine reference into a class (any +class will do) to fool the Template Toolkit into thinking it's an +object rather than a subroutine. This will then bypass the automatic +"call-a-subroutine-to-return-a-value" magic. + + my $vars = { + myfilter => bless(\&my_filter_sub, 'anything_you_like'), + }; + +template: + + [% FILTER $myfilter %] + ... + [% END %] + +Filters bound to template variables remain local to the variable +context in which they are defined. That is, if you define a filter in +a PERL block within a template that is loaded via INCLUDE, then the +filter definition will only exist until the end of that template when +the stash is delocalised, restoring the previous variable state. If +you want to define a filter which persists for the lifetime of the +processor, or define additional dynamic filter factories, then you can +call the define_filter() method on the current Template::Context +object. + +See L<Template::Manual::Filters> for a complete list of available filters, +their descriptions and examples of use. + + + + + + +=item USE + +The USE directive can be used to load and initialise "plugin" +extension modules. + + [% USE myplugin %] + +A plugin is a regular Perl module that conforms to a particular +object-oriented interface, allowing it to be loaded into and used +automatically by the Template Toolkit. For details of this interface +and information on writing plugins, consult L<Template::Plugin>. + +The plugin name is case-sensitive and will be appended to the +PLUGIN_BASE value (default: 'Template::Plugin') to construct a full +module name. Any periods, '.', in the name will be converted to '::'. + + [% USE MyPlugin %] # => Template::Plugin::MyPlugin + [% USE Foo.Bar %] # => Template::Plugin::Foo::Bar + +Various standard plugins are included with the Template Toolkit (see +below and L<Template::Manual::Plugins>). These can be specified in lower +case and are mapped to the appropriate name. + + [% USE cgi %] # => Template::Plugin::CGI + [% USE table %] # => Template::Plugin::Table + +Any additional parameters supplied in parenthesis after the plugin +name will be also be passed to the new() constructor. A reference to +the current Template::Context object is always passed as the first +parameter. + + [% USE MyPlugin('foo', 123) %] + +equivalent to: + + Template::Plugin::MyPlugin->new($context, 'foo', 123); + +Named parameters may also be specified. These are collated into a +hash which is passed by reference as the last parameter to the +constructor, as per the general code calling interface. + + [% USE url('/cgi-bin/foo', mode='submit', debug=1) %] + +equivalent to: + + Template::Plugin::URL->new($context, '/cgi-bin/foo' + { mode => 'submit', debug => 1 }); + +The plugin may represent any data type; a simple variable, hash, list or +code reference, but in the general case it will be an object reference. +Methods can be called on the object (or the relevant members of the +specific data type) in the usual way: + + [% USE table(mydata, rows=3) %] + + [% FOREACH row = table.rows %] + <tr> + [% FOREACH item = row %] + <td>[% item %]</td> + [% END %] + </tr> + [% END %] + +An alternative name may be provided for the plugin by which it can be +referenced: + + [% USE scores = table(myscores, cols=5) %] + + [% FOREACH row = scores.rows %] + ... + [% END %] + +You can use this approach to create multiple plugin objects with +different configurations. This example shows how the 'format' plugin +is used to create sub-routines bound to variables for formatting text +as per printf(). + + [% USE bold = format('<b>%s</b>') %] + [% USE ital = format('<i>%s</i>') %] + + [% bold('This is bold') %] + [% ital('This is italic') %] + +Output: + + <b>This is bold</b> + <i>This is italic</i> + +This next example shows how the URL plugin can be used to build +dynamic URLs from a base part and optional query parameters. + + [% USE mycgi = URL('/cgi-bin/foo.pl', debug=1) %] + <a href="[% mycgi %]">... + <a href="[% mycgi(mode='submit') %]"... + +Output: + + <a href="/cgi-bin/foo.pl?debug=1">... + <a href="/cgi-bin/foo.pl?mode=submit&debug=1">... + +The CGI plugin is an example of one which delegates to another Perl +module. In this this case, it is to Lincoln Stein's CGI.pm module. +All of the methods provided by CGI.pm are available via the plugin. + + [% USE CGI %] + + [% CGI.start_form %] + + [% CGI.checkbox_group(name => 'colours', + values => [ 'red' 'green' 'blue' ]) + %] + + [% CGI.popup_menu(name => 'items', + values => [ 'foo' 'bar' 'baz' ]) + %] + + [% CGI.end_form %] + +Simon Matthews has written the DBI plugin which provides an interface +to Tim Bunce's DBI module (available from CPAN). Here's a short +example: + + [% USE DBI('DBI:mSQL:mydbname') %] + + [% FOREACH user = DBI.query('SELECT * FROM users') %] + [% user.id %] [% user.name %] [% user.etc.etc %] + [% END %] + +See L<Template::Manual::Plugins> for more information on the plugins +distributed with the toolkit or available from CPAN. + +The LOAD_PERL option (disabled by default) provides a further way by +which external Perl modules may be loaded. If a regular Perl module +(i.e. not a Template::Plugin::* or other module relative to some +PLUGIN_BASE) supports an object-oriented interface and a new() +constructor then it can be loaded and instantiated automatically. The +following trivial example shows how the IO::File module might be used. + + [% USE file = IO.File('/tmp/mydata') %] + + [% WHILE (line = file.getline) %] + <!-- [% line %] --> + [% END %] + + + + + + +=item MACRO + +The MACRO directive allows you to define a directive or directive block +which is then evaluated each time the macro is called. + + [% MACRO header INCLUDE header %] + +Calling the macro as: + + [% header %] + +is then equivalent to: + + [% INCLUDE header %] + +Macros can be passed named parameters when called. These values remain +local to the macro. + + [% header(title='Hello World') %] + +equivalent to: + + [% INCLUDE header title='Hello World' %] + +A MACRO definition may include parameter names. Values passed to the +macros are then mapped to these local variables. Other named parameters +may follow these. + + [% MACRO header(title) INCLUDE header %] + + [% header('Hello World') %] + [% header('Hello World', bgcol='#123456') %] + +equivalent to: + + [% INCLUDE header title='Hello World' %] + [% INCLUDE header title='Hello World' bgcol='#123456' %] + +Here's another example, defining a macro for display numbers +in comma-delimited groups of 3, using the chunk and join virtual +method. + + [% MACRO number(n) GET n.chunk(-3).join(',') %] + + [% number(1234567) %] # 1,234,567 + +A MACRO may precede any directive and must conform to the structure +of the directive. + + [% MACRO header IF frames %] + [% INCLUDE frames/header %] + [% ELSE %] + [% INCLUDE header %] + [% END %] + + [% header %] + +A MACRO may also be defined as an anonymous BLOCK. The block will be +evaluated each time the macro is called. + + [% MACRO header BLOCK %] + ...content... + [% END %] + + [% header %] + +If you've got the EVAL_PERL option set, then you can even define a +MACRO as a PERL block (see below): + + [% MACRO triple(n) PERL %] + my $n = $stash->get('n'); + print $n * 3; + [% END -%] + + + + + + + +=item PERL + +(for the advanced reader) + +The PERL directive is used to mark the start of a block which contains +Perl code for evaluation. The EVAL_PERL option must be enabled for Perl +code to be evaluated or a 'perl' exception will be thrown with the +message 'EVAL_PERL not set'. + +Perl code is evaluated in the Template::Perl package. The $context +package variable contains a reference to the current Template::Context +object. This can be used to access the functionality of the Template +Toolkit to process other templates, load plugins, filters, etc. +See L<Template::Context> for further details. + + [% PERL %] + print $context->include('myfile'); + [% END %] + +The $stash variable contains a reference to the top-level stash object +which manages template variables. Through this, variable values can +be retrieved and updated. See L<Template::Stash> for further details. + + [% PERL %] + $stash->set(foo => 'bar'); + print "foo value: ", $stash->get('foo'); + [% END %] + +Output + foo value: bar + +Output is generated from the PERL block by calling print(). Note that +the Template::Perl::PERLOUT handle is selected (tied to an output +buffer) instead of STDOUT. + + [% PERL %] + print "foo\n"; # OK + print PERLOUT "bar\n"; # OK, same as above + print Template::Perl::PERLOUT "baz\n"; # OK, same as above + print STDOUT "qux\n"; # WRONG! + [% END %] + +The PERL block may contain other template directives. These are +processed before the Perl code is evaluated. + + [% name = 'Fred Smith' %] + + [% PERL %] + print "[% name %]\n"; + [% END %] + +Thus, the Perl code in the above example is evaluated as: + + print "Fred Smith\n"; + +Exceptions may be thrown from within PERL blocks via die() and will be +correctly caught by enclosing TRY blocks. + + [% TRY %] + [% PERL %] + die "nothing to live for\n"; + [% END %] + [% CATCH %] + error: [% error.info %] + [% END %] + +output: + error: nothing to live for + + + + +=item RAWPERL + +(for the very advanced reader) + +The Template Toolkit parser reads a source template and generates the +text of a Perl subroutine as output. It then uses eval() to evaluate +it into a subroutine reference. This subroutine is then called to +process the template, passing a reference to the current +Template::Context object through which the functionality of the +Template Toolkit can be accessed. The subroutine reference can be +cached, allowing the template to be processed repeatedly without +requiring any further parsing. + +For example, a template such as: + + [% PROCESS header %] + The [% animal %] sat on the [% location %] + [% PROCESS footer %] + +is converted into the following Perl subroutine definition: + + sub { + my $context = shift; + my $stash = $context->stash; + my $output = ''; + my $error; + + eval { BLOCK: { + $output .= $context->process('header'); + $output .= "The "; + $output .= $stash->get('animal'); + $output .= " sat on the "; + $output .= $stash->get('location'); + $output .= $context->process('footer'); + $output .= "\n"; + } }; + if ($@) { + $error = $context->catch($@, \$output); + die $error unless $error->type eq 'return'; + } + + return $output; + } + +To examine the Perl code generated, such as in the above example, set +the $Template::Parser::DEBUG package variable to any true value. You +can also set the $Template::Directive::PRETTY variable true to have +the code formatted in a readable manner for human consumption. The +source code for each generated template subroutine will be printed to +STDERR on compilation (i.e. the first time a template is used). + + $Template::Parser::DEBUG = 1; + $Template::Directive::PRETTY = 1; + + ... + + $template->process($file, $vars) + || die $template->error(), "\n"; + +The PERL ... END construct allows Perl code to be embedded into a +template (when the EVAL_PERL option is set), but it is evaluated at +"runtime" using eval() each time the template subroutine is called. +This is inherently flexible, but not as efficient as it could be, +especially in a persistent server environment where a template may be +processed many times. + +The RAWPERL directive allows you to write Perl code that is integrated +directly into the generated Perl subroutine text. It is evaluated +once at compile time and is stored in cached form as part of the +compiled template subroutine. This makes RAWPERL blocks more +efficient than PERL blocks. + +The downside is that you must code much closer to the metal. Within +PERL blocks, you can call print() to generate some output. RAWPERL +blocks don't afford such luxury. The code is inserted directly into +the generated subroutine text and should conform to the convention of +appending to the '$output' variable. + + [% PROCESS header %] + + [% RAWPERL %] + $output .= "Some output\n"; + ... + $output .= "Some more output\n"; + [% END %] + +The critical section of the generated subroutine for this example would +then look something like: + + ... + eval { BLOCK: { + $output .= $context->process('header'); + $output .= "\n"; + $output .= "Some output\n"; + ... + $output .= "Some more output\n"; + $output .= "\n"; + } }; + ... + +As with PERL blocks, the $context and $stash references are pre-defined +and available for use within RAWPERL code. + + +=back + +=head2 Exception Handling and Flow Control + +=over 4 + + +=item TRY / THROW / CATCH / FINAL + +(more advanced material) + +The Template Toolkit supports fully functional, nested exception +handling. The TRY directive introduces an exception handling scope +which continues until the matching END directive. Any errors that +occur within that block will be caught and can be handled by one +of the CATCH blocks defined. + + [% TRY %] + ...blah...blah... + [% CALL somecode %] + ...etc... + [% INCLUDE someblock %] + ...and so on... + [% CATCH %] + An error occurred! + [% END %] + +Errors are raised as exceptions (objects of the Template::Exception +class) and contain two fields, 'type' and 'info'. The exception +'type' can be any string containing letters, numbers, '_' or '.', and +is used to indicate the kind of error that occurred. The 'info' field +contains an error message indicating what actually went wrong. Within +a catch block, the exception object is aliased to the 'error' variable. +You can access the 'type' and 'info' fields directly. + + [% mydsn = 'dbi:MySQL:foobar' %] + ... + + [% TRY %] + [% USE DBI(mydsn) %] + [% CATCH %] + ERROR! Type: [% error.type %] + Info: [% error.info %] + [% END %] + +output (assuming a non-existant database called 'foobar'): + + ERROR! Type: DBI + Info: Unknown database "foobar" + +The 'error' variable can also be specified by itself and will return a +string of the form "$type error - $info". + + ... + [% CATCH %] + ERROR: [% error %] + [% END %] + +output: + + ERROR: DBI error - Unknown database "foobar" + +Each CATCH block may be specified with a particular exception type +denoting the kind of error that it should catch. Multiple CATCH +blocks can be provided to handle different types of exception that may +be thrown in the TRY block. A CATCH block specified without any type, +as in the previous example, is a default handler which will catch any +otherwise uncaught exceptions. This can also be specified as +[% CATCH DEFAULT %]. + + [% TRY %] + [% INCLUDE myfile %] + [% USE DBI(mydsn) %] + [% CALL somecode %] + ... + [% CATCH file %] + File Error! [% error.info %] + [% CATCH DBI %] + [% INCLUDE database/error.html %] + [% CATCH %] + [% error %] + [% END %] + +Remember that you can specify multiple directives within a single tag, +each delimited by ';'. Thus, you might prefer to write your simple +CATCH blocks more succinctly as: + + [% TRY %] + ... + [% CATCH file; "File Error! $error.info" %] + [% CATCH DBI; INCLUDE database/error.html %] + [% CATCH; error %] + [% END %] + +or even: + + [% TRY %] + ... + [% CATCH file ; + "File Error! $error.info" ; + CATCH DBI ; + INCLUDE database/error.html ; + CATCH ; + error ; + END + %] + +The DBI plugin throws exceptions of the 'DBI' type (in case that +wasn't already obvious). The other specific exception caught here is +of the 'file' type. + +A 'file' error is automatically thrown by the Template Toolkit when it +can't find a file, or fails to load, parse or process a file that has +been requested by an INCLUDE, PROCESS, INSERT or WRAPPER directive. +If 'myfile' can't be found in the example above, the [% INCLUDE myfile +%] directive will raise a 'file' exception which is then caught by the +[% CATCH file %] block, generating the output: + + File Error! myfile: not found + +Note that the DEFAULT option (disabled by default) allows you to +specify a default file to be used any time a template file can't be +found. This will prevent file exceptions from ever being raised when +a non-existant file is requested (unless, of course, the DEFAULT file +doesn't exist). Errors encountered once the file has been found +(i.e. read error, parse error) will be raised as file exceptions as per +usual. + +Uncaught exceptions (i.e. the TRY block doesn't have a type specific +or default CATCH handler) may be caught by enclosing TRY blocks which +can be nested indefinitely across multiple templates. If the error +isn't caught at any level then processing will stop and the Template +process() method will return a false value to the caller. The +relevant Template::Exception object can be retrieved by calling the +error() method. + + [% TRY %] + ... + [% TRY %] + [% INCLUDE $user.header %] + [% CATCH file %] + [% INCLUDE header %] + [% END %] + ... + [% CATCH DBI %] + [% INCLUDE database/error.html %] + [% END %] + +In this example, the inner TRY block is used to ensure that the first +INCLUDE directive works as expected. We're using a variable to +provide the name of the template we want to include, user.header, and +it's possible this contains the name of a non-existant template, or +perhaps one containing invalid template directives. If the INCLUDE fails + with a 'file' error then we CATCH it in the inner block and INCLUDE +the default 'header' file instead. Any DBI errors that occur within +the scope of the outer TRY block will be caught in the relevant CATCH +block, causing the 'database/error.html' template to be processed. +Note that included templates inherit all currently defined template +variable so these error files can quite happily access the 'error' +variable to retrieve information about the currently caught exception. +e.g. + +'database/error.html': + + <h2>Database Error</h2> + A database error has occurred: [% error.info %] + +You can also specify a FINAL block. This is always processed +regardless of the outcome of the TRY and/or CATCH block. If an +exception is uncaught then the FINAL block is processed before jumping +to the enclosing block or returning to the caller. + + [% TRY %] + ... + [% CATCH this %] + ... + [% CATCH that %] + ... + [% FINAL %] + All done! + [% END %] + +The output from the TRY block is left intact up to the point where an +exception occurs. For example, this template: + + [% TRY %] + This gets printed + [% THROW food 'carrots' %] + This doesn't + [% CATCH food %] + culinary delights: [% error.info %] + [% END %] + +generates the following output: + + This gets printed + culinary delights: carrots + +The CLEAR directive can be used in a CATCH or FINAL block to clear +any output created in the TRY block. + + [% TRY %] + This gets printed + [% THROW food 'carrots' %] + This doesn't + [% CATCH food %] + [% CLEAR %] + culinary delights: [% error.info %] + [% END %] + +output: + + culinary delights: carrots + +Exception types are hierarchical, with each level being separated by +the familiar dot operator. A 'DBI.connect' exception is a more +specific kind of 'DBI' error. Similarly, a 'myown.error.barf' is a +more specific kind of 'myown.error' type which itself is also a +'myown' error. A CATCH handler that specifies a general exception +type (such as 'DBI' or 'myown.error') will also catch more specific +types that have the same prefix as long as a more specific handler +isn't defined. Note that the order in which CATCH handlers are +defined is irrelevant; a more specific handler will always catch an +exception in preference to a more generic or default one. + + [% TRY %] + ... + [% CATCH DBI ; + INCLUDE database/error.html ; + CATCH DBI.connect ; + INCLUDE database/connect.html ; + CATCH ; + INCLUDE error.html ; + END + %] + +In this example, a 'DBI.connect' error has it's own handler, a more +general 'DBI' block is used for all other DBI or DBI.* errors and a +default handler catches everything else. + +Exceptions can be raised in a template using the THROW directive. The +first parameter is the exception type which doesn't need to be quoted +(but can be, it's the same as INCLUDE) followed by the relevant error +message which can be any regular value such as a quoted string, +variable, etc. + + [% THROW food "Missing ingredients: $recipe.error" %] + + [% THROW user.login 'no user id: please login' %] + + [% THROW $myerror.type "My Error: $myerror.info" %] + +It's also possible to specify additional positional or named +parameters to the THROW directive if you want to pass more than +just a simple message back as the error info field. + + [% THROW food 'eggs' 'flour' msg='Missing Ingredients' %] + +In this case, the error 'info' field will be a hash array containing +the named arguments, in this case 'msg' =E<gt> 'Missing Ingredients', +and an 'args' item which contains a list of the positional arguments, +in this case 'eggs' and 'flour'. The error 'type' field remains +unchanged, here set to 'food'. + + [% CATCH food %] + [% error.info.msg %] + [% FOREACH item = error.info.args %] + * [% item %] + [% END %] + [% END %] + +This produces the output: + + Missing Ingredients + * eggs + * flour + +In addition to specifying individual positional arguments as +[% error.info.args.n %], the 'info' hash contains keys directly +pointing to the positional arguments, as a convenient shortcut. + + [% error.info.0 %] # same as [% error.info.args.0 %] + +Exceptions can also be thrown from Perl code which you've bound to +template variables, or defined as a plugin or other extension. To +raise an exception, call die() passing a reference to a +Template::Exception object as the argument. This will then be caught +by any enclosing TRY blocks from where the code was called. + + use Template::Exception; + ... + + my $vars = { + foo => sub { + # ... do something ... + die Template::Exception->new('myerr.naughty', + 'Bad, bad error'); + }, + }; + +template: + + [% TRY %] + ... + [% foo %] + ... + [% CATCH myerr ; + "Error: $error" ; + END + %] + +output: + + Error: myerr.naughty error - Bad, bad error + +The 'info' field can also be a reference to another object or data +structure, if required. + + die Template::Exception->new('myerror', { + module => 'foo.pl', + errors => [ 'bad permissions', 'naughty boy' ], + }); + +Later, in a template: + + [% TRY %] + ... + [% CATCH myerror %] + [% error.info.errors.size or 'no'; + error.info.errors.size == 1 ? ' error' : ' errors' %] + in [% error.info.module %]: + [% error.info.errors.join(', ') %]. + [% END %] + +Generating the output: + + 2 errors in foo.pl: + bad permissions, naughty boy. + +You can also call die() with a single string, as is common in much +existing Perl code. This will automatically be converted to an +exception of the 'undef' type (that's the literal string 'undef', +not the undefined value). If the string isn't terminated with a +newline then Perl will append the familiar " at $file line $line" +message. + + sub foo { + # ... do something ... + die "I'm sorry, Dave, I can't do that\n"; + } + +If you're writing a plugin, or some extension code that has the +current Template::Context in scope (you can safely skip this section +if this means nothing to you) then you can also raise an exception by +calling the context throw() method. You can pass it an +Template::Exception object reference, a pair of ($type, $info) parameters +or just an $info string to create an exception of 'undef' type. + + $context->throw($e); # exception object + $context->throw('Denied'); # 'undef' type + $context->throw('user.passwd', 'Bad Password'); + + + + + + + +=item NEXT + +The NEXT directive can be used to start the next iteration of a FOREACH +or WHILE loop. + + [% FOREACH user = userlist %] + [% NEXT IF user.isguest %] + Name: [% user.name %] Email: [% user.email %] + [% END %] + + + + + +=item LAST + +The LAST directive can be used to prematurely exit a FOREACH or WHILE +loop. + + [% FOREACH user = userlist %] + Name: [% user.name %] Email: [% user.email %] + [% LAST IF some.condition %] + [% END %] + +BREAK can also be used as an alias for LAST. + + + + +=item RETURN + +The RETURN directive can be used to stop processing the current +template and return to the template from which it was called, resuming +processing at the point immediately after the INCLUDE, PROCESS or +WRAPPER directive. If there is no enclosing template then the +Template process() method will return to the calling code with a +true value. + + Before + [% INCLUDE half_wit %] + After + + [% BLOCK half_wit %] + This is just half... + [% RETURN %] + ...a complete block + [% END %] + +output: + + Before + This is just half... + After + + + + +=item STOP + +The STOP directive can be used to indicate that the processor should +stop gracefully without processing any more of the template document. +This is a planned stop and the Template process() method will return a +B<true> value to the caller. This indicates that the template was +processed successfully according to the directives within it. + + [% IF something.terrible.happened %] + [% INCLUDE fatal/error.html %] + [% STOP %] + [% END %] + + [% TRY %] + [% USE DBI(mydsn) %] + ... + [% CATCH DBI.connect %] + <p>Cannot connect to the database: [% error.info %]</p> + <br> + We apologise for the inconvenience. The cleaning lady + has removed the server power to plug in her vacuum cleaner. + Please try again later. + </p> + [% INCLUDE footer %] + [% STOP %] + [% END %] + + + + +=item CLEAR + +The CLEAR directive can be used to clear the output buffer for the current +enclosing block. It is most commonly used to clear the output generated +from a TRY block up to the point where the error occurred. + + [% TRY %] + blah blah blah # this is normally left intact + [% THROW some 'error' %] # up to the point of error + ... + [% CATCH %] + [% CLEAR %] # clear the TRY output + [% error %] # print error string + [% END %] + + + + +=back + +=head2 Miscellaneous + +=over 4 + + +=item META + +The META directive allows simple metadata items to be defined within a +template. These are evaluated when the template is parsed and as such +may only contain simple values (e.g. it's not possible to interpolate +other variables values into META variables). + + [% META + title = 'The Cat in the Hat' + author = 'Dr. Seuss' + version = 1.23 + %] + +The 'template' variable contains a reference to the main template +being processed. These metadata items may be retrieved as attributes +of the template. + + <h1>[% template.title %]</h1> + <h2>[% template.author %]</h2> + +The 'name' and 'modtime' metadata items are automatically defined for +each template to contain its name and modification time in seconds +since the epoch. + + [% USE date %] # use Date plugin to format time + ... + [% template.name %] last modified + at [% date.format(template.modtime) %] + +The PRE_PROCESS and POST_PROCESS options allow common headers and +footers to be added to all templates. The 'template' reference is +correctly defined when these templates are processed, allowing headers +and footers to reference metadata items from the main template. + + $template = Template->new({ + PRE_PROCESS => 'header', + POST_PROCESS => 'footer', + }); + + $template->process('cat_in_hat'); + +header: + + <html> + <head> + <title>[% template.title %]</title> + </head> + <body> + +cat_in_hat: + + [% META + title = 'The Cat in the Hat' + author = 'Dr. Seuss' + version = 1.23 + year = 2000 + %] + + The cat in the hat sat on the mat. + +footer: + + <hr> + © [% template.year %] [% template.author %] + </body> + </html> + +The output generated from the above example is: + + <html> + <head> + <title>The Cat in the Hat</title> + </head> + <body> + + The cat in the hat sat on the mat. + + <hr> + © 2000 Dr. Seuss + </body> + </html> + + + +=item TAGS + +The TAGS directive can be used to set the START_TAG and END_TAG values +on a per-template file basis. + + [% TAGS <+ +> %] + + <+ INCLUDE header +> + +The TAGS directive may also be used to set a named TAG_STYLE + + [% TAGS html %] + <!-- INCLUDE header --> + +See the TAGS and TAG_STYLE configuration options for further details. + + + + + + + + +=item DEBUG + +The DEBUG directive can be used to enable or disable directive debug +messages within a template. The DEBUG configuration option must be +set to include DEBUG_DIRS for the DEBUG directives to have any effect. +If DEBUG_DIRS is not set then the parser will automatically ignore and +remove any DEBUG directives. + +The DEBUG directive can be used with an 'on' or 'off' parameter to +enable or disable directive debugging messages from that point +forward. When enabled, the output of each directive in the generated +output will be prefixed by a comment indicate the file, line and +original directive text. + + [% DEBUG on %] + directive debugging is on (assuming DEBUG option is set true) + [% DEBUG off %] + directive debugging is off + +The 'format' parameter can be used to change the format of the debugging +message. + + [% DEBUG format '<!-- $file line $line : [% $text %] -->' %] + + + + + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Filters.pod b/lib/Template/Manual/Filters.pod new file mode 100644 index 0000000..c42f2ef --- /dev/null +++ b/lib/Template/Manual/Filters.pod @@ -0,0 +1,529 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Filters +# +# DESCRIPTION +# This section lists all the standard filters distributed with the +# Template Toolkit for post-processing output. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Filters - Standard filters + +=head1 DESCRIPTION + +This section lists all the standard filters distributed with the +Template Toolkit for post-processing output. + +=head1 STANDARD FILTERS + + + +=head2 format(format) + +The 'format' filter takes a format string as a parameter (as per +printf()) and formats each line of text accordingly. + + [% FILTER format('<!-- %-40s -->') %] + This is a block of text filtered + through the above format. + [% END %] + +output: + + <!-- This is a block of text filtered --> + <!-- through the above format. --> + +=head2 upper + +Folds the input to UPPER CASE. + + [% "hello world" FILTER upper %] + +output: + + HELLO WORLD + +=head2 lower + +Folds the input to lower case. + + [% "Hello World" FILTER lower %] + +output: + + hello world + +=head2 ucfirst + +Folds the first character of the input to UPPER CASE. + + [% "hello" FILTER ucfirst %] + +output: + + Hello + +=head2 lcfirst + +Folds the first character of the input to lower case. + + [% "HELLO" FILTER lcfirst %] + +output: + + hELLO + +=head2 trim + +Trims any leading or trailing whitespace from the input text. Particularly +useful in conjunction with INCLUDE, PROCESS, etc., having the same effect +as the TRIM configuration option. + + [% INCLUDE myfile | trim %] + +=head2 collapse + +Collapse any whitespace sequences in the input text into a single space. +Leading and trailing whitespace (which would be reduced to a single space) +is removed, as per trim. + + [% FILTER collapse %] + + The cat + + sat on + + the mat + + [% END %] + +output: + + The cat sat on the mat + +=head2 html + +Converts the characters 'E<lt>', 'E<gt>' and '&' to '<', '>' and +'&', respectively, protecting them from being interpreted as +representing HTML tags or entities. + + [% FILTER html %] + Binary "<=>" returns -1, 0, or 1 depending on... + [% END %] + +output: + + Binary "<=>" returns -1, 0, or 1 depending on... + +=head2 html_entity + +The html filter is fast and simple but it doesn't encode the full +range of HTML entities that your text may contain. The html_entity +filter uses either the Apache::Util module (which is written in C and +is therefore faster) or the HTML::Entities module (written in Perl but +equally as comprehensive) to perform the encoding. If one or other of +these modules are installed on your system then the text will be +encoded (via the escape_html() or encode_entities() subroutines +respectively) to convert all extended characters into their +appropriate HTML entities (e.g. converting 'é' to 'é'). If +neither module is available on your system then an 'html_entity' exception +will be thrown reporting an appropriate message. + +For further information on HTML entity encoding, see +http://www.w3.org/TR/REC-html40/sgml/entities.html. + +=head2 html_para + +This filter formats a block of text into HTML paragraphs. A sequence of +two or more newlines is used as the delimiter for paragraphs which are +then wrapped in HTML E<lt>pE<gt>...E<lt>/pE<gt> tags. + + [% FILTER html_para %] + The cat sat on the mat. + + Mary had a little lamb. + [% END %] + +output: + + <p> + The cat sat on the mat. + </p> + + <p> + Mary had a little lamb. + </p> + +=head2 html_break / html_para_break + +Similar to the html_para filter described above, but uses the HTML tag +sequence E<lt>brE<gt>E<lt>brE<gt> to join paragraphs. + + [% FILTER html_break %] + The cat sat on the mat. + + Mary had a little lamb. + [% END %] + +output: + + The cat sat on the mat. + <br> + <br> + Mary had a little lamb. + +=head2 html_line_break + +This filter replaces any newlines with E<lt>brE<gt> HTML tags, +thus preserving the line breaks of the original text in the +HTML output. + + [% FILTER html_line_break %] + The cat sat on the mat. + Mary had a little lamb. + [% END %] + +output: + + The cat sat on the mat.<br> + Mary had a little lamb.<br> + +=head2 uri + +This filter URI escapes the input text, converting any characters +outside of the permitted URI character set (as defined by RFC 2396) +into a C<%nn> hex escape. + + [% 'my file.html' | uri %] + +output: + + my%20file.html + +Note that URI escaping isn't always enough when generating hyperlinks in +an HTML document. The C<&> character, for example, is valid in a URI and +will not be escaped by the URI filter. In this case you should also filter +the text through the 'html' filter. + + <a href="[% filename | uri | html %]">click here</a> + +=head2 indent(pad) + +Indents the text block by a fixed pad string or width. The 'pad' argument +can be specified as a string, or as a numerical value to indicate a pad +width (spaces). Defaults to 4 spaces if unspecified. + + [% FILTER indent('ME> ') %] + blah blah blah + cabbages, rhubard, onions + [% END %] + +output: + + ME> blah blah blah + ME> cabbages, rhubard, onions + +=head2 truncate(length) + +Truncates the text block to the length specified, or a default length of +32. Truncated text will be terminated with '...' (i.e. the '...' falls +inside the required length, rather than appending to it). + + [% FILTER truncate(21) %] + I have much to say on this matter that has previously + been said on more than one occasion. + [% END %] + +output: + + I have much to say... + +=head2 repeat(iterations) + +Repeats the text block for as many iterations as are specified (default: 1). + + [% FILTER repeat(3) %] + We want more beer and we want more beer, + [% END %] + We are the more beer wanters! + +output: + + We want more beer and we want more beer, + We want more beer and we want more beer, + We want more beer and we want more beer, + We are the more beer wanters! + +=head2 remove(string) + +Searches the input text for any occurrences of the specified string and +removes them. A Perl regular expression may be specified as the search +string. + + [% "The cat sat on the mat" FILTER remove('\s+') %] + +output: + + Thecatsatonthemat + +=head2 replace(search, replace) + +Similar to the remove filter described above, but taking a second parameter +which is used as a replacement string for instances of the search string. + + [% "The cat sat on the mat" | replace('\s+', '_') %] + +output: + + The_cat_sat_on_the_mat + +=head2 redirect(file, options) + +The 'redirect' filter redirects the output of the block into a separate +file, specified relative to the OUTPUT_PATH configuration item. + + [% FOREACH user = myorg.userlist %] + [% FILTER redirect("users/${user.id}.html") %] + [% INCLUDE userinfo %] + [% END %] + [% END %] + +or more succinctly, using side-effect notation: + + [% INCLUDE userinfo + FILTER redirect("users/${user.id}.html") + FOREACH user = myorg.userlist + %] + +A 'file' exception will be thrown if the OUTPUT_PATH option is undefined. + +An optional 'binmode' argument can follow the filename to explicitly set +the output file to binary mode. + + [% PROCESS my/png/generator + FILTER redirect("images/logo.png", binmode=1) %] + +For backwards compatibility with earlier versions, a single true/false +value can be used to set binary mode. + + [% PROCESS my/png/generator + FILTER redirect("images/logo.png", 1) %] + +For the sake of future compatibility and clarity, if nothing else, we +would strongly recommend you explicitly use the named 'binmode' option +as shown in the first example. + +=head2 eval / evaltt + +The 'eval' filter evaluates the block as template text, processing +any directives embedded within it. This allows template variables to +contain template fragments, or for some method to be provided for +returning template fragments from an external source such as a +database, which can then be processed in the template as required. + + my $vars = { + fragment => "The cat sat on the [% place %]", + }; + $template->process($file, $vars); + +The following example: + + [% fragment | eval %] + +is therefore equivalent to + + The cat sat on the [% place %] + +The 'evaltt' filter is provided as an alias for 'eval'. + +=head2 perl / evalperl + +The 'perl' filter evaluates the block as Perl code. The EVAL_PERL +option must be set to a true value or a 'perl' exception will be +thrown. + + [% my_perl_code | perl %] + +In most cases, the [% PERL %] ... [% END %] block should suffice for +evaluating Perl code, given that template directives are processed +before being evaluate as Perl. Thus, the previous example could have +been written in the more verbose form: + + [% PERL %] + [% my_perl_code %] + [% END %] + +as well as + + [% FILTER perl %] + [% my_perl_code %] + [% END %] + +The 'evalperl' filter is provided as an alias for 'perl' for backwards +compatibility. + +=head2 stdout(options) + +The stdout filter prints the output generated by the enclosing block to +STDOUT. The 'binmode' option can be passed as either a named parameter +or a single argument to set STDOUT to binary mode (see the +binmode perl function). + + [% PROCESS something/cool + FILTER stdout(binmode=1) # recommended %] + + [% PROCESS something/cool + FILTER stdout(1) # alternate %] + +The stdout filter can be used to force binmode on STDOUT, or also inside +redirect, null or stderr blocks to make sure that particular output goes +to stdout. See the null filter below for an example. + +=head2 stderr + +The stderr filter prints the output generated by the enclosing block to +STDERR. + +=head2 null + +The null filter prints nothing. This is useful for plugins whose +methods return values that you don't want to appear in the output. +Rather than assigning every plugin method call to a dummy variable +to silence it, you can wrap the block in a null filter: + + [% FILTER null; + USE im = GD.Image(100,100); + black = im.colorAllocate(0, 0, 0); + red = im.colorAllocate(255,0, 0); + blue = im.colorAllocate(0, 0, 255); + im.arc(50,50,95,75,0,360,blue); + im.fill(50,50,red); + im.png | stdout(1); + END; + -%] + +Notice the use of the stdout filter to ensure that a particular expression +generates output to stdout (in this case in binary mode). + +=head2 latex(outputType) + +Passes the text block to LaTeX and produces either PDF, DVI or +PostScript output. The 'outputType' argument determines the output +format and it should be set to one of the strings: "pdf" (default), +"dvi", or "ps". + +The text block should be a complete LaTeX source file. + + [% FILTER latex("pdf") -%] + \documentclass{article} + + \begin{document} + + \title{A Sample TT2 \LaTeX\ Source File} + \author{Craig Barratt} + \maketitle + + \section{Introduction} + This is some text. + + \end{document} + [% END -%] + +The output will be a PDF file. You should be careful not to prepend or +append any extraneous characters or text outside the FILTER block, +since this text will wrap the (binary) output of the latex filter. +Notice the END directive uses '-%]' for the END_TAG to remove the +trailing new line. + +One example where you might prepend text is in a CGI script where +you might include the Content-Type before the latex output, eg: + + Content-Type: application/pdf + + [% FILTER latex("pdf") -%] + \documentclass{article} + \begin{document} + ... + \end{document} + [% END -%] + +In other cases you might use the redirect filter to put the output +into a file, rather than delivering it to stdout. This might be +suitable for batch scripts: + + [% output = FILTER latex("pdf") -%] + \documentclass{article} + \begin{document} + ... + \end{document} + [% END; output | redirect("document.pdf", 1) -%] + +(Notice the second argument to redirect to force binary mode.) + +Note that the latex filter runs one or two external programs, so it +isn't very fast. But for modest documents the performance is adequate, +even for interactive applications. + +A error of type 'latex' will be thrown if there is an error reported +by latex, pdflatex or dvips. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Internals.pod b/lib/Template/Manual/Internals.pod new file mode 100644 index 0000000..b8cf80b --- /dev/null +++ b/lib/Template/Manual/Internals.pod @@ -0,0 +1,556 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Internals +# +# DESCRIPTION +# This document provides an overview of the internal architecture of +# the Template Toolkit. It is a work in progress and is far from +# complete, currently providing little more than an overview of how +# the major components fit together. Nevertheless, it's a good +# starting point for anyone wishing to delve into the source code to +# find out how it all works. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Internals - Template Toolkit internals + +=head1 DESCRIPTION + +This document provides an overview of the internal architecture of the +Template Toolkit. It is a work in progress and is far from complete, +currently providing little more than an overview of how the major +components fit together. Nevertheless, it's a good starting point for +anyone wishing to delve into the source code to find out how it all +works. + +=head2 Outside Looking In + +The B<Template> module is simply a front end module which creates and +uses a Template::Service and pipes the output wherever you want it to +go (STDOUT by default, or maybe a file, scalar, etc). The +Apache::Template module (available separately from CPAN) is another +front end. That creates a Template::Service::Apache object, calls on +it as required and sends the output back to the relevant +Apache::Request object. + +These front-end modules are really only there to handle any specifics +of the environment in which they're being used. The Apache::Template +front end, for example, handles Apache::Request specifics and +configuration via the httpd.conf. The regular Template front-end +deals with STDOUT, variable refs, etc. Otherwise it is +Template::Service (or subclass) which does all the work. + +The B<Template::Service> module provides a high-quality template +delivery service, with bells, whistles, signed up service level +agreement and a 30-day no quibble money back guarantee. "Have +a good time, all the time", that's our motto. + +Within the lower levels of the Template Toolkit, there are lots of +messy details that we generally don't want to have to worry about most +of the time. Things like templates not being found, or failing to +parse correctly, uncaught exceptions being thrown, missing plugin +modules or dependencies, and so on. Template::Service hides that all +away and makes everything look simple to the outsider. It provides +extra features, like PRE_PROCESS, PROCESS and POST_PROCESS, and also +provides the error recovery mechanism via ERROR. You ask it to +process a template and it takes care of everything for you. The +Template::Service::Apache module goes a little bit further, adding +some extra headers to the Apache::Request, setting a few extra template +variables, and so on. + +For the most part, the job of a service is really just one of +scheduling and dispatching. It receives a request in the form of a +call to its process() method and schedules the named template +specified as an argument, and possibly several other templates +(PRE_PROCESS, etc) to be processed in order. It doesn't actually +process the templates itself, but instead makes a process() call +against a Template::Context object. + +B<Template::Context> is the runtime engine for the Template Toolkit - +the module that hangs everything together in the lower levels of the +Template Toolkit and that one that does most of the real work, albeit +by crafty delegation to various other friendly helper modules. + +Given a template name (or perhaps a reference to a scalar or file +handle) the context process() method must load and compile, or fetch a +cached copy of a previously compiled template, corresponding to that +name. It does this by calling on a list of one or more +Template::Provider objects (the LOAD_TEMPLATES posse) who themselves +might get involved with a Template::Parser to help turn source +templates into executable Perl code (but more on that later). Thankfully, +all of this complexity is hidden away behind a simple template() +method. You call it passing a template name as an argument, and it +returns a compiled template in the form of a Template::Document +object, or otherwise raises an exception. + +A B<Template::Document> is a thin object wrapper around a compiled +template subroutine. The object implements a process() method which +performs a little bit of housekeeping and then calls the template +subroutine. The object also defines template metadata (defined in +C<[% META ... %]> directives) and has a block() method which returns +a hash of any additional C<[% BLOCK xxxx %]> definitions found in the +template source. + +So the context fetches a compiled document via its own template() +method and then gets ready to process it. It first updates the stash +(the place where template variables get defined - more on that +shortly) to set any template variable definitions specified as the +second argument by reference to hash array. Then, it calls the +document process() method, passing a reference to itself, the context +object, as an argument. In doing this, it provides itself as an +object against which template code can make callbacks to access +runtime resources and Template Toolkit functionality. + +What we're trying to say here is this: not only does the Template::Context +object receive calls from the I<outside>, i.e. those originating in user +code calling the process() method on a Template object, but it also +receives calls from the I<inside>, i.e. those originating in template +directives of the form C<[% PROCESS template %]>. + +Before we move on to that, here's a simple structure diagram showing +the outer layers of the Template Toolkit heading inwards, with pseudo +code annotations showing a typical invocation sequence. + + ,--------. + | Caller | use Template; + `--------' my $tt = Template->new( ... ); + | $tt->process($template, \%vars); + | Outside + - - - - | - - - - - - - - - - - - - - - - - - - - - - - - - - - - T T + | package Template; Inside + V + +----------+ sub process($template, \%vars) { + | Template | $out = $self->SERVICE->process($template, $vars); + +----------+ print $out or send it to $self->OUTPUT; + | } + | + | package Template::Service; + | + | sub process($template, \%vars) { + | try { + +----------+ foreach $p in @self->PRE_PROCESS + | Service | $self->CONTEXT->process($p, $vars); + +----------+ + | $self->CONTEXT->process($template, $vars); + | + | foreach $p @self->POST_PROCESS + | $self->CONTEXT->process($p, $vars); + | } + | catch { + | $self->CONTEXT->process($self->ERROR); + | } + | } + | + V package Template::Context; + +----------+ + | Context | sub process($template, \%vars) { + +----------+ # fetch compiled template + | $template = $self->template($template) + | # update stash + | $self->STASH->update($vars); + | # process template + | $template->process($self) + | } + V + +----------+ package Template::Document; + | Document | + +----------+ sub process($context) { + $output = &{ $self->BLOCK }($context); + } + + +=head2 Inside Looking Out + +To understand more about what's going on in these lower levels, we +need to look at what a compiled template looks like. In fact, a +compiled template is just a regular Perl sub-routine. Here's a very +simple one. + + sub my_compiled_template { + return "This is a compiled template.\n"; + } + +You're unlikely to see a compiled template this simple unless you +wrote it yourself but it is entirely valid. All a template subroutine +is obliged to do is return some output (which may be an empty of +course). If it can't for some reason, then it should raise an error +via die(). + + sub my_todo_template { + die "This template not yet implemented\n"; + } + +If it wants to get fancy, it can raise an error as a +Template::Exception object. An exception object is really just a +convenient wrapper for the 'type' and 'info' fields. + + sub my_solilique_template { + die (Template::Exception->new('yorrick', 'Fellow of infinite jest')); + } + +Templates generally need to do a lot more than just generate static +output or raise errors. They may want to inspect variable values, +process another template, load a plugin, run a filter, and so on. +Whenever a template subroutine is called, it gets passed a reference +to a Template::Context object. It is through this context object that +template code can access the features of the Template Toolkit. + +We described earlier how the Template::Service object calls on +Template::Context to handle a process() request from the I<outside>. +We can make a similar request on a context to process a template, but +from within the code of another template. This is a call from the +I<inside>. + + sub my_process_template { + my $context = shift; + + my $output = $context->process('header', { title => 'Hello World' }) + . "\nsome content\n" + . $context->process('footer'); + } + +This is then roughly equivalent to a source template something +like this: + + [% PROCESS header + title = 'Hello World' + %] + some content + [% PROCESS footer %] + +Template variables are stored in, and managed by a B<Template::Stash> +object. This is a blessed hash array in which template variables are +defined. The object wrapper provides get() and set() method which +implement all the magical.variable.features of the Template Toolkit. + +Each context object has its own stash, a reference to which can be +returned by the appropriately named stash() method. So to print the +value of some template variable, or for example, to represent the +following source template: + + <title>[% title %]</title> + +we might have a subroutine definition something like this: + + sub { + my $context = shift; + my $stash = $context->stash(); + return '<title>' . $stash->get('title') . '</title>'; + } + +The stash get() method hides the details of the underlying variable +types, automatically calling code references, checking return values, +and performing other such tricks. If 'title' happens to be bound to a +subroutine then we can specify additional parameters as a list +reference passed as the second argument to get(). + + [% title('The Cat Sat on the Mat') %] + +This translates to the stash get() call: + + $stash->get([ 'title', ['The Cat Sat on the Mat'] ]); + +Dotted compound variables can be requested by passing a single +list reference to the get() method in place of the variable +name. Each pair of elements in the list should correspond to the +variable name and reference to a list of arguments for each +dot-delimited element of the variable. + + [% foo(1, 2).bar(3, 4).baz(5) %] + +is thus equivalent to + + $stash->get([ foo => [1,2], bar => [3,4], baz => [5] ]); + +If there aren't any arguments for an element, you can specify an +empty, zero or null argument list. + + [% foo.bar %] + $stash->get([ 'foo', 0, 'bar', 0 ]); + +The set() method works in a similar way. It takes a variable +name and a variable value which should be assigned to it. + + [% x = 10 %] + $stash->set('x', 10); + + [% x.y = 10 %] + $stash->set([ 'x', 0, 'y', 0 ], 10); + +So the stash gives us access to template variables and the context +provides the higher level functionality. Alongside the process() +method lies the include() method. Just as with the PROCESS / INCLUDE +directives, the key difference is in variable localisation. Before +processing a template, the process() method simply updates the stash +to set any new variable definitions, overwriting any existing values. +In contrast, the include() method creates a copy of the existing +stash, in a process known as I<cloning> the stash, and then uses that +as a temporary variable store. Any previously existing variables are +still defined, but any changes made to variables, including setting +the new variable values passed aas arguments will affect only the +local copy of the stash (although note that it's only a shallow copy, +so it's not foolproof). When the template has been processed, the include() +method restores the previous variable state by I<decloning> the stash. + +The context also provides an insert() method to implement the INSERT +directive, but no wrapper() method. This functionality can be implemented +by rewriting the Perl code and calling include(). + + [% WRAPPER foo -%] + blah blah [% x %] + [%- END %] + + $context->include('foo', { + content => 'blah blah ' . $stash->get('x'), + }); + +Other than the template processing methods process(), include() and insert(), +the context defines methods for fetching plugin objects, plugin(), and +filters, filter(). + + [% USE foo = Bar(10) %] + + $stash->set('foo', $context->plugin('Bar', [10])); + + [% FILTER bar(20) %] + blah blah blah + [% END %] + + my $filter = $context->filter('bar', [20]); + &$filter('blah blah blah'); + +Pretty much everything else you might want to do in a template can be done +in Perl code. Things like IF, UNLESS, FOREACH and so on all have direct +counterparts in Perl. + + [% IF msg %] + Message: [% msg %] + [% END %]; + + if ($stash->get('msg')) { + $output .= 'Message: '; + $output .= $stash->get('msg'); + } + +The best way to get a better understanding of what's going on underneath +the hood is to set the C<$Template::Parser::DEBUG> flag to a true value +and start processing templates. This will cause the parser to print the +generated Perl code for each template it compiles to STDERR. You'll +probably also want to set the C<$Template::Directive::PRETTY> option to +have the Perl pretty-printed for human consumption. + + use Template; + use Template::Parser; + use Template::Directive; + + $Template::Parser::DEBUG = 1; + $Template::Directive::PRETTY = 1; + + my $template = Template->new(); + $template->process(\*DATA, { cat => 'dog', mat => 'log' }); + + __DATA__ + The [% cat %] sat on the [% mat %] + +The output sent to STDOUT remains as you would expect: + + The dog sat on the log + +The output sent to STDERR would look something like this: + + compiled main template document block: + sub { + my $context = shift || die "template sub called without context\n"; + my $stash = $context->stash; + my $output = ''; + my $error; + + eval { BLOCK: { + $output .= "The "; + $output .= $stash->get('cat'); + $output .= " sat on the "; + $output .= $stash->get('mat'); + $output .= "\n"; + } }; + if ($@) { + $error = $context->catch($@, \$output); + die $error unless $error->type eq 'return'; + } + + return $output; + } + + +=head1 HACKING ON THE TEMPLATE TOOLKIT + +Please feel free to hack on the Template Toolkit. If you find a bug +that needs fixing, if you have an idea for something that's missing, +or you feel inclined to tackle something on the TODO list, then by all +means go ahead and do it! + +If you're contemplating something non-trivial then you'll probably +want to bring it up on the mailing list first to get an idea about the +current state of play, find out if anyone's already working on it, and +so on. + +When you start to hack on the Template Toolkit, please make sure you +start from the latest developer release. Stable releases are uploaded +to CPAN and have all-numerical version numbers, e.g. 2.04, 2.05. +Developer releases are available from the Template Toolkit web site +and have a character suffix on the version, e.g. 2.04a, 2.04b, etc. + +Once you've made your changes, please remember to update the test +suite by adding extra tests to one of the existing test scripts in +the 't' sub-directory, or by adding a new test script of your own. +And of course, run C<make test> to ensure that all the tests pass +with your new code. + +Don't forget that any files you do add will need to be added to the +MANIFEST. Running 'make manifest' will do this for you, but you need +to make sure you haven't got any other temporary files lying around +that might also get added to it. + +Documentation is often something that gets overlooked but it's just +as important as the code. If you're updating existing documentation +then you should download the 'docsrc' bundle from which all the +Template Toolkit documentation is built and make your changes in there. +It's also available from the Template Toolkit web site. See the +README distributed in the archive for further information. + +If you're adding a new module, a plugin module, for example, then it's +OK to include the POD documentation in with the module, but I<please> +write it all in one piece at the end of the file, I<after> the code +(just look at any other Template::* module for an example). It's a +religious issue, I know, but I have a strong distaste for POD documentation +interspersed throughout the code. In my not-so-humble opinion, it makes +both the code and the documentation harder to read (same kinda problem +as embedding Perl in HTML). + +Aesthetics aside, if I do want to extract the documentation into the +docsrc bundle then it's easy for me to do it if it's all written in +one chunk and extremely tedious if not. So for practical reasons +alone, please keep Perl and POD sections separate. Comment blocks +within the code are of course welcome. + +To share your changes with the rest of the world, you'll need to +prepare a patch file. To do this you should have 2 directories +side-by-side, one which is the original, unmodified distribution +directory for the latest developer release, and the other is a +copy of that same directory which includes your changes. + +The following example shows a typical hacking session. First we +unpack the latest developer release. + + $ tar zxf Template-Toolkit-2.05c.tar.gz + +At this point, it's a good idea to rename the directory to give +some indicate of what it contains. + + $ mv Template-Toolkit-2.05c Template-Toolkit-2.05c-abw-xyz-hack + +Then go hack! + + $ cd Template-Toolkit-2.05c-abw-xyz-hack + + [ hacking ] + + $ cd .. + +When you're all done and ready to prepare a patch, unpack the +distribution archive again so that you've got the original to +diff against your new code. + + $ tar zxf Template-Toolkit-2.05c.tar.gz + +You should now have an original distribution directory and a modified +version of that same directory, side-by-side. + + $ ls + Template-Toolkit-2.05c Template-Toolkit-2.05c-abw-xyz-hack + +Now run diff and save the output into an appropriately named patch +file. + + $ diff -Naur Template-Toolkit-2.05c Template-Toolkit-2.05c-abw-xyz-hack > patch-TT205c-abw-xyz-hack + +You can then post the generated patch file to the mailing list, +describing what it does, why it does it, how it does it and any +other relevant information. + +If you want to apply someone else's patch then you should start with the +same original distribution source on which the patch is based. From within +the root of the distribution, run 'patch' feeding in the patch file as +standard input. The 'p1' option is required to strip the first element +of the path name (e.g. Template-Toolkit-2.05c/README becomes README which +is then the correct path). + + $ tar zxf Template-Toolkit-2.05c.tar.gz + $ cd Template-Toolkit-2.05c + $ patch -p1 < ../patch-TT205c-abw-xyz-hack + +The output generated by 'patch' should be something like the following: + + patching file README + patching file lib/Template.pm + patching file lib/Template/Provider.pm + patching file t/provider.t + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Intro.pod b/lib/Template/Manual/Intro.pod new file mode 100644 index 0000000..c50c9e8 --- /dev/null +++ b/lib/Template/Manual/Intro.pod @@ -0,0 +1,295 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Intro +# +# DESCRIPTION +# This section provides a general introduction to the Template +# Toolkit, giving a quick overview of features, examples of template +# directives and use of the Template.pm module. It also described the +# basic concept underlying the toolkit: the separation of +# presentation elements from application logic and data. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Intro - Introduction to the Template Toolkit + +=head1 DESCRIPTION + +This section provides a general introduction to the Template Toolkit, +giving a quick overview of features, examples of template directives +and use of the Template.pm module. It also described the basic concept +underlying the toolkit: the separation of presentation elements from +application logic and data. + +The Template Toolkit is a collection of modules which implement a +fast, flexible, powerful and extensible template processing system. +It was originally designed and remains primarily useful for generating +dynamic web content, but it can be used equally well for processing +any kind of text documents. This POD documentation is all generated +using the Template Toolkit batch mode utility F<ttree>, for example. + +At the simplest level it provides an easy way to process template +files, filling in embedded variable references with their equivalent +values. + + Dear [% name %], + + It has come to our attention that your account is in + arrears to the sum of [% debt %]. + + Please settle your account before [% deadline %] or we + will be forced to revoke your Licence to Thrill. + + The Management. + +By default, template directives are embedded within the character +sequences '[%' ... '%]' but you can change these and various other +options to configure how the Template Toolkit looks, feels and works. +You can set the INTERPOLATE option, for example, if you prefer to +embed your variables in Perl style: + + Dear $name, + + It has come to our attention that your account is in + arrears to the sum of $debt. + ... + +=head2 Template.pm + +The Template.pm module is the front end to the Template Toolkit, +providing access to the full range of functionality through a single +module with a simple interface. It loads the other modules as +required and instantiates a default set of objects to handle +subsequent template processing requests. Configuration parameters may +be passed to the Template.pm constructor, new(), which are then used +to configure the underlying objects. + + use Template; + + my $tt = Template->new({ + INCLUDE_PATH => '/usr/local/templates', + INTERPOLATE => 1, + }) || die "$Template::ERROR\n"; + +The Template object implements a process() method for processing template +files or text. The name of the input template (or various other sources) +is passed as the first argument, followed by a reference to a hash array +of variable definitions for substitution in the template. + + my $vars = { + name => 'Count Edward van Halen', + debt => '3 riffs and a solo', + deadline => 'the next chorus', + }; + + $tt->process('letters/overdrawn', $vars) + || die $tt->error(), "\n"; + + +The process() method returns true (1) on success and prints the +template output to STDOUT, by default. On error, the process() method +returns false (undef). The error() method can then be called to +retrieve details of the error. + +=head2 Component Based Content Construction + +A number of special directives are provided, such as INSERT, INCLUDE +and PROCESS, which allow content to be built up from smaller template +components. This permits a modular approach to building a web site or +other content repository, promoting reusability, cross-site +consistency, ease of construction and subsequent maintenance. Common +elements such as headers, footers, menu bars, tables, and so on, can +be created as separate template files which can then be processed into +other documents as required. All defined variables are inherited by +these templates along with any additional "local" values specified. + + [% PROCESS header + title = "The Cat Sat on the Mat" + %] + + [% PROCESS menu %] + + The location of the missing feline has now been established. + Thank you for your assistance. + + [% INSERT legal/disclaimer %] + + [% PROCESS footer %] + +You can also define a template as a BLOCK within the same file and +PROCESS it just like any other template file. This can be invaluable +for building up repetitive elements such as tables, menus, etc. + + [% BLOCK tabrow %] + <tr><td>[% name %]</td><td>[% email %]</td></tr> + [% END %] + + <table> + [% PROCESS tabrow name="tom" email="tom@here.org" %] + [% PROCESS tabrow name="dick" email="disk@there.org" %] + [% PROCESS tabrow name="larry" email="larry@where.org" %] + </table> + +=head2 Data and Code Binding + +One of the key features that sets the Template Toolkit apart from +other template processors is the ability to bind template variables to +any kind of Perl data: scalars, lists, hash arrays, sub-routines and +objects. + + my $vars = { + root => 'http://here.com/there', + menu => [ 'modules', 'authors', 'scripts' ], + client => { + name => 'Doctor Joseph von Satriani', + id => 'JVSAT', + }, + checkout => sub { my $total = shift; ...; return $something }, + shopcart => My::Cool::Shopping::Cart->new(), + }; + +The Template Toolkit will automatically Do The Right Thing to access +the data in an appropriate manner to return some value which can then +be output. The dot operator '.' is used to access into lists and +hashes or to call object methods. The FOREACH directive is provided for +iterating through lists, and various logical tests are available using +directives such as IF, UNLESS, ELSIF, ELSE, SWITCH, CASE, etc. + + [% FOREACH section = menu %] + <a href="[% root %]/[% section %]/index.html">[% section %]</a> + [% END %] + + <b>Client</a>: [% client.name %] (id: [% client.id %]) + + [% IF shopcart.nitems %] + Your shopping cart contains the following items: + <ul> + [% FOREACH item = shopcart.contents %] + <li>[% item.name %] : [% item.qty %] @ [% item.price %] + [% END %] + </ul> + + [% checkout(shopcart.total) %] + + [% ELSE %] + No items currently in shopping cart. + [% END %] + +=head2 Advanced Features: Filters, Macros, Exceptions, Plugins + +The Template Toolkit also provides a number of additional directives +for advanced processing and programmatical functionality. It supports +output filters (FILTER), allows custom macros to be defined (MACRO), +has a fully-featured exception handling system (TRY, THROW, CATCH, +FINAL) and supports a plugin architecture (USE) which allows special +plugin modules and even regular Perl modules to be loaded and used +with the minimum of fuss. The Template Toolkit is "just" a template +processor but you can trivially extend it to incorporate the +functionality of any Perl module you can get your hands on. Thus, it +is also a scalable and extensible template framework, ideally suited +for managing the presentation layer for application servers, content +management systems and other web applications. + +=head2 Separating Presentation and Application Logic + +Rather than embedding Perl code or some other scripting language +directly into template documents, it encourages you to keep functional +components (i.e. Perl code) separate from presentation components +(e.g. HTML templates). The template variables provide the interface +between the two layers, allowing data to be generated in code and then +passed to a template component for displaying (pipeline model) or for +sub-routine or object references to be bound to variables which can +then be called from the template as and when required (callback +model). + +The directives that the Template Toolkit provide implement their own +mini programming language, but they're not really designed for +serious, general purpose programming. Perl is a far more appropriate +language for that. If you embed application logic (e.g. Perl or other +scripting language fragments) in HTML templates then you risk losing +the clear separation of concerns between functionality and +presentation. It becomes harder to maintain the two elements in +isolation and more difficult, if not impossible, to reuse code or +presentation elements by themselves. It is far better to write your +application code in separate Perl modules, libraries or scripts and +then use templates to control how the resulting data is presented as +output. Thus you should think of the Template Toolkit language as a +set of layout directives for displaying data, not calculating it. + +Having said that, the Template Toolkit doesn't force you into one +approach or the other. It attempts to be pragmatic rather than +dogmatic in allowing you to do whatever best gets the job done. +Thus, if you enable the EVAL_PERL option then you can happily embed +real Perl code in your templates within PERL ... END directives. + +=head2 Performance + +The Template Toolkit uses a fast YACC-like parser which compiles +templates into Perl code for maximum runtime efficiency. It also has +an advanced caching mechanism which manages in-memory and on-disk +(i.e. persistent) versions of compiled templates. The modules that +comprise the toolkit are highly configurable and the architecture +around which they're built is designed to be extensible. The Template +Toolkit provides a powerful framework around which content creation +and delivery systems can be built while also providing a simple +interface through the Template front-end module for general use. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Plugins.pod b/lib/Template/Manual/Plugins.pod new file mode 100644 index 0000000..7955640 --- /dev/null +++ b/lib/Template/Manual/Plugins.pod @@ -0,0 +1,552 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Plugins +# +# DESCRIPTION +# This section lists the standard plugins which can be used to extend +# the runtime functionality of the Template Toolkit. The plugins are +# distributed with the Template Toolkit but may required additional +# modules from CPAN. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Plugins - Standard plugins + +=head1 DESCRIPTION + +This section lists the standard plugins which can be used to extend the +runtime functionality of the Template Toolkit. The plugins are +distributed with the Template Toolkit but may required additional +modules from CPAN. + + + +=head1 TEMPLATE TOOLKIT PLUGINS + +The following plugin modules are distributed with the Template +Toolkit. Some of the plugins interface to external modules (detailed +below) which should be downloaded from any CPAN site and installed +before using the plugin. + +=head2 Autoformat + +The Autoformat plugin is an interface to Damian Conway's Text::Autoformat +Perl module which provides advanced text wrapping and formatting. See +L<Template::Plugin::Autoformat> and L<Text::Autoformat> for further +details. + + [% USE autoformat(left=10, right=20) %] + [% autoformat(mytext) %] # call autoformat sub + [% mytext FILTER autoformat %] # or use autoformat filter + +The Text::Autoformat module is available from CPAN: + + http://www.cpan.org/modules/by-module/Text/ + +=head2 CGI + +The CGI plugin is a wrapper around Lincoln Stein's +E<lt>lstein@genome.wi.mit.eduE<gt> CGI.pm module. The plugin is +distributed with the Template Toolkit (see L<Template::Plugin::CGI>) +and the CGI module itself is distributed with recent versions Perl, +or is available from CPAN. + + [% USE CGI %] + [% CGI.param('param_name') %] + [% CGI.start_form %] + [% CGI.popup_menu( Name => 'color', + Values => [ 'Green', 'Brown' ] ) %] + [% CGI.end_form %] + +=head2 Datafile + +Provides an interface to data stored in a plain text file in a simple +delimited format. The first line in the file specifies field names +which should be delimiter by any non-word character sequence. +Subsequent lines define data using the same delimiter as int he first +line. Blank lines and comments (lines starting '#') are ignored. See +L<Template::Plugin::Datafile> for further details. + +/tmp/mydata: + + # define names for each field + id : email : name : tel + # here's the data + fred : fred@here.com : Fred Smith : 555-1234 + bill : bill@here.com : Bill White : 555-5678 + +example: + + [% USE userlist = datafile('/tmp/mydata') %] + + [% FOREACH user = userlist %] + [% user.name %] ([% user.id %]) + [% END %] + +=head2 Date + +The Date plugin provides an easy way to generate formatted time and date +strings by delegating to the POSIX strftime() routine. See +L<Template::Plugin::Date> and L<POSIX> for further details. + + [% USE date %] + [% date.format %] # current time/date + + File last modified: [% date.format(template.modtime) %] + +=head2 Directory + +The Directory plugin provides a simple interface to a directory and +the files within it. See L<Template::Plugin::Directory> for further +details. + + [% USE dir = Directory('/tmp') %] + [% FOREACH file = dir.files %] + # all the plain files in the directory + [% END %] + [% FOREACH file = dir.dirs %] + # all the sub-directories + [% END %] + +=head2 DBI + +The DBI plugin, developed by Simon Matthews +E<lt>sam@knowledgepool.comE<gt>, brings the full power of Tim Bunce's +E<lt>Tim.Bunce@ig.co.ukE<gt> database interface module (DBI) to your +templates. See L<Template::Plugin::DBI> and L<DBI> for further details. + + [% USE DBI('dbi:driver:database', 'user', 'pass') %] + + [% FOREACH user = DBI.query( 'SELECT * FROM users' ) %] + [% user.id %] [% user.name %] + [% END %] + +The DBI and relevant DBD modules are available from CPAN: + + http://www.cpan.org/modules/by-module/DBI/ + +=head2 Dumper + +The Dumper plugin provides an interface to the Data::Dumper module. See +L<Template::Plugin::Dumper> and L<Data::Dumper> for futher details. + + [% USE dumper(indent=0, pad="<br>") %] + [% dumper.dump(myvar, yourvar) %] + +=head2 File + +The File plugin provides a general abstraction for files and can be +used to fetch information about specific files within a filesystem. +See L<Template::Plugin::File> for further details. + + [% USE File('/tmp/foo.html') %] + [% File.name %] # foo.html + [% File.dir %] # /tmp + [% File.mtime %] # modification time + +=head2 Filter + +This module implements a base class plugin which can be subclassed +to easily create your own modules that define and install new filters. + + package MyOrg::Template::Plugin::MyFilter; + + use Template::Plugin::Filter; + use base qw( Template::Plugin::Filter ); + + sub filter { + my ($self, $text) = @_; + + # ...mungify $text... + + return $text; + } + + # now load it... + [% USE MyFilter %] + + # ...and use the returned object as a filter + [% FILTER $MyFilter %] + ... + [% END %] + +See L<Template::Plugin::Filter> for further details. + +=head2 Format + +The Format plugin provides a simple way to format text according to a +printf()-like format. See L<Template::Plugin::Format> for further +details. + + [% USE bold = format('<b>%s</b>') %] + [% bold('Hello') %] + +=head2 GD::Image, GD::Polygon, GD::Constants + +These plugins provide access to the GD graphics library via Lincoln +D. Stein's GD.pm interface. These plugins allow PNG, JPEG and other +graphical formats to be generated. + + [% FILTER null; + USE im = GD.Image(100,100); + # allocate some colors + black = im.colorAllocate(0, 0, 0); + red = im.colorAllocate(255,0, 0); + blue = im.colorAllocate(0, 0, 255); + # Draw a blue oval + im.arc(50,50,95,75,0,360,blue); + # And fill it with red + im.fill(50,50,red); + # Output image in PNG format + im.png | stdout(1); + END; + -%] + +See L<Template::Plugin::GD::Image> for further details. + +=head2 GD::Text, GD::Text::Align, GD::Text::Wrap + +These plugins provide access to Martien Verbruggen's GD::Text, +GD::Text::Align and GD::Text::Wrap modules. These plugins allow the +layout, alignment and wrapping of text when drawing text in GD images. + + [% FILTER null; + USE gd = GD.Image(200,400); + USE gdc = GD.Constants; + black = gd.colorAllocate(0, 0, 0); + green = gd.colorAllocate(0, 255, 0); + txt = "This is some long text. " | repeat(10); + USE wrapbox = GD.Text.Wrap(gd, + line_space => 4, + color => green, + text => txt, + ); + wrapbox.set_font(gdc.gdMediumBoldFont); + wrapbox.set(align => 'center', width => 160); + wrapbox.draw(20, 20); + gd.png | stdout(1); + END; + -%] + +See L<Template::Plugin::GD::Text>, L<Template::Plugin::GD::Text::Align> +and L<Template::Plugin::GD::Text::Wrap> for further details. + +=head2 GD::Graph::lines, GD::Graph::bars, GD::Graph::points, GD::Graph::linespoin +ts, GD::Graph::area, GD::Graph::mixed, GD::Graph::pie + +These plugins provide access to Martien Verbruggen's GD::Graph module +that allows graphs, plots and charts to be created. These plugins allow +graphs, plots and charts to be generated in PNG, JPEG and other +graphical formats. + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th"], + [ 4, 2, 3, 4, 3, 3.5] + ]; + USE my_graph = GD.Graph.pie(250, 200); + my_graph.set( + title => 'A Pie Chart', + label => 'Label', + axislabelclr => 'black', + pie_height => 36, + transparent => 0, + ); + my_graph.plot(data).png | stdout(1); + END; + -%] + +See +L<Template::Plugin::GD::Graph::lines>, +L<Template::Plugin::GD::Graph::bars>, +L<Template::Plugin::GD::Graph::points>, +L<Template::Plugin::GD::Graph::linespoints>, +L<Template::Plugin::GD::Graph::area>, +L<Template::Plugin::GD::Graph::mixed>, +L<Template::Plugin::GD::Graph::pie>, and +L<GD::Graph>, +for more details. + +=head2 GD::Graph::bars3d, GD::Graph::lines3d, GD::Graph::pie3d + +These plugins provide access to Jeremy Wadsack's GD::Graph3d +module. This allows 3D bar charts and 3D lines plots to +be generated. + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"], + [ 1, 2, 5, 6, 3, 1.5, 1, 3, 4], + ]; + USE my_graph = GD.Graph.bars3d(); + my_graph.set( + x_label => 'X Label', + y_label => 'Y label', + title => 'A 3d Bar Chart', + y_max_value => 8, + y_tick_number => 8, + y_label_skip => 2, + # shadows + bar_spacing => 8, + shadow_depth => 4, + shadowclr => 'dred', + transparent => 0, + my_graph.plot(data).png | stdout(1); + END; + -%] + +See +L<Template::Plugin::GD::Graph::lines3d>, +L<Template::Plugin::GD::Graph::bars3d>, and +L<Template::Plugin::GD::Graph::pie3d> +for more details. + +=head2 HTML + +The HTML plugin is very new and very basic, implementing a few useful +methods for generating HTML. It is likely to be extended in the future +or integrated with a larger project to generate HTML elements in a generic +way (as discussed recently on the mod_perl mailing list). + + [% USE HTML %] + [% HTML.escape("if (a < b && c > d) ..." %] + [% HTML.attributes(border => 1, cellpadding => 2) %] + [% HTML.element(table => { border => 1, cellpadding => 2 }) %] + +See L<Template::Plugin::HTML> for further details. + +=head2 Iterator + +The Iterator plugin provides a way to create a Template::Iterator +object to iterate over a data set. An iterator is created +automatically by the FOREACH directive and is aliased to the 'loop' +variable. This plugin allows an iterator to be explicitly created +with a given name, or the default plugin name, 'iterator'. See +L<Template::Plugin::Iterator> for further details. + + [% USE iterator(list, args) %] + + [% FOREACH item = iterator %] + [% '<ul>' IF iterator.first %] + <li>[% item %] + [% '</ul>' IF iterator.last %] + [% END %] + +=head2 Pod + +This plugin provides an interface to the L<Pod::POM|Pod::POM> module +which parses POD documents into an internal object model which can +then be traversed and presented through the Template Toolkit. + + [% USE Pod(podfile) %] + + [% FOREACH head1 = Pod.head1; + FOREACH head2 = head1/head2; + ... + END; + END + %] + +=head2 String + +The String plugin implements an object-oriented interface for +manipulating strings. See L<Template::Plugin::String> for further +details. + + [% USE String 'Hello' %] + [% String.append(' World') %] + + [% msg = String.new('Another string') %] + [% msg.replace('string', 'text') %] + + The string "[% msg %]" is [% msg.length %] characters long. + +=head2 Table + +The Table plugin allows you to format a list of data items into a +virtual table by specifying a fixed number of rows or columns, with +an optional overlap. See L<Template::Plugin::Table> for further +details. + + [% USE table(list, rows=10, overlap=1) %] + + [% FOREACH item = table.col(3) %] + [% item %] + [% END %] + +=head2 URL + +The URL plugin provides a simple way of contructing URLs from a base +part and a variable set of parameters. See L<Template::Plugin::URL> +for further details. + + [% USE mycgi = url('/cgi-bin/bar.pl', debug=1) %] + + [% mycgi %] + # ==> /cgi/bin/bar.pl?debug=1 + + [% mycgi(mode='submit') %] + # ==> /cgi/bin/bar.pl?mode=submit&debug=1 + +=head2 Wrap + +The Wrap plugin uses the Text::Wrap module by David Muir Sharnoff +E<lt>muir@idiom.comE<gt> (with help from Tim Pierce and many many others) +to provide simple paragraph formatting. See L<Template::Plugin::Wrap> +and L<Text::Wrap> for further details. + + [% USE wrap %] + [% wrap(mytext, 40, '* ', ' ') %] # use wrap sub + [% mytext FILTER wrap(40) -%] # or wrap FILTER + +The Text::Wrap module is available from CPAN: + + http://www.cpan.org/modules/by-module/Text/ + +=head2 XML::DOM + +The XML::DOM plugin gives access to the XML Document Object Module via +Clark Cooper E<lt>cooper@sch.ge.comE<gt> and Enno Derksen's +E<lt>enno@att.comE<gt> XML::DOM module. See L<Template::Plugin::XML::DOM> +and L<XML::DOM> for further details. + + [% USE dom = XML.DOM %] + [% doc = dom.parse(filename) %] + + [% FOREACH node = doc.getElementsByTagName('CODEBASE') %] + * [% node.getAttribute('href') %] + [% END %] + +The plugin requires the XML::DOM module, available from CPAN: + + http://www.cpan.org/modules/by-module/XML/ + +=head2 XML::RSS + +The XML::RSS plugin is a simple interface to Jonathan Eisenzopf's +E<lt>eisen@pobox.comE<gt> XML::RSS module. A RSS (Rich Site Summary) +file is typically used to store short news 'headlines' describing +different links within a site. This plugin allows you to parse RSS +files and format the contents accordingly using templates. +See L<Template::Plugin::XML::RSS> and L<XML::RSS> for further details. + + [% USE news = XML.RSS(filename) %] + + [% FOREACH item = news.items %] + <a href="[% item.link %]">[% item.title %]</a> + [% END %] + +The XML::RSS module is available from CPAN: + + http://www.cpan.org/modules/by-module/XML/ + +=head2 XML::Simple + +This plugin implements an interface to the L<XML::Simple|XML::Simple> +module. + + [% USE xml = XML.Simple(xml_file_or_text) %] + + [% xml.head.title %] + +See L<Template::Plugin::XML::Simple> for further details. + +=head2 XML::Style + +This plugin defines a filter for performing simple stylesheet based +transformations of XML text. + + [% USE xmlstyle + table = { + attributes = { + border = 0 + cellpadding = 4 + cellspacing = 1 + } + } + %] + + [% FILTER xmlstyle %] + <table> + <tr> + <td>Foo</td> <td>Bar</td> <td>Baz</td> + </tr> + </table> + [% END %] + +See L<Template::Plugin::XML::Style> for further details. + +=head2 XML::XPath + +The XML::XPath plugin provides an interface to Matt Sergeant's +E<lt>matt@sergeant.orgE<gt> XML::XPath module. See +L<Template::Plugin::XML::XPath> and L<XML::XPath> for further details. + + [% USE xpath = XML.XPath(xmlfile) %] + [% FOREACH page = xpath.findnodes('/html/body/page') %] + [% page.getAttribute('title') %] + [% END %] + +The plugin requires the XML::XPath module, available from CPAN: + + http://www.cpan.org/modules/by-module/XML/ + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Refs.pod b/lib/Template/Manual/Refs.pod new file mode 100644 index 0000000..b0c9719 --- /dev/null +++ b/lib/Template/Manual/Refs.pod @@ -0,0 +1,171 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Refs +# +# DESCRIPTION +# This section provides references to external modules, projects and +# other resources related to the Template Toolkit. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Refs - Related modules, projects and other resources + +=head1 DESCRIPTION + +This section provides references to external modules, projects and +other resources related to the Template Toolkit. + +=head2 Resources + +The Template Toolkit web site contains the latest information, news and +other resources. + + http://www.template-toolkit.org/ + +A mailing list exists for up-to-date information on the Template Toolkit +and for following and contributing to the development process. To +subscribe, send an email to + + templates-request@template-toolkit.org + +with the message 'subscribe' in the body. You can also use the web +interface to subscribe or browse the archives: + + http://www.template-toolkit.org/mailman/listinfo/templates + +The F<tpage> and F<ttree> scripts are distributed and installed along +with the Template Toolkit. The F<tpage> script simply processes named +files or STDIN if unspecified, using a default Template object. The +F<ttree> script can be used to process entire directory trees of templates, +allowing large content systems such as web sites to be rebuilt from a +single command or configuration file. + + perldoc tpage + perldoc ttree + +The F<Template::Tutorial> document provides an introduction to the Template +Toolkit and shows some typical examples of usage. + + perldoc Template::Tutorial + +You may also like to consult the paper 'Building and Managing Web Systems +with the Template Toolkit' and accompanying slides from the presentation +at the 4th Perl Conference. These are available from the Template +Toolkit web site: + + http://www.template-toolkit.org/docs.html + + + +=head2 Projects + +There are a number of other projects related to the Template Toolkit. + +=over 4 + +=item OpenInteract + +OpenInteract is a robust web application framework built to run under +Apache and mod_perl using the Template Toolkit as a foundation. + + http://www.openinteract.org/ + +=item Apache::Template + +This is an Apache/mod_perl interface to the Template Toolkit. Available +from CPAN in the directory: + + http://www.cpan.org/modules/by-module/Apache/ + +=item AxKit::Template + +AxKit is Matt Sergeant's Apache XML Delivery Toolkit. AxKit::Template +provides an interface between AxKit and the Template Toolkit. Available +from CPAN in the directory: + + http://www.cpan.org/modules/by-module/Apache/ + +=item Slashcode + +Slashcode is the code which runs Slashdot. Version 2 uses the +Template Toolkit for generating the user interface from database +driven template. + + http://slashcode.org/ + +=item OpenFrame + +OpenFrame is an open source application framework for distributed +media applications. It ships with a generator for the Template +Toolkit. + + http://openframe.fotango.com/ + +=item PCMT + +PCMT is the Personal Content Management Toolkit. It uses the Template +Toolkit as the presentation engine. + + http://pcmt.sf.net/ + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Syntax.pod b/lib/Template/Manual/Syntax.pod new file mode 100644 index 0000000..cc1b6c8 --- /dev/null +++ b/lib/Template/Manual/Syntax.pod @@ -0,0 +1,306 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Syntax +# +# DESCRIPTION +# This section describes the syntax, structure and semantics of the +# Template Toolkit directives and general presentation language. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Syntax - Directive syntax, structure and semantics + +=head1 DESCRIPTION + +This section describes the syntax, structure and semantics of the +Template Toolkit directives and general presentation language. + +=head2 Tag Styles + +By default, template directives are embedded within the character sequences +'[%' and '%]'. e.g. + + [% PROCESS header %] + + <h1>Hello World!</h1> + <a href="[% page.next %]"><img src="[% icon.next %].gif"></a> + + [% PROCESS footer %] + +You can change the tag characters using the START_TAG, END_TAG and +TAG_STYLE configuration options. You can also use the TAGS directive +to define a new tag style for the current template file. + +You can also set the INTERPOLATE option to allow simple variable +references to be embedded directly in templates, prefixed by a '$'. + + # INTERPOLATE => 0 + <td>[% name %]</td> <td>[% email %]</td> + + # INTERPOLATE => 1 + <td>$name</td> <td>$email</td> + +Directives may be embedded anywhere in a line of text and can be split +across several lines. Insignificant whitespace is generally ignored +within the directive. + + [% INCLUDE header + title = 'Hello World' + bgcol = '#ffffff' + %] + + [%INCLUDE menu align='right'%] + + Name: [% name %] ([%id%]) + +=head2 Comments + +The '#' character is used to indicate comments within a directive. +When placed immediately inside the opening directive tag, it causes +the entire directive to be ignored. + + [%# this entire directive is ignored no + matter how many lines it wraps onto + %] + +In any other position, it causes the remainder of the current line to +be treated as a comment. + + [% # this is a comment + theta = 20 # so is this + rho = 30 # <aol>me too!</aol> + %] + +=head2 Chomping Whitespace + +You can add '-' or '+' to the immediate start or end of a directive +tag to control the whitespace chomping options. See the PRE_CHOMP and +POST_CHOMP options for further details. + + [% BLOCK foo -%] # remove trailing newline + This is block foo + [%- END %] # remove leading newline + +=head2 Implicit Directives: GET and SET + +The simplest directives are GET and SET which retrieve and update +variable values respectively. The GET and SET keywords are actually +optional as the parser is smart enough to see them for what they +really are (but note the caveat below on using side-effect notation). +Thus, you'll generally see: + + [% SET foo = 10 %] + [% GET foo %] + +written as: + + [% foo = 10 %] + [% foo %] + +You can also express simple logical statements as implicit GET directives: + + [% title or template.title or 'Default Title' %] + + [% mode == 'graphics' ? "Graphics Mode Enabled" : "Text Mode" %] + +All other directives should start with a keyword specified in UPPER +CASE (but see the ANYCASE option). All directives keywords are in +UPPER CASE to make them visually distinctive and to distinguish them +from variables of the same name but different case. It is perfectly +valid, for example, to define a variable called 'stop' which is +entirely separate from the STOP directive. + + [% stop = 'Clackett Lane Bus Depot' %] + + The bus will next stop at [% stop %] # variable + + [% STOP %] # directive + +=head2 Block Directives + +Directives such as FOREACH, WHILE, BLOCK, FILTER, etc., mark the start +of a block which may contain text or other directives up to the +matching END directive. Blocks may be nested indefinitely. The +IF, UNLESS, ELSIF and ELSE directives also define blocks and may be +grouped together in the usual manner. + + [% FOREACH item = [ 'foo' 'bar' 'baz' ] %] + * Item: [% item %] + [% END %] + + [% BLOCK footer %] + Copyright 2000 [% me %] + [% INCLUDE company/logo %] + [% END %] + + [% IF foo %] + [% FOREACH thing = foo.things %] + [% thing %] + [% END %] + [% ELSIF bar %] + [% INCLUDE barinfo %] + [% ELSE %] + do nothing... + [% END %] + +Block directives can also be used in a convenient side-effect notation. + + [% INCLUDE userinfo FOREACH user = userlist %] + + [% INCLUDE debugtxt msg="file: $error.info" + IF debugging %] + + [% "Danger Will Robinson" IF atrisk %] + +versus: + + [% FOREACH user = userlist %] + [% INCLUDE userinfo %] + [% END %] + + [% IF debugging %] + [% INCLUDE debugtxt msg="file: $error.info" %] + [% END %] + + [% IF atrisk %] + Danger Will Robinson + [% END %] + +=head2 Capturing Block Output + +The output of a directive can be captured by simply assigning the directive +to a variable. + + [% headtext = PROCESS header title="Hello World" %] + + [% people = PROCESS userinfo FOREACH user = userlist %] + +This can be used in conjunction with the BLOCK directive for defining large +blocks of text or other content. + + [% poem = BLOCK %] + The boy stood on the burning deck, + His fleece was white as snow. + A rolling stone gathers no moss, + And Keith is sure to follow. + [% END %] + +Note one important caveat of using this syntax in conjunction with side-effect +notation. The following directive does not behave as might be expected: + + [% var = 'value' IF some_condition %] + +In this case, the directive is interpreted as (spacing added for clarity) + + [% var = IF some_condition %] + value + [% END %] + +rather than + + [% IF some_condition %] + [% var = 'value' %] + [% END %] + +The variable is assigned the output of the IF block which returns +'value' if true, but nothing if false. In other words, the following +directive will always cause 'var' to be cleared. + + [% var = 'value' IF 0 %] + +To achieve the expected behaviour, the directive should be written as: + + [% SET var = 'value' IF some_condition %] + +=head2 Chaining Filters + +Multiple FILTER directives can be chained together in sequence. They +are called in the order defined, piping the output of one into the +input of the next. + + [% PROCESS somefile FILTER truncate(100) FILTER html %] + +The pipe character, '|', can also be used as an alias for FILTER. + + [% PROCESS somefile | truncate(100) | html %] + +=head2 Multiple Directive Blocks + +Multiple directives can be included within a single tag when delimited +by semi-colons, ';'. Note however that the TAGS directive must always +be specified in a tag by itself. + + [% IF title; + INCLUDE header; + ELSE; + INCLUDE other/header title="Some Other Title"; + END + %] + +versus + + [% IF title %] + [% INCLUDE header %] + [% ELSE %] + [% INCLUDE other/header title="Some Other Title" %] + [% END %] + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/VMethods.pod b/lib/Template/Manual/VMethods.pod new file mode 100644 index 0000000..7e380fa --- /dev/null +++ b/lib/Template/Manual/VMethods.pod @@ -0,0 +1,529 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::VMethods +# +# DESCRIPTION +# The Template Toolkit provides virtual methods for manipulating +# variable values. Most of them are analogous to regular Perl +# functions of the same names. This section describes the different +# virtual methods that can be applied to scalar, list and hash +# values. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::VMethods - Virtual Methods + +=head1 DESCRIPTION + +The Template Toolkit provides virtual methods for manipulating variable +values. Most of them are analogous to regular Perl functions of the +same names. This section describes the different virtual methods that +can be applied to scalar, list and hash values. + +=head2 Scalar Virtual Methods + +=over 4 + +=item defined + +Returns true if the value is defined. + + [% user = get_user(uid) IF uid.defined %] + +=item length + +Returns the length of the string representation of the item: + + [% IF password.length < 8 %] + Password too short, dumbass! + [% END %] + +=item repeat(n) + +Repeat the string a specified number of times. + + [% name = 'foo' %] + [% name.repeat(3) %] # foofoofoo + +=item replace(search, replace) + +Outputs the string with all instances of the first argument (specified +as a Perl regular expression) with the second. + + [% name = 'foo, bar & baz' %] + [% name.replace('\W+', '_') %] # foo_bar_baz + +=item match(pattern) + +Performs a regular expression match on the string using the pattern +passed as an argument. If the pattern matches the string then the +method returns a reference to a list of any strings captured within +parenthesis in the pattern. + + [% name = 'Larry Wall' %] + [% matches = name.match('(\w+) (\w+)') %] + [% matches.1 %], [% matches.0 %] # Wall, Larry + +If the pattern does not match then the method returns false, rather +than returning an empty list which Perl and the Template Toolkit both +consider to be a true value. This allows you to write expression like +this. + + [% "We're not worthy!" IF name.match('Larry Wall') %] + + [% IF (matches = name.match('(\w+) (\w+)')) %] + pattern matches: [% matches.join(', ') %] + [% ELSE %] + pattern does not match + [% END %] + +Any regex modifiers, like C</s>, should be added in the regex using +the C<(?s)> syntax. For example, to modify the regex to disregard +whitespace (the C</x> switch), use: + + [% re = '(?x) + (\w+) + [ ] + (\w+) + '; + matches = name.match(re); + %] + +=item search(pattern) + +Performs a similar function to 'match' but simply returns true if the +string matches the regular expression pattern passed as an argument. + + [% name = 'foo bar baz' %] + [% name.search('bar') ? 'bar' : 'no bar' %] # bar + +This virtual method is now deprecated in favour of 'match'. Move along +now, there's nothing more to see here. + +=item split(pattern) + +Calls Perl's split() function to split a string into a list of +strings. + + [% FOREACH dir = mypath.split(':') %] + [% dir %] + [% END %] + +=item chunk(size) + +Splits the value into a list of chunks of a certain size. + + [% ccard_no = "1234567824683579"; + ccard_no.chunk(4).join + %] + +Output: + + 1234 5678 2468 3579 + +If the size is specified as a negative number then the text will +be chunked from right-to-left. This gives the correct grouping +for numbers, for example. + + [% number = 1234567; + number.chunk(-3).join(',') + %] + +Output: + + 1,234,567 + +=item list + +Return the value as a single element list. This can be useful if you +have a variable which may contain a single item or a list and you want +to treat them equally. The 'list' method can be called against a list +reference and will simply return the original reference, effectively +a no-op. + + [% thing.list.size %] # thing can be a scalar or a list + +=item hash + +Return the value as a hash reference containing a single entry with +the key 'value' indicating the original scalar value. As with the +'list' virtual method, this is generally used to help massage data +into different formats. + +=item size + +Always returns 1 for scalar values. This method is provided for +consistency with the hash and list size methods. + +=back + + +=head2 Hash Virtual Methods + +=over 4 + +=item keys, values, each + +The regular hash operators returning lists of keys, values or both. +Note how we use a '$' prefix on the 'key' variable in this example to +have it interpolated (i.e. replaced with its value) before use. + + [% FOREACH key = product.keys %] + [% key %] => [% product.$key %] + [% END %] + +=item sort, nsort + +Return a list of the keys, sorted alphabetically (sort) or numerically +(nsort) according to the corresponding values in the hash. + + [% FOREACH n = phones.sort %] + [% phones.$n %] is [% n %], + [% END %] + +=item import + +The import method can be called on a hash array to import the contents +of another hash array. + + [% hash1 = { + foo => 'Foo', + bar => 'Bar', + } + hash2 = { + wiz => 'Wiz', + woz => 'Woz', + } + %] + + [% hash1.import(hash2) %] + [% hash1.wiz %] # Wiz + +You can also call the import() method by itself to import a hash array +into the current namespace hash. + + [% user = { id => 'lwall', name => 'Larry Wall' } %] + [% import(user) %] + [% id %]: [% name %] # lwall: Larry Wall + +=item defined, exists + +Returns a true or false value if an item in the hash denoted by the key +passed as an argument is defined or exists, respectively. + + [% hash.defined('somekey') ? 'yes' : 'no' %] + [% hash.exists('somekey') ? 'yes' : 'no' %] + +=item size + +Returns the number of key =E<gt> value pairs in the hash. + +=item item + +Returns an item from the hash using a key passed as an argument. + + [% hash.item('foo') %] # same as hash.foo + +=item list + +Returns the contents of the hash in list form. An argument can be +passed to indicate the desired items required in the list: 'keys' to +return a list of the keys (same as hash.keys), 'values' to return a +list of the values (same as hash.values), or 'each' to return as list +of (key, value) pairs (same as hash.each). When called without an +argument it returns a list of hash references, each of which contains +a 'key' and 'value' item representing a single key =E<gt> value pair +in the hash. + +=back + + +=head2 List Virtual Methods + +=over 4 + +=item first, last + +Returns the first/last item in the list. The item is not removed from the +list. + + [% results.first %] to [% results.last %] + +If either is given a numeric argument C<n>, they return the first or +last C<n> elements: + + The first 5 results are [% results.first(5).join(", ") %]. + +=item size, max + +Returns the size of a list (number of elements) and the maximum +index number (size - 1), respectively. + + [% results.size %] search results matched your query + +=item reverse + +Returns the items of the list in reverse order. + + [% FOREACH s = scores.reverse %] + ... + [% END %] + +=item join + +Joins the items in the list into a single string, using Perl's join +function. + + [% items.join(', ') %] + +=item grep + +Returns a list of the items in the list that match a regular expression +pattern. + + [% FOREACH directory.files.grep('\.txt$') %] + ... + [% END %] + +=item sort, nsort + +Returns the items in alpha (sort) or numerical (nsort) order. + + [% library = books.sort %] + +An argument can be provided to specify a search key. Where an item in +the list is a hash reference, the search key will be used to retrieve a +value from the hash which will then be used as the comparison value. +Where an item is an object which implements a method of that name, the +method will be called to return a comparison value. + + [% library = books.sort('author') %] + +In the example, the 'books' list can contains hash references with +an 'author' key or objects with an 'author' method. + +=item unshift(item), push(item) + +Adds an item to the start/end of a list. + + [% mylist.unshift('prev item') %] + [% mylist.push('next item') %] + +=item shift, pop + +Removes the first/last item from the list and returns it. + + [% first = mylist.shift %] + [% last = mylist.pop %] + +=item unique + +Returns a list of the unique elements in a list, in the same order +as in the list itself. + + [% mylist = [ 1, 2, 3, 2, 3, 4, 1, 4, 3, 4, 5 ] %] + [% numbers = mylist.unique %] + +While this can be explicitly sorted, it is not required that the list +be sorted before the unique elements are pulled out (unlike the Unix +command line utility). + + [% numbers = mylist.unique.sort %] + +=item merge + +Returns a list composed of zero or more other lists: + + [% list_one = [ 1 2 3 ]; + list_two = [ 4 5 6 ]; + list_three = [ 7 8 9 ]; + list_four = list_one.merge(list_two, list_three); + %] + +The original lists are not modified. + +=item slice(from, to) + +Returns a slice of items in the list between the bounds passed as +arguments. If the second argument, 'to', isn't specified, then it +defaults to the last item in the list. The original list is not +modified. + + [% first_three = list.slice(0,2) %] + + [% last_three = list.slice(-3, -1) %] + +=item splice(offset, length, list) + +Behaves just like Perl's splice() function allowing you to selectively +remove and/or replace elements in a list. It removes 'length' items +from the list, starting at 'offset' and replaces them with the items +in 'list'. + + [% play_game = [ 'play', 'scrabble' ]; + ping_pong = [ 'ping', 'pong' ]; + redundant = play_game.splice(1, 1, ping_pong); + + redundant.join; # scrabble + play_game.join; # play ping pong + %] + +The method returns a list of the items removed by the splice. +You can use the CALL directive to ignore the output if you're +not planning to do anything with it. + + [% CALL play_game.splice(1, 1, ping_pong) %] + +As well as providing a reference to a list of replacement values, +you can pass in a list of items. + + [% CALL list.splice(-1, 0, 'foo', 'bar') %] + +Be careful about passing just one item in as a replacement value. +If it is a reference to a list then the contents of the list will +be used. If it's not a list, then it will be treated as a single +value. You can use square brackets around a single item if you +need to be explicit: + + [% # push a single item, an_item + CALL list.splice(-1, 0, an_item); + + # push the items from another_list + CALL list.splice(-1, 0, another_list); + + # push a reference to another_list + CALL list.splice(-1, 0, [ another_list ]); + %] + +=back + +=head2 Automagic Promotion of Scalar to List for Virtual Methods + +In addition to the scalar virtual methods listed in the previous +section, you can also call any list virtual method against a scalar. +The item will be automagically promoted to a single element list and +the appropriate list virtual method will be called. + +One particular benefit of this comes when calling subroutines or +object methods that return a list of items, rather than the +preferred reference to a list of items. In this case, the +Template Toolkit automatically folds the items returned into +a list. + +The upshot is that you can continue to use existing Perl modules or +code that returns lists of items, without having to refactor it +just to keep the Template Toolkit happy (by returning references +to list). Class::DBI module is just one example of a particularly +useful module which returns values this way. + +If only a single item is returned from a subroutine then the +Template Toolkit assumes it meant to return a single item (rather +than a list of 1 item) and leaves it well alone, returning the +single value as it is. If you're executing a database query, +for example, you might get 1 item returned, or perhaps many +items which are then folded into a list. + +The FOREACH directive will happily accept either a list or a single +item which it will treat as a list. So it's safe to write directives +like this, where we assume that 'something' is bound to a subroutine +which might return 1 or more items: + + [% FOREACH item = something %] + ... + [% END %] + +The automagic promotion of scalars to single item lists means +that you can also use list virtual methods safely, even if you +only get one item returned. For example: + + [% something.first %] + [% something.join %] + [% something.reverse.join(', ') %] + +Note that this is very much a last-ditch behaviour. If the single +item return is an object with a 'first' method, for example, then that +will be called, as expected, in preference to the list virtual method. + +=head2 Defining Custom Virtual Methods + +You can define your own virtual methods for scalars, lists and hash +arrays. The Template::Stash package variables $SCALAR_OPS, $LIST_OPS +and $HASH_OPS are references to hash arrays that define these virtual +methods. HASH_OPS and LIST_OPS methods are subroutines that accept a +hash/list reference as the first item. SCALAR_OPS are subroutines +that accept a scalar value as the first item. Any other arguments +specified when the method is called will be passed to the subroutine. + + # load Template::Stash to make method tables visible + use Template::Stash; + + # define list method to return new list of odd numbers only + $Template::Stash::LIST_OPS->{ odd } = sub { + my $list = shift; + return [ grep { $_ % 2 } @$list ]; + }; + +template: + + [% primes = [ 2, 3, 5, 7, 9 ] %] + [% primes.odd.join(', ') %] # 3, 5, 7, 9 + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Variables.pod b/lib/Template/Manual/Variables.pod new file mode 100644 index 0000000..e8d998c --- /dev/null +++ b/lib/Template/Manual/Variables.pod @@ -0,0 +1,868 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Variables +# +# DESCRIPTION +# This section describes the different ways in which Perl data can be +# bound to template variables and accessed via Template Toolkit +# directives. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Variables - Template variables and code bindings + +=head1 DESCRIPTION + +This section describes the different ways in which Perl data can be +bound to template variables and accessed via Template Toolkit +directives. + +=head2 Template Variables + +A reference to a hash array may be passed as the second argument to +the process() method, containing definitions of template variables. +The VARIABLES (a.k.a. PRE_DEFINE) option can also be used to pre-define +variables for all templates processed by the object. + + my $tt = Template->new({ + VARIABLES => { + version => 3.14, + release => 'Sahara', + }, + }); + + my $vars = { + serial_no => 271828, + }; + + $tt->process('myfile', $vars); + +'myfile': + + This is version [% version %] ([% release %]). + Serial number: [% serial_no %] + +output: + + This is version 3.14 (Sahara) + Serial number: 271828 + +Variable names may contain any alphanumeric characters or underscores. +They may be lower, upper or mixed case although the usual convention +is to use lower case. The case I<is> significant however, and 'foo', +'Foo' and 'FOO' are all different variables. Upper case variable +names are permitted, but not recommended due to a possible conflict +with an existing or future reserved word. As of version 2.00, these +are: + + GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER + IF UNLESS ELSE ELSIF FOR FOREACH WHILE SWITCH CASE + USE PLUGIN FILTER MACRO PERL RAWPERL BLOCK META + TRY THROW CATCH FINAL NEXT LAST BREAK RETURN STOP + CLEAR TO STEP AND OR NOT MOD DIV END + + +The variable values may be of virtually any Perl type, including +simple scalars, references to lists, hash arrays, subroutines or +objects. The Template Toolkit will automatically apply the correct +procedure to accessing these values as they are used in the template. + +Example: + + my $vars = { + article => 'The Third Shoe', + person => { + id => 314, + name => 'Mr. Blue', + email => 'blue@nowhere.org', + }, + primes => [ 2, 3, 5, 7, 11, 13 ], + wizard => sub { return join(' ', 'Abracadabra!', @_) }, + cgi => CGI->new('mode=submit&debug=1'), + }; + +template: + + [% article %] + + [% person.id %]: [% person.name %] <[% person.email %]> + + [% primes.first %] - [% primes.last %], including [% primes.3 %] + [% primes.size %] prime numbers: [% primes.join(', ') %] + + [% wizard %] + [% wizard('Hocus Pocus!') %] + + [% cgi.param('mode') %] + +output: + + The Third Shoe + + 314: Mr. Blue <blue@nowhere.org> + + 2 - 13, including 7 + 6 prime numbers: 2, 3, 5, 7, 11, 13 + + Abracadabra! + Abracadabra! Hocus Pocus! + + submit + +=head2 Scalar Values + +Regular scalar variables are accessed by simply specifying their name. +As these are just entries in the top-level variable hash they can be +considered special cases of hash array referencing as described below, +with the main namespace hash automatically implied. + + [% article %] + +=head2 Hash Array References + +Members of hash arrays are accessed by specifying the hash reference +and key separated by the dot '.' operator. + + my $vars = { + 'home' => 'http://www.myserver.com/homepage.html', + 'page' => { + 'this' => 'mypage.html', + 'next' => 'nextpage.html', + 'prev' => 'prevpage.html', + }, + }; + +template: + + <a href="[% home %]">Home</a> + <a href="[% page.prev %]">Previous Page</a> + <a href="[% page.next %]">Next Page</a> + +output: + + <a href="http://www.myserver.com/homepage.html">Home</a> + <a href="prevpage.html">Previous Page</a> + <a href="nextpage.html">Next Page</a> + +Any key in a hash which starts with a '_' or '.' character will be +considered private and cannot be evaluated or updated from within a +template. The undefined value will be returned for any such variable +accessed which the Template Toolkit will silently ignore (unless the +DEBUG option is enabled). + + my $vars = { + message => 'Hello World!', + _secret => "On the Internet, no-one knows you're a dog", + thing => { + public => 123, + _private => 456, + '.hidden' => 789, + }, + }; + +template: + + [% message %] # outputs "Hello World!" + [% _secret %] # no output + [% thing.public %] # outputs "123" + [% thing._private %] # no output + [% thing..hidden %] # ERROR: unexpected token (..) + +To access a hash entry using a key stored in another variable, prefix +the key variable with '$' to have it interpolated before use (see +L<Variable Interpolation>). + + [% pagename = 'next' %] + [% page.$pagename %] # same as [% page.next %] + +When you assign to a variable that contains multiple namespace +elements (i.e. it has one or more '.' characters in the name), +any hashes required to represent intermediate namespaces will be +created automatically. In this following example, the 'product' +variable automatically springs into life as a hash array unless +otherwise defined. + + [% product.id = 'XYZ-2000' + product.desc = 'Bogon Generator' + product.price = 666 + %] + + The [% product.id %] [% product.desc %] + costs $[% product.price %].00 + +output: + + The XYZ-2000 Bogon Generator + costs $666.00 + +You can use Perl's familiar '{' ... '}' construct to explicitly create +a hash and assign it to a variable. Note that commas are optional +between key/value pairs and '=' can be used in place of '=E<gt>'. + + [% product = { + id => 'XYZ-2000', + desc => 'Bogon Generator', + price => 666, + } + %] + +=head2 List References + +Items in lists are also accessed by use of the dot operator. + + my $vars = { + 'people' => [ 'Tom', 'Dick', 'Larry' ], + }; + +template: + + [% people.0 %] # Tom + [% people.1 %] # Dick + [% people.2 %] # Larry + +The FOREACH directive can be used to iterate through items in a list. + + [% FOREACH person = people %] + Hello [% person %] + [% END %] + +output: + + Hello Tom + Hello Dick + Hello Larry + +Lists can be constructed in-situ using the regular anonymous list +'[' ... ']' construct. Commas between items are optional. + + [% cols = [ 'red', 'green', 'blue' ] %] + + [% FOREACH c = cols %] + ... + +or: + + [% FOREACH c = [ 'red', 'green', 'blue' ] %] + ... + +You can also create simple numerical sequences using the familiar '..' +operator: + + [% n = [ 1 .. 4 ] %] # n is [ 1, 2, 3, 4 ] + + [% x = 4 + y = 8 + z = [x..y] # z is [ 4, 5, 6, 7, 8 ] + %] + +=head2 Subroutines + +Template variables can contain references to Perl subroutines. When +the variable is used, the Template Toolkit will automatically call the +subroutine, passing any additional arguments specified. The return +value from the subroutine is used as the variable value and inserted +into the document output. + + my $vars = { + wizard => sub { return join(' ', 'Abracadabra!', @_) }, + }; + +template: + + [% wizard %] # Abracadabra! + [% wizard('Hocus Pocus!') %] # Abracadabra! Hocus Pocus! + + +=head2 Objects + +Template variables can also contain references to Perl objects. +Methods are called using the dot operator to specify the method +against the object variable. Additional arguments can be specified +as with subroutines. + + use CGI; + + ... + + my $vars = { + # hard coded CGI params for purpose of example + cgi => CGI->new('mode=submit&debug=1'), + }; + +template: + + [% FOREACH p = cgi.param %] # returns list of param keys + [% p %] => [% cgi.param(p) %] # fetch each param value + [% END %] + +output: + + mode => submit + debug => 1 + +Object methods can also be called as lvalues. That is, they can appear on +the left side of an assignment. The method will be called passing the +assigning value as an argument. + + [% myobj.method = 10 %] + +equivalent to: + + [% myobj.method(10) %] + +=head2 Parameters and Return Values + +Subroutines and methods will be passed any arguments specified in the +template. Any template variables in the argument list will first be +evaluated and their resultant values passed to the code. + + my $vars = { + mycode => sub { return 'received ' . join(', ', @_) }, + }; + +template: + + [% foo = 10 %] + [% mycode(foo, 20) %] # received 10, 20 + +Named parameters may also be specified. These are automatically collected +into a single hash array which is passed by reference as the B<last> +parameter to the sub-routine. Named parameters can be specified using +either '=E<gt>' or '=' and can appear anywhere in the argument list. + + my $vars = { + myjoin => \&myjoin, + }; + + sub myjoin { + # look for hash ref as last argument + my $params = ref $_[-1] eq 'HASH' ? pop : { }; + return join($params->{ joint } || ' + ', @_); + } + +template: + + [% myjoin(10, 20, 30) %] + [% myjoin(10, 20, 30, joint = ' - ' %] + [% myjoin(joint => ' * ', 10, 20, 30 %] + +output: + + 10 + 20 + 30 + 10 - 20 - 30 + 10 * 20 * 30 + +Parenthesised parameters may be added to any element of a variable, +not just those that are bound to code or object methods. At present, +parameters will be ignored if the variable isn't "callable" but are +supported for future extensions. Think of them as "hints" to that +variable, rather than just arguments passed to a function. + + [% r = 'Romeo' %] + [% r(100, 99, s, t, v) %] # outputs "Romeo" + +User code should return a value for the variable it represents. This +can be any of the Perl data types described above: a scalar, or +reference to a list, hash, subroutine or object. Where code returns a +list of multiple values the items will automatically be folded into a +list reference which can be accessed as per normal. + + my $vars = { + # either is OK, first is recommended + items1 => sub { return [ 'foo', 'bar', 'baz' ] }, + items2 => sub { return ( 'foo', 'bar', 'baz' ) }, + }; + +template: + + [% FOREACH i = items1 %] + ... + [% END %] + + [% FOREACH i = items2 %] + ... + [% END %] + +=head2 Error Handling + +Errors can be reported from user code by calling die(). Errors raised +in this way are caught by the Template Toolkit and converted to +structured exceptions which can be handled from within the template. +A reference to the exception object is then available as the 'error' +variable. + + my $vars = { + barf => sub { + die "a sick error has occurred\n"; + }, + }; + +template: + + [% TRY %] + [% barf %] # calls sub which throws error via die() + [% CATCH %] + [% error.info %] # outputs "a sick error has occurred\n" + [% END %] + +Error messages thrown via die() are converted to exceptions of type +'undef'. Exceptions of user-defined types can be thrown by calling +die() with a reference to a Template::Exception object. + + use Template::Exception; + + ... + + my $vars = { + login => sub { + ... + die Template::Exception->new('badpwd', + 'password too silly'); + }, + }; + +template: + + [% TRY %] + [% login %] + [% CATCH badpwd %] + Bad password: [% error.info %] + [% CATCH %] + Some other '[% error.type %]' error: [% error.info %] + [% END %] + +The exception types 'stop' and 'return' are used to implement the +STOP and RETURN directives. Throwing an exception as: + + die (Template::Exception->new('stop')); + +has the same effect as the directive: + + [% STOP %] + +Subroutines and methods can also raise errors by returning a list or +reference to a list containing the undefined value (undef) followed by +an exception object or error message. This is supported for backwards +compatibility with version 1 but may be deprecated in some future +version. + + my $vars = { + # currently equivalent + barf => sub { + die "I'm sorry Dave, I can't do that"; + }, + yack => sub { + return (undef, "I'm sorry Dave, I can't do that"); + }, + }; + +=head2 Virtual Methods + +The Template Toolkit implements a number of "virtual methods" which +can be applied to scalars, hashes or lists. For example: + + [% mylist = [ 'foo', 'bar', 'baz' ] %] + [% newlist = mylist.sort %] + +Here 'mylist' is a regular reference to a list, and 'sort' is +a virtual method that returns a new list of the items in sorted +order. You can chain multiple virtual methods together. For +example: + + [% mylist.sort.join(', ') %] + +Here the 'join' virtual method is called to join the sorted list into +a single string, generating the following output: + + bar, baz, foo + +See L<Template::Manual::VMethods> for details of all the virtual +methods available. + +=head2 Variable Interpolation + +The Template Toolkit uses '$' consistently to indicate that a variable +should be interpolated in position. Most frequently, you see this in +double-quoted strings: + + [% fullname = "$honorific $firstname $surname" %] + +Or embedded in plain text when the INTERPOLATE option is set: + + Dear $honorific $firstname $surname, + +The same rules apply within directives. If a variable is prefixed +with a '$' then it is replaced with its value before being used. The +most common use is to retrieve an element from a hash where the key is +stored in a variable. + + [% uid = 'abw' %] + [% userlist.$uid %] # same as 'userlist.abw' + +Curly braces can be used to delimit interpolated variable names where +necessary. + + [% userlist.${me.id}.name %] + +Directives such as INCLUDE, PROCESS, etc., that accept a template name +as the first argument, will automatically quote it for convenience. + + [% INCLUDE foo/bar.txt %] + +equivalent to: + + [% INCLUDE "foo/bar.txt" %] + +To INCLUDE a template whose name is stored in a variable, simply +prefix the variable name with '$' to have it interpolated. + + [% myfile = 'header' %] + [% INCLUDE $myfile %] + +equivalent to: + + [% INCLUDE header %] + +Note also that a variable containing a reference to a Template::Document +object can also be processed in this way. + + my $vars = { + header => Template::Document->new({ ... }), + }; + +template: + + [% INCLUDE $header %] + +=head2 Local and Global Variables + +Any simple variables that you create, or any changes you make to +existing variables, will only persist while the template is being +processed. The top-level variable hash is copied before processing +begins and any changes to variables are made in this copy, leaving the +original intact. The same thing happens when you INCLUDE another +template. The current namespace hash is cloned to prevent any +variable changes made in the included template from interfering with +existing variables. The PROCESS option bypasses the localisation step +altogether making it slightly faster, but requiring greater attention +to the possibility of side effects caused by creating or changing any +variables within the processed template. + + [% BLOCK change_name %] + [% name = 'bar' %] + [% END %] + + [% name = 'foo' %] + [% INCLUDE change_name %] + [% name %] # foo + [% PROCESS change_name %] + [% name %] # bar + +Dotted compound variables behave slightly differently because the +localisation process is only skin deep. The current variable +namespace hash is copied, but no attempt is made to perform a +deep-copy of other structures within it (hashes, arrays, objects, +etc). A variable referencing a hash, for example, will be copied to +create a new reference but which points to the same hash. Thus, the +general rule is that simple variables (undotted variables) are +localised, but existing complex structures (dotted variables) are not. + + [% BLOCK all_change %] + [% x = 20 %] # changes copy + [% y.z = 'zulu' %] # changes original + [% END %] + + [% x = 10 + y = { z => 'zebra' } + %] + [% INCLUDE all_change %] + [% x %] # still '10' + [% y.z %] # now 'zulu' + + +If you create a complex structure such as a hash or list reference +within a local template context then it will cease to exist when +the template is finished processing. + + [% BLOCK new_stuff %] + [% # define a new 'y' hash array in local context + y = { z => 'zulu' } + %] + [% END %] + + [% x = 10 %] + [% INCLUDE new_stuff %] + [% x %] # outputs '10' + [% y %] # nothing, y is undefined + +Similarly, if you update an element of a compound variable which +I<doesn't> already exists then a hash will be created automatically +and deleted again at the end of the block. + + [% BLOCK new_stuff %] + [% y.z = 'zulu' %] + [% END %] + +However, if the hash I<does> already exist then you will modify the +original with permanent effect. To avoid potential confusion, it is +recommended that you don't update elements of complex variables from +within blocks or templates included by another. + +If you want to create or update truly global variables then you can +use the 'global' namespace. This is a hash array automatically created +in the top-level namespace which all templates, localised or otherwise +see the same reference to. Changes made to variables within this +hash are visible across all templates. + + [% global.version = 123 %] + +=head2 Compile Time Constant Folding + +In addition to variables that get resolved each time a template is +processed, you can also define variables that get resolved just once +when the template is compiled. This generally results in templates +processing faster because there is less work to be done. + +To define compile-time constants, specify a CONSTANTS hash as a +constructor item as per VARIABLES. The CONSTANTS hash can contain any +kind of complex, nested, or dynamic data structures, just like regular +variables. + + my $tt = Template->new({ + CONSTANTS => { + version => 3.14, + release => 'skyrocket', + col => { + back => '#ffffff', + fore => '#000000', + }, + myobj => My::Object->new(), + mysub => sub { ... }, + joint => ', ', + }, + }); + +Within a template, you access these variables using the 'constants' +namespace prefix. + + Version [% constants.version %] ([% constants.release %]) + + Background: [% constants.col.back %] + +When the template is compiled, these variable references are replaced +with the corresponding value. No further variable lookup is then +required when the template is processed. + +You can call subroutines, object methods, and even virtual methods on +constant variables. + + [% constants.mysub(10, 20) %] + [% constants.myobj(30, 40) %] + [% constants.col.keys.sort.join(', ') %] + +One important proviso is that any arguments you pass to subroutines +or methods must also be literal values or compile time constants. + +For example, these are both fine: + + # literal argument + [% constants.col.keys.sort.join(', ') %] + + # constant argument + [% constants.col.keys.sort.join(constants.joint) %] + +But this next example will raise an error at parse time because +'joint' is a runtime variable and cannot be determined at compile +time. + + # ERROR: runtime variable argument! + [% constants.col.keys.sort.join(joint) %] + +The CONSTANTS_NAMESPACE option can be used to provide a different +namespace prefix for constant variables. For example: + + my $tt = Template->new({ + CONSTANTS => { + version => 3.14, + # ...etc... + }, + CONSTANTS_NAMESPACE => 'const', + }); + +Constants would then be referenced in templates as: + + [% const.version %] + +=head2 Special Variables + +A number of special variables are automatically defined by the Template +Toolkit. + +=over 4 + +=item template + +The 'template' variable contains a reference to the main template +being processed, in the form of a Template::Document object. This +variable is correctly defined within PRE_PROCESS, PROCESS and +POST_PROCESS templates, allowing standard headers, footers, etc., to +access metadata items from the main template. The 'name' and +'modtime' metadata items are automatically provided, giving the +template name and modification time in seconds since the epoch. + +Note that the 'template' variable always references the top-level +template, even when processing other template components via INCLUDE, +PROCESS, etc. + +=item component + +The 'component' variable is like 'template' but always contains a +reference to the current, innermost template component being processed. +In the main template, the 'template' and 'component' variable will +reference the same Template::Document object. In any other template +component called from the main template, the 'template' variable +will remain unchanged, but 'component' will contain a new reference +to the current component. + +This example should demonstrate the difference: + + $template->process('foo') + || die $template->error(), "\n"; + +'foo': + + [% template.name %] # foo + [% component.name %] # foo + [% PROCESS footer %] + +'footer': + + [% template.name %] # foo + [% component.name %] # footer + +=item loop + +Within a FOREACH loop, the 'loop' variable references the Template::Iterator +object responsible for controlling the loop. + + [% FOREACH item = [ 'foo', 'bar', 'baz' ] -%] + [% "Items:\n" IF loop.first -%] + [% loop.count %]/[% loop.size %]: [% item %] + [% END %] + +=item error + +Within a CATCH block, the 'error' variable contains a reference to the +Template::Exception object thrown from within the TRY block. The +'type' and 'info' methods can be called or the variable itself can +be printed for automatic stringification into a message of the form +"$type error - $info". See L<Template::Exception> for further details. + + [% TRY %] + ... + [% CATCH %] + [% error %] + [% END %] + +=item content + +The WRAPPER method captures the output from a template block and then +includes a named template, passing the captured output as the 'content' +variable. + + [% WRAPPER box %] + Be not afeard; the isle is full of noises, + Sounds and sweet airs, that give delight and hurt not. + [% END %] + + [% BLOCK box %] + <table border=1> + <tr> + <td> + [% content %] + </td> + </tr> + </table> + [% END %] + +=back + +=head2 Compound Variables + +Compound 'dotted' variables may contain any number of separate +elements. Each element may evaluate to any of the permitted variable +types and the processor will then correctly use this value to evaluate +the rest of the variable. Arguments may be passed to any of the +intermediate elements. + + [% myorg.people.sort('surname').first.fullname %] + +Intermediate variables may be used and will behave entirely as expected. + + [% sorted = myorg.people.sort('surname') %] + [% sorted.first.fullname %] + +This simplified dotted notation has the benefit of hiding the +implementation details of your data. For example, you could implement +a data structure as a hash array one day and then change it to an +object the next without requiring any change to the templates. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Manual/Views.pod b/lib/Template/Manual/Views.pod new file mode 100644 index 0000000..7bc53b0 --- /dev/null +++ b/lib/Template/Manual/Views.pod @@ -0,0 +1,642 @@ +#============================================================= -*-perl-*- +# +# Template::Manual::Views +# +# DESCRIPTION +# This section describes dynamic views: a powerful but experimental +# new feature in version 2.01 of the Template Toolkit. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Manual::Views - Template Toolkit views (experimental) + +=head1 DESCRIPTION + +This section describes dynamic views: a powerful but experimental new +feature in version 2.01 of the Template Toolkit. + +A view is effectively a collection of templates and/or variable +definitions which can be passed around as a self-contained unit. This +then represents a particular interface or presentation style for other +objects or items of data. + +You can use views to implement custom "skins" for an application or +content set. You can use them to help simplify the presentation of +common objects or data types. You can even use then to automate the +presentation of complex data structures such as that generated in an +XML::DOM tree or similar. You let an iterator do the walking, and the +view does the talking (or in this case, the presenting). Voila - you +have view independant, structure shy traversal using templates. + +In general, views can be used in a number of different ways to achieve +several different things. They elegantly solve some problems which +were otherwise difficult or complicated, and make easy some things +that were previously hard. + +At the moment, they're still very experimental. The directive syntax +and underlying API are likely to change quite considerably over the +next version or two. Please be very wary about building your +multi-million dollar e-commerce solutions based around this feature. + +=head2 Views as Template Collectors/Providers + +The VIEW directive starts a view definition and includes a name by +which the view can be referenced. The view definition continues up to +the matching END directive. + + [% VIEW myview %] + ... + [% END %] + +The first role of a view is to act as a collector and provider of templates. +The include() method can be called on a view to effectively do the same +thing as the INCLUDE directive. The template name is passed as the first +argument, followed by any local variable definitions for the template. + + [% myview.include('header', title='The Title') %] + + # equivalent to + [% INCLUDE header title='The Title' %] + +Views accept a number of configuration options which can be used to control +different aspects of their behaviour. The 'prefix' and 'suffix' options +can be specified to add a fixed prefix and/or suffix to the name of each template. + + [% VIEW myview + prefix = 'my/' + suffix = '.tt2' ; + END + %] + +Now the call + + [% myview.include('header', title='The Title') %] + +is equivalent to + + [% INCLUDE my/header.tt2 title='The Title' %] + +Views provide an AUTOLOAD method which maps method names to the +include() method. Thus, the following are all equivalent: + + [% myview.include('header', title='Hello World') %] + [% myview.include_header(title='Hello World') %] + [% myview.header(title='Hello World') %] + +=head2 Local BLOCK Definitions + +A VIEW definition can include BLOCK definitions which remain local to +the view. A request for a particular template will return a BLOCK, +if defined, in preference to any other template of the same name. + + [% BLOCK foo %] + public foo block + [% END %] + + [% VIEW plain %] + [% BLOCK foo %] + plain foo block + [% END %] + [% END %] + + [% VIEW fancy %] + [% BLOCK foo %] + fancy foo block + [% END %] + [% END %] + + [% INCLUDE foo %] # public foo block + [% plain.foo %] # plain foo block + [% fancy.foo %] # fancy foo block + +In addition to BLOCK definitions, a VIEW can contain any other +template directives. The entire VIEW definition block is processed to +initialise the view but no output is generated (this may change RSN - +and get stored as 'output' item, subsequently accessible as [% +view.output %]). However, directives that have side-effects, such as +those that update a variable, will have noticable consequences. + +=head2 Preserving Variable State within Views + +Views can also be used to save the values of any existing variables, +or to create new ones at the point at which the view is defined. +Unlike simple template metadata (META) which can only contain static +string values, the view initialisation block can contain any template +directives and generate any kind of dynamic output and/or data items. + + [% VIEW my_web_site %] + [% view.title = title or 'My Cool Web Site' %] + [% view.author = "$abw.name, $abw.email" %] + [% view.sidebar = INCLUDE my/sidebar.tt2 %] + [% END %] + +Note that additional data items can be specified as arguments to the VIEW +directive. Anything that doesn't look like a configuration parameter is +assumed to be a data item. This can be a little hazardous, of course, because +you never know when a new configuration item might get added which interferes +with your data. + + [% VIEW my_web_site + # config options + prefix = 'my/' + # misc data + title = title or 'My Cool Web Site' + author = "$abw.name, $abw.email" + sidebar = INCLUDE my/sidebar.tt2 + %] + ... + [% END %] + +Outside of the view definition you can access the view variables as, for +example: + + [% my_web_site.title %] + +One important feature is the equivalence of simple variables and templates. +You can implement the view item 'title' as a simple variable, a template +defined in an external file, possibly with a prefix/suffix automatically +appended, or as a local BLOCK definition within the [% VIEW %] ... [% END %] +definition. If you use the syntax above then the view will Do The Right +Thing to return the appropriate output. + +At the END of the VIEW definition the view is "sealed" to prevent you +from accidentally updating any variable values. If you attempt to change +the value of a variable after the END of the VIEW definition block then +an 'view' error will be thrown. + + [% TRY; + my_web_site.title = 'New Title'; + CATCH; + error; + END + %] + +The error above will be reported as: + + view error - cannot update item in sealed view: title + +The same is true if you pass a parameter to a view variable. This is +interpreted as an attempt to update the variable and will raise the same +warning. + + [% my_web_site.title('New Title') %] # view error! + +You can set the 'silent' parameter to have the view ignore these +parameters and simply return the variable value. + + [% VIEW my_web_site + silent = 1 + title = title or 'My Cool Web Site' + # ... ; + END + %] + + [% my_web_site.title('Blah Blah') %] # My Cool Web Site + +Alternately, you can specify that a view is unsealed allowing existing +variables to be updated and new variables defined. + + [% VIEW my_web_site + sealed = 0 + title = title or 'My Cool Web Site' + # ... ; + END + %] + + [% my_web_site.title('Blah Blah') %] # Blah Blah + [% my_web_site.title %] # Blah Blah + +=head2 Inheritance, Delegation and Reuse + +Views can be inherited from previously defined views by use of the 'base' +parameter. This example shows how a base class view is defined which +applies a 'view/default/' prefix to all template names. + + [% VIEW my.view.default + prefix = 'view/default/'; + END + %] + +Thus the directive: + + [% my.view.default.header(title='Hello World') %] + +is now equivalent to: + + [% INCLUDE view/default/header title='Hello World' %] + +A second view can be defined which specifies the default view as a +base. + + [% VIEW my.view.fancy + base = my.view.default + prefix = 'view/fancy/'; + END + %] + +Now the directive: + + [% my.view.fancy.header(title='Hello World') %] + +will resolve to: + + [% INCLUDE view/fancy/header title='Hello World' %] + +or if that doesn't exist, it will be handled by the base view as: + + [% INCLUDE view/default/header title='Hello World' %] + +When a parent view is specified via the 'base' parameter, the +delegation of a view to its parent for fetching templates and accessing +user defined variables is automatic. You can also implement your own +inheritance, delegation or other reuse patterns by explicitly +delegating to other views. + + [% BLOCK foo %] + public foo block + [% END %] + + [% VIEW plain %] + [% BLOCK foo %] + <plain>[% PROCESS foo %]</plain> + [% END %] + [% END %] + + [% VIEW fancy %] + [% BLOCK foo %] + [% plain.foo | replace('plain', 'fancy') %] + [% END %] + [% END %] + + [% plain.foo %] # <plain>public foo block</plain> + [% fancy.foo %] # <fancy>public foo block</fancy> + +Note that the regular INCLUDE/PROCESS/WRAPPER directives work entirely +independantly of views and will always get the original, unaltered +template name rather than any local per-view definition. + +=head2 Self-Reference + +A reference to the view object under definition is available with the +VIEW ... END block by its specified name and also by the special name +'view' (similar to the C<my $self = shift;> in a Perl method or the +'this' pointer in C++, etc). The view is initially unsealed allowing +any data items to be defined and updated within the VIEW ... END +block. The view is automatically sealed at the end of the definition +block, preventing any view data from being subsequently changed. + +(NOTE: sealing should be optional. As well as sealing a view to prevent +updates (SEALED), it should be possible to set an option in the view to +allow external contexts to update existing variables (UPDATE) or even +create totally new view variables (CREATE)). + + [% VIEW fancy %] + [% fancy.title = 'My Fancy Title' %] + [% fancy.author = 'Frank Open' %] + [% fancy.col = { bg => '#ffffff', bar => '#a0a0ff' } %] + [% END %] + +or + + [% VIEW fancy %] + [% view.title = 'My Fancy Title' %] + [% view.author = 'Frank Open' %] + [% view.col = { bg => '#ffffff', bar => '#a0a0ff' } %] + [% END %] + +It makes no real difference in this case if you refer to the view by +its name, 'fancy', or by the general name, 'view'. Outside of the +view block, however, you should always use the given name, 'fancy': + + [% fancy.title %] + [% fancy.author %] + [% fancy.col.bg %] + +The choice of given name or 'view' is much more important when it +comes to BLOCK definitions within a VIEW. It is generally recommended +that you use 'view' inside a VIEW definition because this is guaranteed +to be correctly defined at any point in the future when the block gets +called. The original name of the view might have long since been changed +or reused but the self-reference via 'view' should always be intact and +valid. + +Take the following VIEW as an example: + + [% VIEW foo %] + [% view.title = 'Hello World' %] + [% BLOCK header %] + Title: [% view.title %] + [% END %] + [% END %] + +Even if we rename the view, or create a new 'foo' variable, the header +block still correctly accesses the 'title' attribute of the view to +which it belongs. Whenever a view BLOCK is processed, the 'view' +variable is always updated to contain the correct reference to the +view object to which it belongs. + + [% bar = foo %] + [% foo = { title => "New Foo" } %] # no problem + [% bar.header %] # => Title: Hello World + +=head2 Saving References to External Views + +When it comes to view inheritance, it's always a good idea to take a +local copy of a parent or delegate view and store it as an attribute +within the view for later use. This ensures that the correct view +reference is always available, even if the external name of a view +has been changed. + + [% VIEW plain %] + ... + [% END %] + + [% VIEW fancy %] + [% view.plain = plain %] + [% BLOCK foo %] + [% view.plain.foo | replace('plain', 'fancy') %] + [% END %] + [% END %] + + [% plain.foo %] # => <plain>public foo block</plain> + [% plain = 'blah' %] # no problem + [% fancy.foo %] # => <fancy>public foo block</fancy> + + +=head2 Views as Data Presenters + +Another key role of a view is to act as a dispatcher to automatically +apply the correct template to present a particular object or data +item. This is handled via the print() method. + +Here's an example: + + [% VIEW foo %] + + [% BLOCK text %] + Some text: [% item %] + [% END %] + + [% BLOCK hash %] + a hash: + [% FOREACH key = item.keys.sort -%] + [% key %] => [% item.$key %] + [% END -%] + [% END %] + + [% BLOCK list %] + a list: [% item.sort.join(', ') %] + [% END %] + + [% END %] + +We can now use the view to print text, hashes or lists. The print() +method includes the right template depending on the typing of the +argument (or arguments) passed. + + [% some_text = 'I read the news today, oh boy.' %] + [% a_hash = { house => 'Lords', hall => 'Albert' } %] + [% a_list = [ 'sure', 'Nobody', 'really' ] %] + + [% view.print(some_text) %] + # Some text: I read the news today, oh boy. + + [% view.print(a_hash) %] + # a hash: + hall => Albert + house => Lords + [% view.print(a_list) %] + # a list: Nobody, really, sure + + +You can also provide templates to print objects of any other class. +The class name is mapped to a template name with all non-word +character sequences such as '::' converted to a single '_'. + + [% VIEW foo %] + [% BLOCK Foo_Bar %] + a Foo::Bar object: + thingies: [% view.print(item.thingies) %] + doodahs: [% view.print(item.doodahs) %] + [% END %] + [% END %] + + [% USE fubar = Foo::Bar(...) %] + + [% foo.print(fubar) %] + +Note how we use the view object to display various items within the +objects ('thingies' and 'doodahs'). We don't need to worry what +kind of data these represent (text, list, hash, etc) because we can +let the view worry about it, automatically mapping the data type to +the correct template. + +Views may define their own type =E<gt> template map. + + [% VIEW foo + map = { TEXT => 'plain_text', + ARRAY => 'show_list', + HASH => 'show_hash', + My::Module => 'template_name' + default => 'any_old_data' + } + %] + [% BLOCK plain_text %] + ... + [% END %] + + ... + + [% END %] + +They can also provide a 'default' map entry, specified as part of the 'map' +hash or as a parameter by itself. + + + [% VIEW foo + map = { ... }, + default = 'whatever' + %] + ... + [% END %] + +or + + [% VIEW foo %] + [% view.map = { ... } + view.default = 'whatever' + %] + ... + [% END %] + +The print() method provides one more piece of magic. If you pass it a +reference to an object which provides a present() method, then the +method will be called passing the view as an argument. This then gives +any object a chance to determine how it should be presented via the +view. + + package Foo::Bar; + + ... + + sub present { + my ($self, $view) = @_; + return "a Foo::Bar object:\n" + . "thingies: " . $view.print($self->{ _THINGIES }) . "\n" + . "doodahs: " . $view.print($self->{ _DOODAHS }) . "\n"; + } + +The object is free to delve deeply into its innards and mess around with +its own private data, before presenting the relevant data via the view. +In a more complex example, a present() method might walk part of a tree +making calls back against the view to present different nodes within the +tree. We may not want to expose the internal structure of the tree +(because that would break encapsulation and make our presentation code +dependant on it) but we want to have some way of walking the tree and +presenting items found in a particular manner. + +This is known as Structure Shy Traversal. Our view object doesn't require +prior knowledge about the internal structure of any data set to be able +to traverse it and present the data contained therein. The data items +themselves, via the present() method, can implement the internal iterators +to guide the view along the right path to presentation happiness. + +The upshot is that you can use views to greatly simplify the display +of data structures like XML::DOM trees. The documentation for the +Template::Plugins::XML::DOM module contains an example of this. In +essence, it looks something like this: + +XML source: + + <user name="Andy Wardley"> + <project id="iCan" title="iCan, but theyCan't"/> + <project id="p45" title="iDid, but theyDidn't"/> + </user> + +TT View: + + [% VIEW fancy %] + [% BLOCK user %] + User: [% item.name %] + [% item.content(myview) %] + [% END %] + + [% BLOCK project %] + Project: [% project.id %] - [% project.name %] + [% END %] + [% END %] + +Generate view: + + [% USE dom = XML.DOM %] + [% fancy.print(dom.parse(xml_source)) %] + +Output: + + User: Andy Wardley + Project: iCan - iCan, but theyCan't + Project: p45 - iDid, but theyDidn't + +The same approach can be applied to many other areas. Here's an example from +the File/Directory plugins. + + [% VIEW myview %] + [% BLOCK file %] + - [% item.name %] + [% END %] + + [% BLOCK directory %] + * [% item.name %] + [% item.content(myview) FILTER indent %] + [% END %] + [% END %] + + [% USE dir = Directory(dirpath) %] + [% myview.print(dir) %] + +And here's the same approach use to convert Pod documentation to any +other format via template. + + [% # load Pod plugin and parse source file into Pod Object Model + USE Pod; + pom = Pod.parse_file(my_pod_file); + + # define view to map all Pod elements to "pod/html/xxx" templates + VIEW pod2html + prefix='pod/html'; + END; + + # now print document via view (i.e. as HTML) + pod2html.print(pom) + %] + +Here we simply define a template prefix for the view which causes the +view to look for 'pod/html/head1', 'pod/html/head2', 'pod/html/over' +as templates to present the different sections of the parsed Pod document. + +There are some examples in the Template Toolkit test suite: t/pod.t and +t/view.t which may shed some more light on this. See the distribution +sub-directory 'examples/pod/html' for examples of Pod -E<gt> HTML templates. + +(This documentation is incomplete but I'm not going to get it 100% pefect +until the syntax and API stabilise). + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Modules.pod b/lib/Template/Modules.pod new file mode 100644 index 0000000..78dadb8 --- /dev/null +++ b/lib/Template/Modules.pod @@ -0,0 +1,448 @@ +#============================================================= -*-perl-*- +# +# Template::Modules +# +# DESCRIPTION +# This section contains the documentation for the modules that +# comprise the Template Toolkit. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Modules - Core modules comprising the Template Toolkit + +=head1 DESCRIPTION + +This section contains the documentation for the modules that comprise +the Template Toolkit. + +=over 4 + +=item L<Template|Template> + +Front-end module to the Template Toolkit + + + +=item L<Template::Base|Template::Base> + +Base class module implementing common functionality + + + +=item L<Template::Config|Template::Config> + +Factory module for instantiating other TT2 modules + + + +=item L<Template::Constants|Template::Constants> + +Defines constants for the Template Toolkit + + + +=item L<Template::Context|Template::Context> + +Runtime context in which templates are processed + + + +=item L<Template::Document|Template::Document> + +Compiled template document object + + + +=item L<Template::Exception|Template::Exception> + +Exception handling class module + + + +=item L<Template::Filters|Template::Filters> + +Post-processing filters for template blocks + + + +=item L<Template::Iterator|Template::Iterator> + +Data iterator used by the FOREACH directive + + + +=item L<Template::Namespace::Constants|Template::Namespace::Constants> + +Compile time constant folding + + + +=item L<Template::Parser|Template::Parser> + +LALR(1) parser for compiling template documents + + + +=item L<Template::Plugin|Template::Plugin> + +Base class for Template Toolkit plugins + + + +=item L<Template::Plugins|Template::Plugins> + +Plugin provider module + + + +=item L<Template::Provider|Template::Provider> + +Provider module for loading/compiling templates + + + +=item L<Template::Service|Template::Service> + +General purpose template processing service + + + +=item L<Template::Stash|Template::Stash> + +Magical storage for template variables + + + +=item L<Template::Stash::Context|Template::Stash::Context> + +Experimetal stash allowing list/scalar context definition + + + +=item L<Template::Stash::XS|Template::Stash::XS> + +Experimetal high-speed stash written in XS + + + +=item L<Template::Test|Template::Test> + +Module for automating TT2 test scripts + + + +=item L<Template::Plugin::Autoformat|Template::Plugin::Autoformat> + +Interface to Text::Autoformat module + + + +=item L<Template::Plugin::CGI|Template::Plugin::CGI> + +Interface to the CGI module + + + +=item L<Template::Plugin::DBI|Template::Plugin::DBI> + +Template interface to the DBI module + + + +=item L<Template::Plugin::Datafile|Template::Plugin::Datafile> + +Plugin to construct records from a simple data file + + + +=item L<Template::Plugin::Date|Template::Plugin::Date> + +Plugin to generate formatted date strings + + + +=item L<Template::Plugin::Directory|Template::Plugin::Directory> + +Plugin for generating directory listings + + + +=item L<Template::Plugin::Dumper|Template::Plugin::Dumper> + +Plugin interface to Data::Dumper + + + +=item L<Template::Plugin::File|Template::Plugin::File> + +Plugin providing information about files + + + +=item L<Template::Plugin::Filter|Template::Plugin::Filter> + +Base class for plugin filters + + + +=item L<Template::Plugin::Format|Template::Plugin::Format> + +Plugin to create formatting functions + + + +=item L<Template::Plugin::GD::Image|Template::Plugin::GD::Image> + +Interface to GD Graphics Library + + + +=item L<Template::Plugin::GD::Polygon|Template::Plugin::GD::Polygon> + +Interface to GD module Polygon class + + + +=item L<Template::Plugin::GD::Constants|Template::Plugin::GD::Constants> + +Interface to GD module constants + + + +=item L<Template::Plugin::GD::Text|Template::Plugin::GD::Text> + +Text utilities for use with GD + + + +=item L<Template::Plugin::GD::Text::Align|Template::Plugin::GD::Text::Align> + +Draw aligned strings in GD images + + + +=item L<Template::Plugin::GD::Text::Wrap|Template::Plugin::GD::Text::Wrap> + +Break and wrap strings in GD images + + + +=item L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines> + +Create line graphs with axes and legends + + + +=item L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d> + +Create 3D line graphs with axes and legends + + + +=item L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars> + +Create bar graphs with axes and legends + + + +=item L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d> + +Create 3D bar graphs with axes and legends + + + +=item L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points> + +Create point graphs with axes and legends + + + +=item L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints> + +Create line/point graphs with axes and legends + + + +=item L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area> + +Create area graphs with axes and legends + + + +=item L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed> + +Create mixed graphs with axes and legends + + + +=item L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie> + +Create pie charts with legends + + + +=item L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d> + +Create 3D pie charts with legends + + + +=item L<Template::Plugin::HTML|Template::Plugin::HTML> + +Plugin to create HTML elements + + + +=item L<Template::Plugin::Image|Template::Plugin::Image> + +Plugin access to image sizes + + + +=item L<Template::Plugin::Iterator|Template::Plugin::Iterator> + +Plugin to create iterators (Template::Iterator) + + + +=item L<Template::Plugin::Math|Template::Plugin::Math> + +Plugin interface to mathematical functions + + + +=item L<Template::Plugin::Pod|Template::Plugin::Pod> + +Plugin interface to Pod::POM (Pod Object Model) + + + +=item L<Template::Plugin::Procedural|Template::Plugin::Procedural> + +Base class for procedural plugins + + + +=item L<Template::Plugin::String|Template::Plugin::String> + +Object oriented interface for string manipulation + + + +=item L<Template::Plugin::Table|Template::Plugin::Table> + +Plugin to present data in a table + + + +=item L<Template::Plugin::URL|Template::Plugin::URL> + +Plugin to construct complex URLs + + + +=item L<Template::Plugin::View|Template::Plugin::View> + +Plugin to create views (Template::View) + + + +=item L<Template::Plugin::Wrap|Template::Plugin::Wrap> + +Plugin interface to Text::Wrap + + + +=item L<Template::Plugin::XML::DOM|Template::Plugin::XML::DOM> + +Plugin interface to XML::DOM + + + +=item L<Template::Plugin::XML::RSS|Template::Plugin::XML::RSS> + +Plugin interface to XML::RSS + + + +=item L<Template::Plugin::XML::Simple|Template::Plugin::XML::Simple> + +Plugin interface to XML::Simple + + + +=item L<Template::Plugin::XML::Style|Template::Plugin::XML::Style> + +Simple XML stylesheet transfomations + + + +=item L<Template::Plugin::XML::XPath|Template::Plugin::XML::XPath> + +Plugin interface to XML::XPath + + + + + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Namespace/Constants.pm b/lib/Template/Namespace/Constants.pm new file mode 100644 index 0000000..e1b5114 --- /dev/null +++ b/lib/Template/Namespace/Constants.pm @@ -0,0 +1,205 @@ +#================================================================= -*-Perl-*- +# +# Template::Namespace::Constants +# +# DESCRIPTION +# Plugin compiler module for performing constant folding at compile time +# on variables in a particular namespace. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2002 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. +# +# REVISION +# $Id: Constants.pm,v 1.22 2004/01/13 16:20:36 abw Exp $ +# +#============================================================================ + +package Template::Namespace::Constants; + +use strict; +use Template::Base; +use Template::Config; +use Template::Directive; +use Template::Exception; + +use base qw( Template::Base ); +use vars qw( $VERSION $DEBUG ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.22 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0 unless defined $DEBUG; + + +sub _init { + my ($self, $config) = @_; + $self->{ STASH } = Template::Config->stash($config) + || return $self->error(Template::Config->error()); + return $self; +} + + + +#------------------------------------------------------------------------ +# ident(\@ident) foo.bar(baz) +#------------------------------------------------------------------------ + +sub ident { + my ($self, $ident) = @_; + my @save = @$ident; + + # discard first node indicating constants namespace + splice(@$ident, 0, 2); + + my $nelems = @$ident / 2; + my ($e, $result); + local $" = ', '; + + print STDERR "constant ident [ @$ident ] " if $DEBUG; + + foreach $e (0..$nelems-1) { + # node name must be a constant + unless ($ident->[$e * 2] =~ s/^'(.+)'$/$1/s) { + $self->DEBUG(" * deferred (non-constant item: ", $ident->[$e * 2], ")\n") + if $DEBUG; + return Template::Directive->ident(\@save); + } + + # if args is non-zero then it must be eval'ed + if ($ident->[$e * 2 + 1]) { + my $args = $ident->[$e * 2 + 1]; + my $comp = eval "$args"; + if ($@) { + $self->DEBUG(" * deferred (non-constant args: $args)\n") if $DEBUG; + return Template::Directive->ident(\@save); + } + $self->DEBUG("($args) ") if $comp && $DEBUG; + $ident->[$e * 2 + 1] = $comp; + } + } + + + $result = $self->{ STASH }->get($ident); + + if (! length $result || ref $result) { + my $reason = length $result ? 'reference' : 'no result'; + $self->DEBUG(" * deferred ($reason)\n") if $DEBUG; + return Template::Directive->ident(\@save); + } + + $result =~ s/'/\\'/g; + + $self->DEBUG(" * resolved => '$result'\n") if $DEBUG; + + return "'$result'"; +} + +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::Namespace::Constants - Compile time constant folding + +=head1 SYNOPSIS + + # easy way to define constants + use Template; + + my $tt = Template->new({ + CONSTANTS => { + pi => 3.14, + e => 2.718, + }, + }); + + # nitty-gritty, hands-dirty way + use Template::Namespace::Constants; + + my $tt = Template->new({ + NAMESPACE => { + constants => Template::Namespace::Constants->new({ + pi => 3.14, + e => 2.718, + }, + }, + }); + +=head1 DESCRIPTION + +The Template::Namespace::Constants module implements a namespace handler +which is plugged into the Template::Directive compiler module. This then +performs compile time constant folding of variables in a particular namespace. + +=head1 PUBLIC METHODS + +=head2 new(\%constants) + +The new() constructor method creates and returns a reference to a new +Template::Namespace::Constants object. This creates an internal stash +to store the constant variable definitions passed as arguments. + + my $handler = Template::Namespace::Constants->new({ + pi => 3.14, + e => 2.718, + }); + +=head2 ident(\@ident) + +Method called to resolve a variable identifier into a compiled form. In this +case, the method fetches the corresponding constant value from its internal +stash and returns it. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +1.22, 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::Directive|Template::Directive> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Parser.pm b/lib/Template/Parser.pm new file mode 100644 index 0000000..68bf9e0 --- /dev/null +++ b/lib/Template/Parser.pm @@ -0,0 +1,1446 @@ +#============================================================= -*-Perl-*- +# +# Template::Parser +# +# DESCRIPTION +# This module implements a LALR(1) parser and assocated support +# methods to parse template documents into the appropriate "compiled" +# format. Much of the parser DFA code (see _parse() method) is based +# on Francois Desarmenien's Parse::Yapp module. Kudos to him. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +# The following copyright notice appears in the Parse::Yapp +# documentation. +# +# The Parse::Yapp module and its related modules and shell +# scripts are copyright (c) 1998 Francois Desarmenien, +# France. All rights reserved. +# +# You may use and distribute them under the terms of either +# the GNU General Public License or the Artistic License, as +# specified in the Perl README file. +# +#---------------------------------------------------------------------------- +# +# $Id: Parser.pm,v 2.81 2004/01/13 16:19:15 abw Exp $ +# +#============================================================================ + +package Template::Parser; + +require 5.004; + +use strict; +use vars qw( $VERSION $DEBUG $ERROR ); +use base qw( Template::Base ); +use vars qw( $TAG_STYLE $DEFAULT_STYLE $QUOTED_ESCAPES ); + +use Template::Constants qw( :status :chomp ); +use Template::Directive; +use Template::Grammar; + +# parser state constants +use constant CONTINUE => 0; +use constant ACCEPT => 1; +use constant ERROR => 2; +use constant ABORT => 3; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.81 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0 unless defined $DEBUG; +$ERROR = ''; + + +#======================================================================== +# -- COMMON TAG STYLES -- +#======================================================================== + +$TAG_STYLE = { + 'default' => [ '\[%', '%\]' ], + 'template1' => [ '[\[%]%', '%[\]%]' ], + 'metatext' => [ '%%', '%%' ], + 'html' => [ '<!--', '-->' ], + 'mason' => [ '<%', '>' ], + 'asp' => [ '<%', '%>' ], + 'php' => [ '<\?', '\?>' ], + 'star' => [ '\[\*', '\*\]' ], +}; +$TAG_STYLE->{ template } = $TAG_STYLE->{ tt2 } = $TAG_STYLE->{ default }; + + +$DEFAULT_STYLE = { + START_TAG => $TAG_STYLE->{ default }->[0], + END_TAG => $TAG_STYLE->{ default }->[1], +# TAG_STYLE => 'default', + ANYCASE => 0, + INTERPOLATE => 0, + PRE_CHOMP => 0, + POST_CHOMP => 0, + V1DOLLAR => 0, + EVAL_PERL => 0, +}; + +$QUOTED_ESCAPES = { + n => "\n", + r => "\r", + t => "\t", +}; + + +#======================================================================== +# ----- PUBLIC METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# new(\%config) +# +# Constructor method. +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $config = $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift(@_) : { @_ }; + my ($tagstyle, $debug, $start, $end, $defaults, $grammar, $hash, $key, $udef); + + my $self = bless { + START_TAG => undef, + END_TAG => undef, + TAG_STYLE => 'default', + ANYCASE => 0, + INTERPOLATE => 0, + PRE_CHOMP => 0, + POST_CHOMP => 0, + V1DOLLAR => 0, + EVAL_PERL => 0, + FILE_INFO => 1, + GRAMMAR => undef, + _ERROR => '', + FACTORY => 'Template::Directive', + }, $class; + + # update self with any relevant keys in config + foreach $key (keys %$self) { + $self->{ $key } = $config->{ $key } if defined $config->{ $key }; + } + $self->{ FILEINFO } = [ ]; + + # DEBUG config item can be a bitmask + if (defined ($debug = $config->{ DEBUG })) { + $self->{ DEBUG } = $debug & ( Template::Constants::DEBUG_PARSER + | Template::Constants::DEBUG_FLAGS ); + $self->{ DEBUG_DIRS } = $debug & Template::Constants::DEBUG_DIRS; + } + # package variable can be set to 1 to support previous behaviour + elsif ($DEBUG == 1) { + $self->{ DEBUG } = Template::Constants::DEBUG_PARSER; + $self->{ DEBUG_DIRS } = 0; + } + # otherwise let $DEBUG be a bitmask + else { + $self->{ DEBUG } = $DEBUG & ( Template::Constants::DEBUG_PARSER + | Template::Constants::DEBUG_FLAGS ); + $self->{ DEBUG_DIRS } = $DEBUG & Template::Constants::DEBUG_DIRS; + } + + $grammar = $self->{ GRAMMAR } ||= do { + require Template::Grammar; + Template::Grammar->new(); + }; + + # build a FACTORY object to include any NAMESPACE definitions, + # but only if FACTORY isn't already an object + if ($config->{ NAMESPACE } && ! ref $self->{ FACTORY }) { + my $fclass = $self->{ FACTORY }; + $self->{ FACTORY } = $fclass->new( NAMESPACE => $config->{ NAMESPACE } ) + || return $class->error($fclass->error()); + } + + # load grammar rules, states and lex table + @$self{ qw( LEXTABLE STATES RULES ) } + = @$grammar{ qw( LEXTABLE STATES RULES ) }; + + $self->new_style($config) + || return $class->error($self->error()); + + return $self; +} + + +#------------------------------------------------------------------------ +# new_style(\%config) +# +# Install a new (stacked) parser style. This feature is currently +# experimental but should mimic the previous behaviour with regard to +# TAG_STYLE, START_TAG, END_TAG, etc. +#------------------------------------------------------------------------ + +sub new_style { + my ($self, $config) = @_; + my $styles = $self->{ STYLE } ||= [ ]; + my ($tagstyle, $tags, $start, $end, $key); + + # clone new style from previous or default style + my $style = { %{ $styles->[-1] || $DEFAULT_STYLE } }; + + # expand START_TAG and END_TAG from specified TAG_STYLE + if ($tagstyle = $config->{ TAG_STYLE }) { + return $self->error("Invalid tag style: $tagstyle") + unless defined ($tags = $TAG_STYLE->{ $tagstyle }); + ($start, $end) = @$tags; + $config->{ START_TAG } ||= $start; + $config->{ END_TAG } ||= $end; + } + + foreach $key (keys %$DEFAULT_STYLE) { + $style->{ $key } = $config->{ $key } if defined $config->{ $key }; + } + push(@$styles, $style); + return $style; +} + + +#------------------------------------------------------------------------ +# old_style() +# +# Pop the current parser style and revert to the previous one. See +# new_style(). ** experimental ** +#------------------------------------------------------------------------ + +sub old_style { + my $self = shift; + my $styles = $self->{ STYLE }; + return $self->error('only 1 parser style remaining') + unless (@$styles > 1); + pop @$styles; + return $styles->[-1]; +} + + +#------------------------------------------------------------------------ +# parse($text, $data) +# +# Parses the text string, $text and returns a hash array representing +# the compiled template block(s) as Perl code, in the format expected +# by Template::Document. +#------------------------------------------------------------------------ + +sub parse { + my ($self, $text, $info) = @_; + my ($tokens, $block); + + $info->{ DEBUG } = $self->{ DEBUG_DIRS } + unless defined $info->{ DEBUG }; + +# print "info: { ", join(', ', map { "$_ => $info->{ $_ }" } keys %$info), " }\n"; + + # store for blocks defined in the template (see define_block()) + my $defblock = $self->{ DEFBLOCK } = { }; + my $metadata = $self->{ METADATA } = [ ]; + + $self->{ _ERROR } = ''; + + # split file into TEXT/DIRECTIVE chunks + $tokens = $self->split_text($text) + || return undef; ## RETURN ## + + push(@{ $self->{ FILEINFO } }, $info); + + # parse chunks + $block = $self->_parse($tokens, $info); + + pop(@{ $self->{ FILEINFO } }); + + return undef unless $block; ## RETURN ## + + $self->debug("compiled main template document block:\n$block") + if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER; + + return { + BLOCK => $block, + DEFBLOCKS => $defblock, + METADATA => { @$metadata }, + }; +} + + + +#------------------------------------------------------------------------ +# split_text($text) +# +# Split input template text into directives and raw text chunks. +#------------------------------------------------------------------------ + +sub split_text { + my ($self, $text) = @_; + my ($pre, $dir, $prelines, $dirlines, $postlines, $chomp, $tags, @tags); + my $style = $self->{ STYLE }->[-1]; + my ($start, $end, $prechomp, $postchomp, $interp ) = + @$style{ qw( START_TAG END_TAG PRE_CHOMP POST_CHOMP INTERPOLATE ) }; + + my @tokens = (); + my $line = 1; + + return \@tokens ## RETURN ## + unless defined $text && length $text; + + # extract all directives from the text + while ($text =~ s/ + ^(.*?) # $1 - start of line up to directive + (?: + $start # start of tag + (.*?) # $2 - tag contents + $end # end of tag + ) + //sx) { + + ($pre, $dir) = ($1, $2); + $pre = '' unless defined $pre; + $dir = '' unless defined $dir; + + $postlines = 0; # denotes lines chomped + $prelines = ($pre =~ tr/\n//); # NULL - count only + $dirlines = ($dir =~ tr/\n//); # ditto + + # the directive CHOMP options may modify the preceding text + for ($dir) { + # remove leading whitespace and check for a '-' chomp flag + s/^([-+\#])?\s*//s; + if ($1 && $1 eq '#') { + # comment out entire directive except for any chomp flag + $dir = ($dir =~ /([-+])$/) ? $1 : ''; + } + else { + $chomp = ($1 && $1 eq '+') ? 0 : ($1 || $prechomp); +# my $space = $prechomp == &Template::Constants::CHOMP_COLLAPSE + my $space = $prechomp == CHOMP_COLLAPSE + ? ' ' : ''; + + # chomp off whitespace and newline preceding directive + $chomp and $pre =~ s/(\n|^)([ \t]*)\Z/($1||$2) ? $space : ''/me + and $1 eq "\n" + and $prelines++; + } + + # remove trailing whitespace and check for a '-' chomp flag + s/\s*([-+])?\s*$//s; + $chomp = ($1 && $1 eq '+') ? 0 : ($1 || $postchomp); + my $space = $postchomp == &Template::Constants::CHOMP_COLLAPSE + ? ' ' : ''; + + $postlines++ + if $chomp and $text =~ s/ + ^ + ([ \t]*)\n # whitespace to newline + (?:(.|\n)|$) # any char (not EOF) + / + (($1||$2) ? $space : '') . (defined $2 ? $2 : '') + /ex; + } + + # any text preceding the directive can now be added + if (length $pre) { + push(@tokens, $interp + ? [ $pre, $line, 'ITEXT' ] + : ('TEXT', $pre) ); + $line += $prelines; + } + + # and now the directive, along with line number information + if (length $dir) { + # the TAGS directive is a compile-time switch + if ($dir =~ /^TAGS\s+(.*)/i) { + my @tags = split(/\s+/, $1); + if (scalar @tags > 1) { + ($start, $end) = map { quotemeta($_) } @tags; + } + elsif ($tags = $TAG_STYLE->{ $tags[0] }) { + ($start, $end) = @$tags; + } + else { + warn "invalid TAGS style: $tags[0]\n"; + } + } + else { + # DIRECTIVE is pushed as: + # [ $dirtext, $line_no(s), \@tokens ] + push(@tokens, + [ $dir, + ($dirlines + ? sprintf("%d-%d", $line, $line + $dirlines) + : $line), + $self->tokenise_directive($dir) ]); + } + } + + # update line counter to include directive lines and any extra + # newline chomped off the start of the following text + $line += $dirlines + $postlines; + } + + # anything remaining in the string is plain text + push(@tokens, $interp + ? [ $text, $line, 'ITEXT' ] + : ( 'TEXT', $text) ) + if length $text; + + return \@tokens; ## RETURN ## +} + + + +#------------------------------------------------------------------------ +# interpolate_text($text, $line) +# +# Examines $text looking for any variable references embedded like +# $this or like ${ this }. +#------------------------------------------------------------------------ + +sub interpolate_text { + my ($self, $text, $line) = @_; + my @tokens = (); + my ($pre, $var, $dir); + + + while ($text =~ + / + ( (?: \\. | [^\$] ){1,3000} ) # escaped or non-'$' character [$1] + | + ( \$ (?: # embedded variable [$2] + (?: \{ ([^\}]*) \} ) # ${ ... } [$3] + | + ([\w\.]+) # $word [$4] + ) + ) + /gx) { + + ($pre, $var, $dir) = ($1, $3 || $4, $2); + + # preceding text + if (defined($pre) && length($pre)) { + $line += $pre =~ tr/\n//; + $pre =~ s/\\\$/\$/g; + push(@tokens, 'TEXT', $pre); + } + # $variable reference + if ($var) { + $line += $dir =~ tr/\n/ /; + push(@tokens, [ $dir, $line, $self->tokenise_directive($var) ]); + } + # other '$' reference - treated as text + elsif ($dir) { + $line += $dir =~ tr/\n//; + push(@tokens, 'TEXT', $dir); + } + } + + return \@tokens; +} + + + +#------------------------------------------------------------------------ +# tokenise_directive($text) +# +# Called by the private _parse() method when it encounters a DIRECTIVE +# token in the list provided by the split_text() or interpolate_text() +# methods. The directive text is passed by parameter. +# +# The method splits the directive into individual tokens as recognised +# by the parser grammar (see Template::Grammar for details). It +# constructs a list of tokens each represented by 2 elements, as per +# split_text() et al. The first element contains the token type, the +# second the token itself. +# +# The method tokenises the string using a complex (but fast) regex. +# For a deeper understanding of the regex magic at work here, see +# Jeffrey Friedl's excellent book "Mastering Regular Expressions", +# from O'Reilly, ISBN 1-56592-257-3 +# +# Returns a reference to the list of chunks (each one being 2 elements) +# identified in the directive text. On error, the internal _ERROR string +# is set and undef is returned. +#------------------------------------------------------------------------ + +sub tokenise_directive { + my ($self, $text, $line) = @_; + my ($token, $uctoken, $type, $lookup); + my $lextable = $self->{ LEXTABLE }; + my $style = $self->{ STYLE }->[-1]; + my ($anycase, $start, $end) = @$style{ qw( ANYCASE START_TAG END_TAG ) }; + my @tokens = ( ); + + while ($text =~ + / + # strip out any comments + (\#[^\n]*) + | + # a quoted phrase matches in $3 + (["']) # $2 - opening quote, ' or " + ( # $3 - quoted text buffer + (?: # repeat group (no backreference) + \\\\ # an escaped backslash \\ + | # ...or... + \\\2 # an escaped quote \" or \' (match $1) + | # ...or... + . # any other character + | \n + )*? # non-greedy repeat + ) # end of $3 + \2 # match opening quote + | + # an unquoted number matches in $4 + (-?\d+(?:\.\d+)?) # numbers + | + # filename matches in $5 + ( \/?\w+(?:(?:\/|::?)\w*)+ | \/\w+) + | + # an identifier matches in $6 + (\w+) # variable identifier + | + # an unquoted word or symbol matches in $7 + ( [(){}\[\]:;,\/\\] # misc parenthesis and symbols +# | \-> # arrow operator (for future?) + | [+\-*] # math operations + | \$\{? # dollar with option left brace + | => # like '=' + | [=!<>]?= | [!<>] # eqality tests + | &&? | \|\|? # boolean ops + | \.\.? # n..n sequence + | \S+ # something unquoted + ) # end of $7 + /gmxo) { + + # ignore comments to EOL + next if $1; + + # quoted string + if (defined ($token = $3)) { + # double-quoted string may include $variable references + if ($2 eq '"') { + if ($token =~ /[\$\\]/) { + $type = 'QUOTED'; + # unescape " and \ but leave \$ escaped so that + # interpolate_text() doesn't incorrectly treat it + # as a variable reference +# $token =~ s/\\([\\"])/$1/g; + for ($token) { + s/\\([^\$nrt])/$1/g; + s/\\([nrt])/$QUOTED_ESCAPES->{ $1 }/ge; + } + push(@tokens, ('"') x 2, + @{ $self->interpolate_text($token) }, + ('"') x 2); + next; + } + else { + $type = 'LITERAL'; + $token =~ s['][\\']g; + $token = "'$token'"; + } + } + else { + $type = 'LITERAL'; + $token = "'$token'"; + } + } + # number + elsif (defined ($token = $4)) { + $type = 'NUMBER'; + } + elsif (defined($token = $5)) { + $type = 'FILENAME'; + } + elsif (defined($token = $6)) { + # reserved words may be in lower case unless case sensitive + $uctoken = $anycase ? uc $token : $token; + if (defined ($type = $lextable->{ $uctoken })) { + $token = $uctoken; + } + else { + $type = 'IDENT'; + } + } + elsif (defined ($token = $7)) { + # reserved words may be in lower case unless case sensitive + $uctoken = $anycase ? uc $token : $token; + unless (defined ($type = $lextable->{ $uctoken })) { + $type = 'UNQUOTED'; + } + } + + push(@tokens, $type, $token); + +# print(STDERR " +[ $type, $token ]\n") +# if $DEBUG; + } + +# print STDERR "tokenise directive() returning:\n [ @tokens ]\n" +# if $DEBUG; + + return \@tokens; ## RETURN ## +} + + +#------------------------------------------------------------------------ +# define_block($name, $block) +# +# Called by the parser 'defblock' rule when a BLOCK definition is +# encountered in the template. The name of the block is passed in the +# first parameter and a reference to the compiled block is passed in +# the second. This method stores the block in the $self->{ DEFBLOCK } +# hash which has been initialised by parse() and will later be used +# by the same method to call the store() method on the calling cache +# to define the block "externally". +#------------------------------------------------------------------------ + +sub define_block { + my ($self, $name, $block) = @_; + my $defblock = $self->{ DEFBLOCK } + || return undef; + + $self->debug("compiled block '$name':\n$block") + if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER; + + $defblock->{ $name } = $block; + + return undef; +} + +sub push_defblock { + my $self = shift; + my $stack = $self->{ DEFBLOCK_STACK } ||= []; + push(@$stack, $self->{ DEFBLOCK } ); + $self->{ DEFBLOCK } = { }; +} + +sub pop_defblock { + my $self = shift; + my $defs = $self->{ DEFBLOCK }; + my $stack = $self->{ DEFBLOCK_STACK } || return $defs; + return $defs unless @$stack; + $self->{ DEFBLOCK } = pop @$stack; + return $defs; +} + + +#------------------------------------------------------------------------ +# add_metadata(\@setlist) +#------------------------------------------------------------------------ + +sub add_metadata { + my ($self, $setlist) = @_; + my $metadata = $self->{ METADATA } + || return undef; + + push(@$metadata, @$setlist); + + return undef; +} + + +#------------------------------------------------------------------------ +# location() +# +# Return Perl comment indicating current parser file and line +#------------------------------------------------------------------------ + +sub location { + my $self = shift; + return "\n" unless $self->{ FILE_INFO }; + my $line = ${ $self->{ LINE } }; + my $info = $self->{ FILEINFO }->[-1]; + my $file = $info->{ path } || $info->{ name } + || '(unknown template)'; + $line =~ s/\-.*$//; # might be 'n-n' + return "#line $line \"$file\"\n"; +} + + +#======================================================================== +# ----- PRIVATE METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# _parse(\@tokens, \@info) +# +# Parses the list of input tokens passed by reference and returns a +# Template::Directive::Block object which contains the compiled +# representation of the template. +# +# This is the main parser DFA loop. See embedded comments for +# further details. +# +# On error, undef is returned and the internal _ERROR field is set to +# indicate the error. This can be retrieved by calling the error() +# method. +#------------------------------------------------------------------------ + +sub _parse { + my ($self, $tokens, $info) = @_; + my ($token, $value, $text, $line, $inperl); + my ($state, $stateno, $status, $action, $lookup, $coderet, @codevars); + my ($lhs, $len, $code); # rule contents + my $stack = [ [ 0, undef ] ]; # DFA stack + +# DEBUG +# local $" = ', '; + + # retrieve internal rule and state tables + my ($states, $rules) = @$self{ qw( STATES RULES ) }; + + # call the grammar set_factory method to install emitter factory + $self->{ GRAMMAR }->install_factory($self->{ FACTORY }); + + $line = $inperl = 0; + $self->{ LINE } = \$line; + $self->{ FILE } = $info->{ name }; + $self->{ INPERL } = \$inperl; + + $status = CONTINUE; + my $in_string = 0; + + while(1) { + # get state number and state + $stateno = $stack->[-1]->[0]; + $state = $states->[$stateno]; + + # see if any lookaheads exist for the current state + if (exists $state->{'ACTIONS'}) { + + # get next token and expand any directives (i.e. token is an + # array ref) onto the front of the token list + while (! defined $token && @$tokens) { + $token = shift(@$tokens); + if (ref $token) { + ($text, $line, $token) = @$token; + if (ref $token) { + if ($info->{ DEBUG } && ! $in_string) { + # - - - - - - - - - - - - - - - - - - - - - - - - - + # This is gnarly. Look away now if you're easily + # frightened. We're pushing parse tokens onto the + # pending list to simulate a DEBUG directive like so: + # [% DEBUG msg line='20' text='INCLUDE foo' %] + # - - - - - - - - - - - - - - - - - - - - - - - - - + my $dtext = $text; + $dtext =~ s[(['\\])][\\$1]g; + unshift(@$tokens, + DEBUG => 'DEBUG', + IDENT => 'msg', + IDENT => 'line', + ASSIGN => '=', + LITERAL => "'$line'", + IDENT => 'text', + ASSIGN => '=', + LITERAL => "'$dtext'", + IDENT => 'file', + ASSIGN => '=', + LITERAL => "'$info->{ name }'", + (';') x 2, + @$token, + (';') x 2); + } + else { + unshift(@$tokens, @$token, (';') x 2); + } + $token = undef; # force redo + } + elsif ($token eq 'ITEXT') { + if ($inperl) { + # don't perform interpolation in PERL blocks + $token = 'TEXT'; + $value = $text; + } + else { + unshift(@$tokens, + @{ $self->interpolate_text($text, $line) }); + $token = undef; # force redo + } + } + } + else { + # toggle string flag to indicate if we're crossing + # a string boundary + $in_string = ! $in_string if $token eq '"'; + $value = shift(@$tokens); + } + }; + # clear undefined token to avoid 'undefined variable blah blah' + # warnings and let the parser logic pick it up in a minute + $token = '' unless defined $token; + + # get the next state for the current lookahead token + $action = defined ($lookup = $state->{'ACTIONS'}->{ $token }) + ? $lookup + : defined ($lookup = $state->{'DEFAULT'}) + ? $lookup + : undef; + } + else { + # no lookahead actions + $action = $state->{'DEFAULT'}; + } + + # ERROR: no ACTION + last unless defined $action; + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # shift (+ive ACTION) + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - + if ($action > 0) { + push(@$stack, [ $action, $value ]); + $token = $value = undef; + redo; + }; + + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # reduce (-ive ACTION) + # - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ($lhs, $len, $code) = @{ $rules->[ -$action ] }; + + # no action imples ACCEPTance + $action + or $status = ACCEPT; + + # use dummy sub if code ref doesn't exist + $code = sub { $_[1] } + unless $code; + + @codevars = $len + ? map { $_->[1] } @$stack[ -$len .. -1 ] + : (); + + eval { + $coderet = &$code( $self, @codevars ); + }; + if ($@) { + my $err = $@; + chomp $err; + return $self->_parse_error($err); + } + + # reduce stack by $len + splice(@$stack, -$len, $len); + + # ACCEPT + return $coderet ## RETURN ## + if $status == ACCEPT; + + # ABORT + return undef ## RETURN ## + if $status == ABORT; + + # ERROR + last + if $status == ERROR; + } + continue { + push(@$stack, [ $states->[ $stack->[-1][0] ]->{'GOTOS'}->{ $lhs }, + $coderet ]), + } + + # ERROR ## RETURN ## + return $self->_parse_error('unexpected end of input') + unless defined $value; + + # munge text of last directive to make it readable +# $text =~ s/\n/\\n/g; + + return $self->_parse_error("unexpected end of directive", $text) + if $value eq ';'; # end of directive SEPARATOR + + return $self->_parse_error("unexpected token ($value)", $text); +} + + + +#------------------------------------------------------------------------ +# _parse_error($msg, $dirtext) +# +# Method used to handle errors encountered during the parse process +# in the _parse() method. +#------------------------------------------------------------------------ + +sub _parse_error { + my ($self, $msg, $text) = @_; + my $line = $self->{ LINE }; + $line = ref($line) ? $$line : $line; + $line = 'unknown' unless $line; + + $msg .= "\n [% $text %]" + if defined $text; + + return $self->error("line $line: $msg"); +} + + +#------------------------------------------------------------------------ +# _dump() +# +# Debug method returns a string representing the internal state of the +# object. +#------------------------------------------------------------------------ + +sub _dump { + my $self = shift; + my $output = "[Template::Parser] {\n"; + my $format = " %-16s => %s\n"; + my $key; + + foreach $key (qw( START_TAG END_TAG TAG_STYLE ANYCASE INTERPOLATE + PRE_CHOMP POST_CHOMP V1DOLLAR )) { + my $val = $self->{ $key }; + $val = '<undef>' unless defined $val; + $output .= sprintf($format, $key, $val); + } + + $output .= '}'; + return $output; +} + + +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::Parser - LALR(1) parser for compiling template documents + +=head1 SYNOPSIS + + use Template::Parser; + + $parser = Template::Parser->new(\%config); + $template = $parser->parse($text) + || die $parser->error(), "\n"; + +=head1 DESCRIPTION + +The Template::Parser module implements a LALR(1) parser and associated methods +for parsing template documents into Perl code. + +=head1 PUBLIC METHODS + +=head2 new(\%params) + +The new() constructor creates and returns a reference to a new +Template::Parser object. A reference to a hash may be supplied as a +parameter to provide configuration values. These may include: + +=over + + + + +=item START_TAG, END_TAG + +The START_TAG and END_TAG options are used to specify character +sequences or regular expressions that mark the start and end of a +template directive. The default values for START_TAG and END_TAG are +'[%' and '%]' respectively, giving us the familiar directive style: + + [% example %] + +Any Perl regex characters can be used and therefore should be escaped +(or use the Perl C<quotemeta> function) if they are intended to +represent literal characters. + + my $parser = Template::Parser->new({ + START_TAG => quotemeta('<+'), + END_TAG => quotemeta('+>'), + }); + +example: + + <+ INCLUDE foobar +> + +The TAGS directive can also be used to set the START_TAG and END_TAG values +on a per-template file basis. + + [% TAGS <+ +> %] + + + + + + +=item TAG_STYLE + +The TAG_STYLE option can be used to set both START_TAG and END_TAG +according to pre-defined tag styles. + + my $parser = Template::Parser->new({ + TAG_STYLE => 'star', + }); + +Available styles are: + + template [% ... %] (default) + template1 [% ... %] or %% ... %% (TT version 1) + metatext %% ... %% (Text::MetaText) + star [* ... *] (TT alternate) + php <? ... ?> (PHP) + asp <% ... %> (ASP) + mason <% ... > (HTML::Mason) + html <!-- ... --> (HTML comments) + +Any values specified for START_TAG and/or END_TAG will over-ride +those defined by a TAG_STYLE. + +The TAGS directive may also be used to set a TAG_STYLE + + [% TAGS html %] + <!-- INCLUDE header --> + + + + + + +=item PRE_CHOMP, POST_CHOMP + +Anything outside a directive tag is considered plain text and is +generally passed through unaltered (but see the INTERPOLATE option). +This includes all whitespace and newlines characters surrounding +directive tags. Directives that don't generate any output will leave +gaps in the output document. + +Example: + + Foo + [% a = 10 %] + Bar + +Output: + + Foo + + Bar + +The PRE_CHOMP and POST_CHOMP options can help to clean up some of this +extraneous whitespace. Both are disabled by default. + + my $parser = Template::Parser->new({ + PRE_CHOMP => 1, + POST_CHOMP => 1, + }); + +With PRE_CHOMP set to 1, the newline and whitespace preceding a directive +at the start of a line will be deleted. This has the effect of +concatenating a line that starts with a directive onto the end of the +previous line. + + Foo <----------. + | + ,---(PRE_CHOMP)----' + | + `-- [% a = 10 %] --. + | + ,---(POST_CHOMP)---' + | + `-> Bar + +With POST_CHOMP set to 1, any whitespace after a directive up to and +including the newline will be deleted. This has the effect of joining +a line that ends with a directive onto the start of the next line. + +If PRE_CHOMP or POST_CHOMP is set to 2, then instead of removing all +the whitespace, the whitespace will be collapsed to a single space. +This is useful for HTML, where (usually) a contiguous block of +whitespace is rendered the same as a single space. + +You may use the CHOMP_NONE, CHOMP_ALL, and CHOMP_COLLAPSE constants +from the Template::Constants module to deactivate chomping, remove +all whitespace, or collapse whitespace to a single space. + +PRE_CHOMP and POST_CHOMP can be activated for individual directives by +placing a '-' immediately at the start and/or end of the directive. + + [% FOREACH user = userlist %] + [%- user -%] + [% END %] + +The '-' characters activate both PRE_CHOMP and POST_CHOMP for the one +directive '[%- name -%]'. Thus, the template will be processed as if +written: + + [% FOREACH user = userlist %][% user %][% END %] + +Note that this is the same as if PRE_CHOMP and POST_CHOMP were set +to CHOMP_ALL; the only way to get the CHOMP_COLLAPSE behavior is +to set PRE_CHOMP or POST_CHOMP accordingly. If PRE_CHOMP or POST_CHOMP +is already set to CHOMP_COLLAPSE, using '-' will give you CHOMP_COLLAPSE +behavior, not CHOMP_ALL behavior. + +Similarly, '+' characters can be used to disable PRE_CHOMP or +POST_CHOMP (i.e. leave the whitespace/newline intact) options on a +per-directive basis. + + [% FOREACH user = userlist %] + User: [% user +%] + [% END %] + +With POST_CHOMP enabled, the above example would be parsed as if written: + + [% FOREACH user = userlist %]User: [% user %] + [% END %] + + + + + +=item INTERPOLATE + +The INTERPOLATE flag, when set to any true value will cause variable +references in plain text (i.e. not surrounded by START_TAG and END_TAG) +to be recognised and interpolated accordingly. + + my $parser = Template::Parser->new({ + INTERPOLATE => 1, + }); + +Variables should be prefixed by a '$' to identify them. Curly braces +can be used in the familiar Perl/shell style to explicitly scope the +variable name where required. + + # INTERPOLATE => 0 + <a href="http://[% server %]/[% help %]"> + <img src="[% images %]/help.gif"></a> + [% myorg.name %] + + # INTERPOLATE => 1 + <a href="http://$server/$help"> + <img src="$images/help.gif"></a> + $myorg.name + + # explicit scoping with { } + <img src="$images/${icon.next}.gif"> + +Note that a limitation in Perl's regex engine restricts the maximum length +of an interpolated template to around 32 kilobytes or possibly less. Files +that exceed this limit in size will typically cause Perl to dump core with +a segmentation fault. If you routinely process templates of this size +then you should disable INTERPOLATE or split the templates in several +smaller files or blocks which can then be joined backed together via +PROCESS or INCLUDE. + + + + + + + +=item ANYCASE + +By default, directive keywords should be expressed in UPPER CASE. The +ANYCASE option can be set to allow directive keywords to be specified +in any case. + + # ANYCASE => 0 (default) + [% INCLUDE foobar %] # OK + [% include foobar %] # ERROR + [% include = 10 %] # OK, 'include' is a variable + + # ANYCASE => 1 + [% INCLUDE foobar %] # OK + [% include foobar %] # OK + [% include = 10 %] # ERROR, 'include' is reserved word + +One side-effect of enabling ANYCASE is that you cannot use a variable +of the same name as a reserved word, regardless of case. The reserved +words are currently: + + GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER + IF UNLESS ELSE ELSIF FOR FOREACH WHILE SWITCH CASE + USE PLUGIN FILTER MACRO PERL RAWPERL BLOCK META + TRY THROW CATCH FINAL NEXT LAST BREAK RETURN STOP + CLEAR TO STEP AND OR NOT MOD DIV END + + +The only lower case reserved words that cannot be used for variables, +regardless of the ANYCASE option, are the operators: + + and or not mod div + + + + + + + + +=item V1DOLLAR + +In version 1 of the Template Toolkit, an optional leading '$' could be placed +on any template variable and would be silently ignored. + + # VERSION 1 + [% $foo %] === [% foo %] + [% $hash.$key %] === [% hash.key %] + +To interpolate a variable value the '${' ... '}' construct was used. +Typically, one would do this to index into a hash array when the key +value was stored in a variable. + +example: + + my $vars = { + users => { + aba => { name => 'Alan Aardvark', ... }, + abw => { name => 'Andy Wardley', ... }, + ... + }, + uid => 'aba', + ... + }; + + $template->process('user/home.html', $vars) + || die $template->error(), "\n"; + +'user/home.html': + + [% user = users.${uid} %] # users.aba + Name: [% user.name %] # Alan Aardvark + +This was inconsistent with double quoted strings and also the +INTERPOLATE mode, where a leading '$' in text was enough to indicate a +variable for interpolation, and the additional curly braces were used +to delimit variable names where necessary. Note that this use is +consistent with UNIX and Perl conventions, among others. + + # double quoted string interpolation + [% name = "$title ${user.name}" %] + + # INTERPOLATE = 1 + <img src="$images/help.gif"></a> + <img src="$images/${icon.next}.gif"> + +For version 2, these inconsistencies have been removed and the syntax +clarified. A leading '$' on a variable is now used exclusively to +indicate that the variable name should be interpolated +(e.g. subsituted for its value) before being used. The earlier example +from version 1: + + # VERSION 1 + [% user = users.${uid} %] + Name: [% user.name %] + +can now be simplified in version 2 as: + + # VERSION 2 + [% user = users.$uid %] + Name: [% user.name %] + +The leading dollar is no longer ignored and has the same effect of +interpolation as '${' ... '}' in version 1. The curly braces may +still be used to explicitly scope the interpolated variable name +where necessary. + +e.g. + + [% user = users.${me.id} %] + Name: [% user.name %] + +The rule applies for all variables, both within directives and in +plain text if processed with the INTERPOLATE option. This means that +you should no longer (if you ever did) add a leading '$' to a variable +inside a directive, unless you explicitly want it to be interpolated. + +One obvious side-effect is that any version 1 templates with variables +using a leading '$' will no longer be processed as expected. Given +the following variable definitions, + + [% foo = 'bar' + bar = 'baz' + %] + +version 1 would interpret the following as: + + # VERSION 1 + [% $foo %] => [% GET foo %] => bar + +whereas version 2 interprets it as: + + # VERSION 2 + [% $foo %] => [% GET $foo %] => [% GET bar %] => baz + +In version 1, the '$' is ignored and the value for the variable 'foo' is +retrieved and printed. In version 2, the variable '$foo' is first interpolated +to give the variable name 'bar' whose value is then retrieved and printed. + +The use of the optional '$' has never been strongly recommended, but +to assist in backwards compatibility with any version 1 templates that +may rely on this "feature", the V1DOLLAR option can be set to 1 +(default: 0) to revert the behaviour and have leading '$' characters +ignored. + + my $parser = Template::Parser->new({ + V1DOLLAR => 1, + }); + + + + + + +=item GRAMMAR + +The GRAMMAR configuration item can be used to specify an alternate +grammar for the parser. This allows a modified or entirely new +template language to be constructed and used by the Template Toolkit. + +Source templates are compiled to Perl code by the Template::Parser +using the Template::Grammar (by default) to define the language +structure and semantics. Compiled templates are thus inherently +"compatible" with each other and there is nothing to prevent any +number of different template languages being compiled and used within +the same Template Toolkit processing environment (other than the usual +time and memory constraints). + +The Template::Grammar file is constructed from a YACC like grammar +(using Parse::YAPP) and a skeleton module template. These files are +provided, along with a small script to rebuild the grammar, in the +'parser' sub-directory of the distribution. You don't have to know or +worry about these unless you want to hack on the template language or +define your own variant. There is a README file in the same directory +which provides some small guidance but it is assumed that you know +what you're doing if you venture herein. If you grok LALR parsers, +then you should find it comfortably familiar. + +By default, an instance of the default Template::Grammar will be +created and used automatically if a GRAMMAR item isn't specified. + + use MyOrg::Template::Grammar; + + my $parser = Template::Parser->new({ + GRAMMAR = MyOrg::Template::Grammar->new(); + }); + + + +=item DEBUG + +The DEBUG option can be used to enable various debugging features +of the Template::Parser module. + + use Template::Constants qw( :debug ); + + my $template = Template->new({ + DEBUG => DEBUG_PARSER | DEBUG_DIRS, + }); + +The DEBUG value can include any of the following. Multiple values +should be combined using the logical OR operator, '|'. + +=over 4 + +=item DEBUG_PARSER + +This flag causes the L<Template::Parser|Template::Parser> to generate +debugging messages that show the Perl code generated by parsing and +compiling each template. + +=item DEBUG_DIRS + +This option causes the Template Toolkit to generate comments +indicating the source file, line and original text of each directive +in the template. These comments are embedded in the template output +using the format defined in the DEBUG_FORMAT configuration item, or a +simple default format if unspecified. + +For example, the following template fragment: + + + Hello World + +would generate this output: + + ## input text line 1 : ## + Hello + ## input text line 2 : World ## + World + + +=back + + + + +=back + +=head2 parse($text) + +The parse() method parses the text passed in the first parameter and +returns a reference to a Template::Document object which contains the +compiled representation of the template text. On error, undef is +returned. + +Example: + + $doc = $parser->parse($text) + || die $parser->error(); + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + + + +=head1 VERSION + +2.81, 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. + + + +The original Template::Parser module was derived from a standalone +parser generated by version 0.16 of the Parse::Yapp module. The +following copyright notice appears in the Parse::Yapp documentation. + + The Parse::Yapp module and its related modules and shell + scripts are copyright (c) 1998 Francois Desarmenien, + France. All rights reserved. + + You may use and distribute them under the terms of either + the GNU General Public License or the Artistic License, as + specified in the Perl README file. + +=head1 SEE ALSO + +L<Template|Template>, L<Template::Grammar|Template::Grammar>, L<Template::Directive|Template::Directive> + diff --git a/lib/Template/Plugin.pm b/lib/Template/Plugin.pm new file mode 100644 index 0000000..a6c9df2 --- /dev/null +++ b/lib/Template/Plugin.pm @@ -0,0 +1,409 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin +# +# DESCRIPTION +# +# Module defining a base class for a plugin object which can be loaded +# and instantiated via the USE directive. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Plugin.pm,v 2.65 2004/01/13 16:19:15 abw Exp $ +# +#============================================================================ + +package Template::Plugin; + +require 5.004; + +use strict; +use Template::Base; + +use vars qw( $VERSION $DEBUG $ERROR $AUTOLOAD ); +use base qw( Template::Base ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.65 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0; + + +#======================================================================== +# ----- CLASS METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# load() +# +# Class method called when the plugin module is first loaded. It +# returns the name of a class (by default, its own class) or a prototype +# object which will be used to instantiate new objects. The new() +# method is then called against the class name (class method) or +# prototype object (object method) to create a new instances of the +# object. +#------------------------------------------------------------------------ + +sub load { + return $_[0]; +} + + +#------------------------------------------------------------------------ +# new($context, $delegate, @params) +# +# Object constructor which is called by the Template::Context to +# instantiate a new Plugin object. This base class constructor is +# used as a general mechanism to load and delegate to other Perl +# modules. The context is passed as the first parameter, followed by +# a reference to a delegate object or the name of the module which +# should be loaded and instantiated. Any additional parameters passed +# to the USE directive are forwarded to the new() constructor. +# +# A plugin object is returned which has an AUTOLOAD method to delegate +# requests to the underlying object. +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + bless { + }, $class; +} + +sub old_new { + my ($class, $context, $delclass, @params) = @_; + my ($delegate, $delmod); + + return $class->error("no context passed to $class constructor\n") + unless defined $context; + + if (ref $delclass) { + # $delclass contains a reference to a delegate object + $delegate = $delclass; + } + else { + # delclass is the name of a module to load and instantiate + ($delmod = $delclass) =~ s|::|/|g; + + eval { + require "$delmod.pm"; + $delegate = $delclass->new(@params) + || die "failed to instantiate $delclass object\n"; + }; + return $class->error($@) if $@; + } + + bless { + _CONTEXT => $context, + _DELEGATE => $delegate, + _PARAMS => \@params, + }, $class; +} + + +#------------------------------------------------------------------------ +# fail($error) +# +# Version 1 error reporting function, now replaced by error() inherited +# from Template::Base. Raises a "deprecated function" warning and then +# calls error(). +#------------------------------------------------------------------------ + +sub fail { + my $class = shift; + my ($pkg, $file, $line) = caller(); + warn "Template::Plugin::fail() is deprecated at $file line $line. Please use error()\n"; + $class->error(@_); +} + + +#======================================================================== +# ----- OBJECT METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# AUTOLOAD +# +# General catch-all method which delegates all calls to the _DELEGATE +# object. +#------------------------------------------------------------------------ + +sub OLD_AUTOLOAD { + my $self = shift; + my $method = $AUTOLOAD; + + $method =~ s/.*:://; + return if $method eq 'DESTROY'; + + if (ref $self eq 'HASH') { + my $delegate = $self->{ _DELEGATE } || return; + return $delegate->$method(@_); + } + my ($pkg, $file, $line) = caller(); +# warn "no such '$method' method called on $self at $file line $line\n"; + return undef; +} + + +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 - Base class for Template Toolkit plugins + +=head1 SYNOPSIS + + package MyOrg::Template::Plugin::MyPlugin; + use base qw( Template::Plugin ); + use Template::Plugin; + use MyModule; + + sub new { + my $class = shift; + my $context = shift; + bless { + ... + }, $class; + } + +=head1 DESCRIPTION + +A "plugin" for the Template Toolkit is simply a Perl module which +exists in a known package location (e.g. Template::Plugin::*) and +conforms to a regular standard, allowing it to be loaded and used +automatically. + +The Template::Plugin module defines a base class from which other +plugin modules can be derived. A plugin does not have to be derived +from Template::Plugin but should at least conform to its object-oriented +interface. + +It is recommended that you create plugins in your own package namespace +to avoid conflict with toolkit plugins. e.g. + + package MyOrg::Template::Plugin::FooBar; + +Use the PLUGIN_BASE option to specify the namespace that you use. e.g. + + use Template; + my $template = Template->new({ + PLUGIN_BASE => 'MyOrg::Template::Plugin', + }); + +=head1 PLUGIN API + +The following methods form the basic interface between the Template +Toolkit and plugin modules. + +=over 4 + +=item load($context) + +This method is called by the Template Toolkit when the plugin module +is first loaded. It is called as a package method and thus implicitly +receives the package name as the first parameter. A reference to the +Template::Context object loading the plugin is also passed. The +default behaviour for the load() method is to simply return the class +name. The calling context then uses this class name to call the new() +package method. + + package MyPlugin; + + sub load { # called as MyPlugin->load($context) + my ($class, $context) = @_; + return $class; # returns 'MyPlugin' + } + +=item new($context, @params) + +This method is called to instantiate a new plugin object for the USE +directive. It is called as a package method against the class name +returned by load(). A reference to the Template::Context object creating +the plugin is passed, along with any additional parameters specified in +the USE directive. + + sub new { # called as MyPlugin->new($context) + my ($class, $context, @params) = @_; + bless { + _CONTEXT => $context, + }, $class; # returns blessed MyPlugin object + } + +=item error($error) + +This method, inherited from the Template::Base module, is used for +reporting and returning errors. It can be called as a package method +to set/return the $ERROR package variable, or as an object method to +set/return the object _ERROR member. When called with an argument, it +sets the relevant variable and returns undef. When called without an +argument, it returns the value of the variable. + + sub new { + my ($class, $context, $dsn) = @_; + + return $class->error('No data source specified') + unless $dsn; + + bless { + _DSN => $dsn, + }, $class; + } + + ... + + my $something = MyModule->new() + || die MyModule->error(), "\n"; + + $something->do_something() + || die $something->error(), "\n"; + +=back + +=head1 DEEPER MAGIC + +The Template::Context object that handles the loading and use of +plugins calls the new() and error() methods against the package name +returned by the load() method. In pseudo-code terms, it might look +something like this: + + $class = MyPlugin->load($context); # returns 'MyPlugin' + + $object = $class->new($context, @params) # MyPlugin->new(...) + || die $class->error(); # MyPlugin->error() + +The load() method may alterately return a blessed reference to an +object instance. In this case, new() and error() are then called as +I<object> methods against that prototype instance. + + package YourPlugin; + + sub load { + my ($class, $context) = @_; + bless { + _CONTEXT => $context, + }, $class; + } + + sub new { + my ($self, $context, @params) = @_; + return $self; + } + +In this example, we have implemented a 'Singleton' plugin. One object +gets created when load() is called and this simply returns itself for +each call to new(). + +Another implementation might require individual objects to be created +for every call to new(), but with each object sharing a reference to +some other object to maintain cached data, database handles, etc. +This pseudo-code example demonstrates the principle. + + package MyServer; + + sub load { + my ($class, $context) = @_; + bless { + _CONTEXT => $context, + _CACHE => { }, + }, $class; + } + + sub new { + my ($self, $context, @params) = @_; + MyClient->new($self, @params); + } + + sub add_to_cache { ... } + + sub get_from_cache { ... } + + + package MyClient; + + sub new { + my ($class, $server, $blah) = @_; + bless { + _SERVER => $server, + _BLAH => $blah, + }, $class; + } + + sub get { + my $self = shift; + $self->{ _SERVER }->get_from_cache(@_); + } + + sub put { + my $self = shift; + $self->{ _SERVER }->add_to_cache(@_); + } + +When the plugin is loaded, a MyServer instance is created. The new() +method is called against this object which instantiates and returns a +MyClient object, primed to communicate with the creating MyServer. + +=head1 Template::Plugin Delegation + +As of version 2.01, the Template::Plugin module no longer provides an +AUTOLOAD method to delegate to other objects or classes. This was a +badly designed feature that caused more trouble than good. You can +easily add your own AUTOLOAD method to perform delegation if you +require this kind of functionality. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.65, 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|Template>, L<Template::Plugins|Template::Plugins>, L<Template::Context|Template::Context> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/Autoformat.pm b/lib/Template/Plugin/Autoformat.pm new file mode 100644 index 0000000..b7153e4 --- /dev/null +++ b/lib/Template/Plugin/Autoformat.pm @@ -0,0 +1,242 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Autoformat +# +# DESCRIPTION +# Plugin interface to Damian Conway's Text::Autoformat module. +# +# AUTHORS +# Robert McArthur <mcarthur@dstc.edu.au> +# - original plugin code +# +# Andy Wardley <abw@kfs.org> +# - added FILTER registration, support for forms and some additional +# documentation +# +# COPYRIGHT +# Copyright (C) 2000 Robert McArthur & 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: Autoformat.pm,v 2.64 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Autoformat; + +require 5.004; + +use strict; +use vars qw( $VERSION ); +use base qw( Template::Plugin ); +use Template::Plugin; +use Text::Autoformat; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); + +sub new { + my ($class, $context, $options) = @_; + my $filter_factory; + my $plugin; + + if ($options) { + # create a closure to generate filters with additional options + $filter_factory = sub { + my $context = shift; + my $filtopt = ref $_[-1] eq 'HASH' ? pop : { }; + @$filtopt{ keys %$options } = values %$options; + return sub { + tt_autoformat(@_, $filtopt); + }; + }; + + # and a closure to represent the plugin + $plugin = sub { + my $plugopt = ref $_[-1] eq 'HASH' ? pop : { }; + @$plugopt{ keys %$options } = values %$options; + tt_autoformat(@_, $plugopt); + }; + } + else { + # simple filter factory closure (no legacy options from constructor) + $filter_factory = sub { + my $context = shift; + my $filtopt = ref $_[-1] eq 'HASH' ? pop : { }; + return sub { + tt_autoformat(@_, $filtopt); + }; + }; + + # plugin without options can be static + $plugin = \&tt_autoformat; + } + + # now define the filter and return the plugin + $context->define_filter('autoformat', [ $filter_factory => 1 ]); + return $plugin; +} + +sub tt_autoformat { + my $options = ref $_[-1] eq 'HASH' ? pop : { }; + my $form = $options->{ form }; + my $out = $form ? Text::Autoformat::form($options, $form, @_) + : Text::Autoformat::autoformat(join('', @_), $options); + return $out; +} + +__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::Autoformat - Interface to Text::Autoformat module + +=head1 SYNOPSIS + + [% USE autoformat(options) %] + + [% autoformat(text, more_text, ..., options) %] + + [% FILTER autoformat(options) %] + a block of text + [% END %] + +=head1 EXAMPLES + + # define some text for the examples + [% text = BLOCK %] + Be not afeard. The isle is full of noises, sounds and sweet + airs that give delight but hurt not. + [% END %] + + # pass options to constructor... + [% USE autoformat(case => 'upper') %] + [% autoformat(text) %] + + # and/or pass options to the autoformat subroutine itself + [% USE autoformat %] + [% autoformat(text, case => 'upper') %] + + # using the autoformat filter + [% USE autoformat(left => 10, right => 30) %] + [% FILTER autoformat %] + Be not afeard. The isle is full of noises, sounds and sweet + airs that give delight but hurt not. + [% END %] + + # another filter example with configuration options + [% USE autoformat %] + [% FILTER autoformat(left => 20) %] + Be not afeard. The isle is full of noises, sounds and sweet + airs that give delight but hurt not. + [% END %] + + # another FILTER example, defining a 'poetry' filter alias + [% USE autoformat %] + [% text FILTER poetry = autoformat(left => 20, right => 40) %] + + # reuse the 'poetry' filter alias + [% text FILTER poetry %] + + # shorthand form ('|' is an alias for 'FILTER') + [% text | autoformat %] + + # using forms + [% USE autoformat(form => '>>>>.<<<', numeric => 'AllPlaces') %] + [% autoformat(10, 20.32, 11.35) %] + +=head1 DESCRIPTION + +The autoformat plugin is an interface to Damian Conway's Text::Autoformat +Perl module which provides advanced text wrapping and formatting. + +Configuration options may be passed to the plugin constructor via the +USE directive. + + [% USE autoformat(right => 30) %] + +The autoformat subroutine can then be called, passing in text items which +will be wrapped and formatted according to the current configuration. + + [% autoformat('The cat sat on the mat') %] + +Additional configuration items can be passed to the autoformat subroutine +and will be merged with any existing configuration specified via the +constructor. + + [% autoformat(text, left => 20) %] + +Configuration options are passed directly to the Text::Autoformat plugin. +At the time of writing, the basic configuration items are: + + left left margin (default: 1) + right right margin (default 72) + justify justification as one of 'left', 'right', 'full' + or 'centre' (default: left) + case case conversion as one of 'lower', 'upper', + 'sentence', 'title', or 'highlight' (default: none) + squeeze squeeze whitespace (default: enabled) + +The plugin also accepts a 'form' item which can be used to define a +format string. When a form is defined, the plugin will call the +underlying form() subroutine in preference to autoformat(). + + [% USE autoformat(form => '>>>>.<<') %] + [% autoformat(123.45, 666, 3.14) %] + +Additional configuration items relevant to forms can also be specified. + + [% USE autoformat(form => '>>>>.<<', numeric => 'AllPlaces') %] + [% autoformat(123.45, 666, 3.14) %] + +These can also be passed directly to the autoformat subroutine. + + [% USE autoformat %] + [% autoformat( 123.45, 666, 3.14, + form => '>>>>.<<', + numeric => 'AllPlaces' ) + %] + +See L<Text::Autoformat> for further details. + +=head1 AUTHORS + +Robert McArthur E<lt>mcarthur@dstc.edu.auE<gt> wrote the original plugin +code, with some modifications and additions from Andy Wardley +E<lt>abw@wardley.orgE<gt>. + +Damian Conway E<lt>damian@conway.orgE<gt> wrote the Text::Autoformat +module (in his copious spare time :-) which does all the clever stuff. + +=head1 VERSION + +2.64, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + + + +=head1 COPYRIGHT + +Copyright (C) 2000 Robert McArthur & 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::Plugin|Template::Plugin>, L<Text::Autoformat|Text::Autoformat> + diff --git a/lib/Template/Plugin/CGI.pm b/lib/Template/Plugin/CGI.pm new file mode 100644 index 0000000..53b19d8 --- /dev/null +++ b/lib/Template/Plugin/CGI.pm @@ -0,0 +1,168 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::CGI +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the CGI.pm module. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: CGI.pm,v 2.64 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::CGI; + +require 5.004; + +use strict; +use vars qw( $VERSION ); +use base qw( Template::Plugin ); +use Template::Plugin; +use CGI; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); + +sub new { + my $class = shift; + my $context = shift; + CGI->new(@_); +} + +package CGI; + +sub params { + my $self = shift; + local $" = ', '; + + return $self->{ _TT_PARAMS } ||= do { + # must call Vars() in a list context to receive + # plain list of key/vals rather than a tied hash + my $params = { $self->Vars() }; + + # convert any null separated values into lists + @$params{ keys %$params } = map { + /\0/ ? [ split /\0/ ] : $_ + } values %$params; + + $params; + }; +} + +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::CGI - Interface to the CGI module + +=head1 SYNOPSIS + + [% USE CGI %] + [% CGI.param('parameter') %] + + [% USE things = CGI %] + [% things.param('name') %] + + # see CGI docs for other methods provided by the CGI object + +=head1 DESCRIPTION + +This is a very simple Template Toolkit Plugin interface to the CGI module. +A CGI object will be instantiated via the following directive: + + [% USE CGI %] + +CGI methods may then be called as follows: + + [% CGI.header %] + [% CGI.param('parameter') %] + +An alias can be used to provide an alternate name by which the object should +be identified. + + [% USE mycgi = CGI %] + [% mycgi.start_form %] + [% mycgi.popup_menu({ Name => 'Color' + Values => [ 'Green' 'Black' 'Brown' ] }) %] + +Parenthesised parameters to the USE directive will be passed to the plugin +constructor: + + [% USE cgiprm = CGI('uid=abw&name=Andy+Wardley') %] + [% cgiprm.param('uid') %] + +=head1 METHODS + +In addition to all the methods supported by the CGI module, this +plugin defines the following. + +=head2 params() + +This method returns a reference to a hash of all the CGI parameters. +Any parameters that have multiple values will be returned as lists. + + [% USE CGI('user=abw&item=foo&item=bar') %] + + [% CGI.params.user %] # abw + [% CGI.params.item.join(', ') %] # foo, bar + +=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>, L<CGI|CGI> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/DBI.pm b/lib/Template/Plugin/DBI.pm new file mode 100644 index 0000000..b916bfc --- /dev/null +++ b/lib/Template/Plugin/DBI.pm @@ -0,0 +1,947 @@ +#============================================================================== +# +# Template::Plugin::DBI +# +# DESCRIPTION +# A Template Toolkit plugin to provide access to a DBI data source. +# +# AUTHORS +# Original version by Simon Matthews <sam@knowledgepool.com> +# with some reworking by Andy Wardley <abw@kfs.org> and other +# contributions from Craig Barratt <craig@arraycomm.com>, +# Dave Hodgkinson <daveh@davehodgkinson.com> and Rafael Kitover +# <caelum@debian.org> +# +# COPYRIGHT +# Copyright (C) 1999-2000 Simon Matthews. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +# REVISION +# $Id: DBI.pm,v 2.62 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================== + +package Template::Plugin::DBI; + +require 5.004; + +use strict; +use Template::Plugin; +use Template::Exception; +use DBI; + +use vars qw( $VERSION $DEBUG $QUERY $ITERATOR ); +use base qw( Template::Plugin ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.62 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0 unless defined $DEBUG; +$QUERY = 'Template::Plugin::DBI::Query'; +$ITERATOR = 'Template::Plugin::DBI::Iterator'; + +# alias _connect() to connect() for backwards compatability +*_connect = \*connect; + + +#------------------------------------------------------------------------ +# new($context, @params) +# +# Constructor which returns a reference to a new DBI plugin object. +# A connection string (dsn), user name and password may be passed as +# positional arguments or a hash array of connection parameters can be +# passed to initialise a connection. Otherwise, an unconnected DBI +# plugin object is returned. +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $context = shift; + my $self = ref $class ? $class : bless { + _CONTEXT => $context, + _STH => [ ], + }, $class; + + $self->connect(@_) if @_; + + return $self; +} + + +#------------------------------------------------------------------------ +# connect( $data_source, $username, $password, $attributes ) +# connect( { data_source => 'dbi:driver:database' +# username => 'foo' +# password => 'bar' } ) +# +# Opens a DBI connection for the plugin. +#------------------------------------------------------------------------ + +sub connect { + my $self = shift; + my $params = ref $_[-1] eq 'HASH' ? pop(@_) : { }; + my ($dbh, $dsn, $user, $pass, $klobs); + + # set debug flag + $DEBUG = $params->{ debug } if exists $params->{ debug }; + $self->{ _DEBUG } = $params->{ debug } || 0; + + # fetch 'dbh' named paramater or use positional arguments or named + # parameters to specify 'dsn', 'user' and 'pass' + + if ($dbh = $params->{ dbh }) { + # disconnect any existing database handle that we previously opened + $self->{ _DBH }->disconnect() + if $self->{ _DBH } && $self->{ _DBH_CONNECT }; + + # store new dbh but leave _DBH_CONNECT false to prevent us + # from automatically closing it in the future + $self->{ _DBH } = $dbh; + $self->{ _DBH_CONNECT } = 0; + } + else { + + # certain Perl programmers are known to have problems with short + # term memory loss (see Tie::Hash::Cannabinol) so we let the poor + # blighters fumble any kind of argument that looks like it might + # identify the database + + $dsn = shift + || $params->{ data_source } + || $params->{ database } + || $params->{ connect } + || $params->{ dsn } + || $params->{ db } + || $ENV{DBI_DSN} + || return $self->_throw('data source not defined'); + + # add 'dbi:' prefix if it's not there + $dsn = "dbi:$dsn" unless $dsn =~ /^dbi:/i; + + $user = shift + || $params->{ username } + || $params->{ user }; + + $pass = shift + || $params->{ password } + || $params->{ pass }; + + # save connection data because we might need it later to do a tie() + @$self{ qw( _DSN _USER _PASS ) } = ($dsn, $user, $pass); + + # reuse existing database handle if connection params match + my $connect = join(':', $dsn || '', $user || '', $pass || ''); + return '' + if $self->{ _DBH } && $self->{ _DBH_CONNECT } eq $connect; + + # otherwise disconnect any existing database handle that we opened + $self->{ _DBH }->disconnect() + if $self->{ _DBH } && $self->{ _DBH_CONNECT }; + + # don't need DBI to automatically print errors because all calls go + # via this plugin interface and we always check return values + $params->{ PrintError } = 0 + unless defined $params->{ PrintError }; + + $self->{ _DBH } = DBI->connect_cached( $dsn, $user, $pass, $params ) + || return $self->_throw("DBI connect failed: $DBI::errstr"); + + # store the connection parameters + $self->{ _DBH_CONNECT } = $connect; + } + + return ''; +} + + +#------------------------------------------------------------------------ +# disconnect() +# +# Disconnects the current active database connection. +#------------------------------------------------------------------------ + +sub disconnect { + my $self = shift; + $self->{ _DBH }->disconnect() + if $self->{ _DBH }; + delete $self->{ _DBH }; + return ''; +} + + +#------------------------------------------------------------------------ +# tie( $table, $key ) +# +# Return a hash tied to a table in the database, indexed by the specified +# key. +#------------------------------------------------------------------------ + +sub tie { + my $self = shift; + my $params = ref $_[-1] eq 'HASH' ? pop(@_) : { }; + my ($table, $key, $klobs, $debug, %hash); + + eval { require Tie::DBI }; + $self->_throw("failed to load Tie::DBI module: $@") if $@; + + $table = shift + || $params->{ table } + || $self->_throw('table not defined'); + + $key = shift + || $params->{ key } + || $self->_throw('key not defined'); + + # Achtung der Klobberman! + $klobs = $params->{ clobber }; + $klobs = $params->{ CLOBBER } unless defined $klobs; + + # going the extra mile to allow user to use UPPER or lower case or + # inherit internel debug flag set by connect() + $debug = $params->{ debug }; + $debug = $params->{ DEBUG } unless defined $debug; + $debug = $self->{ _DEBUG } unless defined $debug; + + tie %hash, 'Tie::DBI', { + %$params, # any other Tie::DBI options like DEBUG, WARN, etc + db => $self->{ _DBH } || $self->{ _DSN }, + user => $self->{ _USER }, + password => $self->{ _PASS }, + table => $table, + key => $key, + CLOBBER => $klobs || 0, + DEBUG => $debug || 0, + }; + + return \%hash; +} + + +#------------------------------------------------------------------------ +# prepare($sql) +# +# Prepare a query and store the live statement handle internally for +# subsequent execute() calls. +#------------------------------------------------------------------------ + +sub prepare { + my $self = shift; + my $sql = shift || return undef; + + my $sth = $self->dbh->prepare($sql) + || return $self->_throw("DBI prepare failed: $DBI::errstr"); + + # create wrapper object around handle to return to template client + $sth = $QUERY->new($sth); + push(@{ $self->{ _STH } }, $sth); + + return $sth; +} + + +#------------------------------------------------------------------------ +# execute() +# +# Calls execute() on the most recent statement created via prepare(). +#------------------------------------------------------------------------ + +sub execute { + my $self = shift; + + my $sth = $self->{ _STH }->[-1] + || return $self->_throw('no query prepared'); + + $sth->execute(@_); +} + + +#------------------------------------------------------------------------ +# query($sql, @params) +# +# Prepares and executes a SQL query. +#------------------------------------------------------------------------ + +sub query { + my $self = shift; + my $sql = shift; + + $self->prepare($sql)->execute(@_); +} + + +#------------------------------------------------------------------------ +# do($sql, \%attr, @bind) +# +# Prepares and executes a SQL statement. +#------------------------------------------------------------------------ + +sub do { + my $self = shift; + + return $self->dbh->do(@_) + || $self->_throw("DBI do failed: $DBI::errstr"); +} + + +#------------------------------------------------------------------------ +# quote($value [, $data_type ]) +# +# Returns a quoted string (correct for the connected database) from the +# value passed in. +#------------------------------------------------------------------------ + +sub quote { + my $self = shift; + $self->dbh->quote(@_); +} + + +#------------------------------------------------------------------------ +# dbh() +# +# Internal method to retrieve the database handle belonging to the +# instance or attempt to create a new one using connect. +#------------------------------------------------------------------------ + +sub dbh { + my $self = shift; + + return $self->{ _DBH } || do { + $self->connect; + $self->{ _DBH }; + }; +} + + +#------------------------------------------------------------------------ +# DESTROY +# +# Called automatically when the plugin object goes out of scope to +# disconnect the database handle cleanly +#------------------------------------------------------------------------ + +sub DESTROY { + my $self = shift; + delete($self->{ _STH }); # first DESTROY any queries + $self->{ _DBH }->disconnect() + if $self->{ _DBH } && $self->{ _DBH_CONNECT }; +} + + +#------------------------------------------------------------------------ +# _throw($error) +# +# Raise an error by throwing it via die() as a Template::Exception +# object of type 'DBI'. +#------------------------------------------------------------------------ + +sub _throw { + my $self = shift; + my $error = shift || die "DBI throw() called without an error string\n"; + + # throw error as DBI exception + die (Template::Exception->new('DBI', $error)); +} + + +#======================================================================== +# Template::Plugin::DBI::Query +#======================================================================== + +package Template::Plugin::DBI::Query; +use vars qw( $DEBUG $ITERATOR ); + +*DEBUG = \$Template::Plugin::DBI::DEBUG; +*ITERATOR = \$Template::Plugin::DBI::ITERATOR; + + +sub new { + my ($class, $sth) = @_; + bless \$sth, $class; +} + +sub execute { + my $self = shift; + + $$self->execute(@_) + || return Template::Plugin::DBI->_throw("execute failed: $DBI::errstr"); + + $ITERATOR->new($$self); +} + +sub DESTROY { + my $self = shift; + $$self->finish(); +} + + +#======================================================================== +# Template::Plugin::DBI::Iterator; +#======================================================================== + +package Template::Plugin::DBI::Iterator; + +use Template::Iterator; +use base qw( Template::Iterator ); +use vars qw( $DEBUG ); + +*DEBUG = \$Template::Plugin::DBI::DEBUG; + + +sub new { + my ($class, $sth, $params) = @_; + + my $rows = $sth->rows(); + + my $self = bless { + _STH => $sth, + SIZE => $rows, + MAX => $rows - 1, + }, $class; + + + return $self; +} + + +#------------------------------------------------------------------------ +# get_first() +# +# Initialises iterator to read from statement handle. We maintain a +# one-record lookahead buffer to allow us to detect if the current +# record is the last in the series. +#------------------------------------------------------------------------ + +sub get_first { + my $self = shift; + $self->{ _STARTED } = 1; + + # set some status variables into $self + @$self{ qw( PREV ITEM FIRST LAST COUNT INDEX ) } + = ( undef, undef, 2, 0, 0, -1 ); + + # support 'number' as an alias for 'count' for backwards compatability + $self->{ NUMBER } = 0; + + print STDERR "get_first() called\n" if $DEBUG; + + # get the first row + $self->_fetchrow(); + + print STDERR "get_first() calling get_next()\n" if $DEBUG; + + return $self->get_next(); +} + + +#------------------------------------------------------------------------ +# get_next() +# +# Called to read remaining result records from statement handle. +#------------------------------------------------------------------------ + +sub get_next { + my $self = shift; + my ($data, $fixup); + + # increment the 'index' and 'count' counts + $self->{ INDEX }++; + $self->{ COUNT }++; + $self->{ NUMBER }++; # 'number' is old name for 'count' + + # decrement the 'first-record' flag + $self->{ FIRST }-- if $self->{ FIRST }; + + # we should have a row already cache in NEXT + return (undef, Template::Constants::STATUS_DONE) + unless $data = $self->{ NEXT }; + + # set PREV to be current ITEM from last iteration + $self->{ PREV } = $self->{ ITEM }; + + # look ahead to the next row so that the rowcache is refilled + $self->_fetchrow(); + + $self->{ ITEM } = $data; + return ($data, Template::Constants::STATUS_OK); +} + + +sub get { + my $self = shift; + my ($data, $error); + + ($data, $error) = $self->{ _STARTED } + ? $self->get_next() : $self->get_first(); + + return $data; +} + + +sub get_all { + my $self = shift; + my $sth = $self->{ _STH }; + my $error; + + my $data = $sth->fetchall_arrayref({}); + $self->throw($error) if ($error = $sth->err()); + unshift(@$data, $self->{ NEXT }) if $self->{ NEXT }; + $self->{ LAST } = 1; + $self->{ NEXT } = undef; + $sth->finish(); + + return $data; +} + + +#------------------------------------------------------------------------ +# _fetchrow() +# +# Retrieve a record from the statement handle and store in row cache. +#------------------------------------------------------------------------ + +sub _fetchrow { + my $self = shift; + my $sth = $self->{ _STH }; + + my $data = $sth->fetchrow_hashref() || do { + $self->{ LAST } = 1; + $self->{ NEXT } = undef; + $sth->finish(); + return; + }; + $self->{ NEXT } = $data; + return; +} + +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::DBI - Template interface to the DBI module + +=head1 SYNOPSIS + +Making an implicit database connection: + + # ...using positional arguments + [% USE DBI('dbi:driver:dbname', 'user', 'pass') %] + + # ...using named parameters + [% USE DBI( database = 'dbi:driver:dbname', + username = 'user', + password = 'pass' ) + %] + + # ...using short named parameters (4 lzy ppl and bad typsits) + [% USE DBI( db = 'driver:dbname', + user = 'user', + pass = 'pass' ) + %] + + # ...or an existing DBI database handle + [% USE DBI( dbh = my_dbh_ref ) %] + +Making explicit database connections: + + [% USE DBI %] + + [% DBI.connect(db, user, pass) %] + ... + + [% DBI.connect(new_db, new_user, new_pass) %] + ... + + [% DBI.disconnect %] # final disconnect is optional + +Making an automagical database connection using DBI_DSN environment variable: + + [% USE DBI %] + +Making database queries: + + # single step query + [% FOREACH user = DBI.query('SELECT * FROM users') %] + [% user.uid %] blah blah [% user.name %] etc. etc. + [% END %] + + # two stage prepare/execute + [% query = DBI.prepare('SELECT * FROM users WHERE uid = ?') %] + + [% FOREACH user = query.execute('sam') %] + ... + [% END %] + + [% FOREACH user = query.execute('abw') %] + ... + [% END %] + +Making non-SELECT statements: + + [% IF DBI.do("DELETE FROM users WHERE uid = '$uid'") %] + The user '[% uid %]' was successfully deleted. + [% END %] + +Using named DBI connections: + + [% USE one = DBI(...) %] + [% USE two = DBI(...) %] + + [% FOREACH item = one.query("SELECT ...etc...") %] + ... + [% END %] + + [% FOREACH item = two.query("SELECT ...etc...") %] + ... + [% END %] + +Tieing to a database table (via Tie::DBI): + + [% people = DBI.tie('users', 'uid') %] + + [% me = people.abw %] # => SELECT * FROM users WHERE uid='abw' + + I am [% me.name %] + + # clobber option allows table updates (see Tie::DBI) + [% people = DBI.tie('users', 'uid', clobber=1) %] + + [% people.abw.name = 'not a number' %] + + I am [% people.abw.name %] # I am a free man! + +=head1 DESCRIPTION + +This Template Toolkit plugin module provides an interface to the Perl +DBI/DBD modules, allowing you to integrate SQL queries into your +template documents. It also provides an interface via the Tie::DBI +module (if installed on your system) so that you can access database +records without having to embed any SQL in your templates. + +A DBI plugin object can be created as follows: + + [% USE DBI %] + +This creates an uninitialised DBI object. You can then open a connection +to a database using the connect() method. + + [% DBI.connect('dbi:driver:dbname', 'user', 'pass') %] + +The DBI connection can be opened when the plugin is created by passing +arguments to the constructor, called from the USE directive. + + [% USE DBI('dbi:driver:dbname', 'user', 'pass') %] + +You can also use named parameters to provide the data source connection +string, user name and password. + + [% USE DBI(database => 'dbi:driver:dbname', + username => 'user', + password => 'pass') %] + +For backwards compatability with previous versions of this plugin, you can +also spell 'database' as 'data_source'. + + [% USE DBI(data_source => 'dbi:driver:dbname', + username => 'user', + password => 'pass') %] + +Lazy Template hackers may prefer to use 'db', 'dsn' or 'connect' as a +shorthand form of the 'database' parameter, and 'user' and 'pass' as +shorthand forms of 'username' and 'password', respectively. You can +also drop the 'dbi:' prefix from the database connect string because +the plugin will add it on for you automagically. + + [% USE DBI(db => 'driver:dbname', + user => 'user', + pass => 'pass') %] + +Any additional DBI attributes can be specified as named parameters. +The 'PrintError' attribute defaults to 0 unless explicitly set true. + + [% USE DBI(db, user, pass, ChopBlanks=1) %] + +An alternate variable name can be provided for the plugin as per regular +Template Toolkit syntax: + + [% USE mydb = DBI('dbi:driver:dbname', 'user', 'pass') %] + + [% FOREACH item = mydb.query('SELECT * FROM users') %] + ... + [% END %] + +You can also specify the DBI plugin name in lower case if you prefer: + + [% USE dbi(dsn, user, pass) %] + + [% FOREACH item = dbi.query('SELECT * FROM users') %] + ... + [% END %] + +The disconnect() method can be called to explicitly disconnect the +current database, but this generally shouldn't be necessary as it is +called automatically when the plugin goes out of scope. You can call +connect() at any time to open a connection to another database. The +previous connection will be closed automatically. + +Internally, the DBI connect_cached() method is used instead of the +connect() method. This allows for connection caching in a server +environment, such as when the Template Toolkit is used from an Apache +mod_perl handler. In such a case, simply enable the mod_env module +and put in a line such as: + + SetEnv DBI_DSN "dbi:mysql:dbname;host=dbhost; + user=uname;password=pword" + +(NOTE: the string shown here is split across 2 lines for the sake of +reasonable page formatting, but you should specify it all as one long +string with no spaces or newlines). + +You can then use the DBI plugin without any parameters or the need +to explicitly call connect(). + +Once you've loaded a DBI plugin and opened a database connection using +one of the techniques shown above, you can then make queries on the database +using the familiar dotted notation: + + [% FOREACH user = DBI.query('SELECT * FROM users') %] + [% user.uid %] blah blah [% user.name %] etc. etc. + [% END %] + +The query() method prepares a query and executes it all in one go. +If you want to repeat a query with different parameters then you +can use a separate prepare/execute cycle. + + [% query = DBI.prepare('SELECT * FROM users WHERE uid = ?') %] + + [% FOREACH user = query.execute('sam') %] + ... + [% END %] + + [% FOREACH user = query.execute('abw') %] + ... + [% END %] + +The query() and execute() methods return an iterator object which +manages the result set returned. You can save a reference to the +iterator and access methods like size() to determine the number of +rows returned by a query. + + [% users = DBI.query('SELECT * FROM users') %] + [% users.size %] records returned + +or even + + [% DBI.query('SELECT * FROM users').size %] + +When used within a FOREACH loop, the iterator is always aliased to the +special C<loop> variable. This makes it possible to do things like this: + + [% FOREACH user = DBI.query('SELECT * FROM users') %] + [% loop.count %]/[% loop.size %]: [% user.name %] + [% END %] + +to generate a result set of the form: + + 1/3: Jerry Garcia + 2/3: Kurt Cobain + 3/3: Freddie Mercury + +See L<Template::Iterator> for further details on iterators and the +methods that they implement. + +The DBI plugin also provides the do() method to execute non-SELECT +statements like this: + + [% IF DBI.do("DELETE FROM users WHERE uid = '$uid'") %] + The user '[% uid %]' was successfully deleted. + [% END %] + +The plugin also allows you to create a tie to a table in the database +using the Tie::DBI module. Simply call the tie() method, passing the +name of the table and the primary key as arguments. + + [% people = DBI.tie('person', 'uid') %] + +You can then access records in the database table as if they were +entries in the 'people' hash. + + My name is [% people.abw.name %] + +IMPORTANT NOTE: the XS Stash (Template::Stash::XS) does not currently +support access to tied hashes. If you are using the XS stash and having +problems then you should try enabling the regular stash instead. You +can do this by setting $Template::Config::STASH to 'Template::Stash' +before instantiating the Template object. + +=head1 OBJECT METHODS + +=head2 connect($database, $username, $password) + +Establishes a database connection. This method accepts both positional +and named parameter syntax. e.g. + + [% DBI.connect( 'dbi:driver:dbname', 'timmy', 'sk8D00Dz' ) %] + + [% DBI.connect( database = 'dbi:driver:dbname' + username = 'timmy' + password = 'sk8D00Dz' ) %] + +The connect method allows you to connect to a data source explicitly. +It can also be used to reconnect an exisiting object to a different +data source. + +If you already have a database handle then you can instruct the plugin +to reuse it by passing it as the 'dbh' parameter. + + [% DBI.connect( dbh = my_dbh_ref ) %] + +=head2 query($sql) + +This method submits an SQL query to the database and creates an iterator +object to return the results. This may be used directly in a FOREACH +directive as shown below. Data is automatically fetched a row at a time +from the query result set as required for memory efficiency. + + [% FOREACH user = DBI.query('SELECT * FROM users') %] + Each [% user.field %] can be printed here + [% END %] + +=head2 prepare($sql) + +Prepare a query for later execution. This returns a compiled query +object (of the Template::Plugin::DBI::Query class) on which the +execute() method can subsequently be called. + + [% query = DBI.prepare('SELECT * FROM users WHERE id = ?') %] + +=head2 execute(@args) + +Execute a previously prepared query. This method should be called on +the query object returned by the prepare() method. Returns an +iterator object which can be used directly in a FOREACH directive. + + [% query = DBI.prepare('SELECT * FROM users WHERE manager = ?') %] + + [% FOREACH minion = query.execute('abw') %] + [% minion.name %] + [% END %] + + [% FOREACH minion = query.execute('sam') %] + [% minion.name %] + [% END %] + +=head2 do($sql) + +The do() method executes a sql statement from which no records are +returned. It will return true if the statement was successful + + [% IF DBI.do("DELETE FROM users WHERE uid = 'sam'") %] + The user was successfully deleted. + [% END %] + +=head2 tie($table, $key, \%args) + +Returns a reference to a hash array tied to a table in the database, +implemented using the Tie::DBI module. You should pass the name of +the table and the key field as arguments. + + [% people = DBI.tie('users', 'uid') %] + +Or if you prefer, you can use the 'table' and 'key' named parameters. + + [% people = DBI.tie(table='users', key='uid') %] + +In this example, the Tie::DBI module will convert the accesses into +the 'people' hash into SQL queries of the form: + + SELECT * FROM users WHERE uid=? + +For example: + + [% me = people.abw %] + +The record returned can then be accessed just like a normal hash. + + I am [% me.name %] + +You can also do things like this to iterate through all the records +in a table. + + [% FOREACH uid = people.keys.sort; + person = people.$uid + %] + * [% person.id %] : [% person.name %] + [% END %] + +With the 'clobber' (or 'CLOBBER') option set you can update the record +and have those changes automatically permeated back into the database. + + [% people = DBI.tie('users', 'uid', clobber=1) %] + + [% people.abw.name = 'not a number' %] + + I am [% people.abw.name %] # I am a free man! + +And you can also add new records. + + [% people.newguy = { + name = 'Nobby Newguy' + ...other fields... + } + %] + +See L<Tie::DBI> for further information on the 'CLOBBER' option. + +=head2 quote($value, $type) + +Calls the quote() method on the underlying DBI handle to quote the value +specified in the appropriate manner for its type. + +=head2 dbh() + +Return the database handle currently in use by the plugin. + +=head2 disconnect() + +Disconnects the current database. + +=head1 AUTHORS + +The DBI plugin was originally written by Simon A Matthews, and +distributed as a separate module. It was integrated into the Template +Toolkit distribution for version 2.00 and includes contributions from +Andy Wardley, Craig Barratt, Dave Hodgkinson and Rafael Kitover. + +=head1 VERSION + +2.62, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + + + +=head1 COPYRIGHT + +Copyright (C) 1999-2001 Simon Matthews. 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::Plugin|Template::Plugin>, L<DBI|DBI>, L<Tie::DBI|Tie::DBI> + diff --git a/lib/Template/Plugin/Datafile.pm b/lib/Template/Plugin/Datafile.pm new file mode 100644 index 0000000..5cf53af --- /dev/null +++ b/lib/Template/Plugin/Datafile.pm @@ -0,0 +1,198 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Datafile +# +# DESCRIPTION +# +# Template Toolkit Plugin which reads a datafile and constructs a +# list object containing hashes representing records in the file. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Datafile.pm,v 2.66 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Datafile; + +require 5.004; + +use strict; +use vars qw( @ISA $VERSION ); +use base qw( Template::Plugin ); +use Template::Plugin; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.66 $ =~ /(\d+)\.(\d+)/); + +sub new { + my ($class, $context, $filename, $params) = @_; + my ($delim, $line, @fields, @data, @results); + my $self = [ ]; + local *FD; + local $/ = "\n"; + + $params ||= { }; + $delim = $params->{'delim'} || ':'; + $delim = quotemeta($delim); + + return $class->fail("No filename specified") + unless $filename; + + open(FD, $filename) + || return $class->fail("$filename: $!"); + + # first line of file should contain field definitions + while (! $line || $line =~ /^#/) { + $line = <FD>; + chomp $line; + $line =~ s/\r$//; + } + + (@fields = split(/\s*$delim\s*/, $line)) + || return $class->fail("first line of file must contain field names"); + + # read each line of the file + while (<FD>) { + chomp; + s/\r$//; + + # ignore comments and blank lines + next if /^#/ || /^\s*$/; + + # split line into fields + @data = split(/\s*$delim\s*/); + + # create hash record to represent data + my %record; + @record{ @fields } = @data; + + push(@$self, \%record); + } + +# return $self; + bless $self, $class; +} + + +sub as_list { + return $_[0]; +} + + +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::Datafile - Plugin to construct records from a simple data file + +=head1 SYNOPSIS + + [% USE mydata = datafile('/path/to/datafile') %] + [% USE mydata = datafile('/path/to/datafile', delim = '|') %] + + [% FOREACH record = mydata %] + [% record.this %] [% record.that %] + [% END %] + +=head1 DESCRIPTION + +This plugin provides a simple facility to construct a list of hash +references, each of which represents a data record of known structure, +from a data file. + + [% USE datafile(filename) %] + +A absolute filename must be specified (for this initial implementation at +least - in a future version it might also use the INCLUDE_PATH). An +optional 'delim' parameter may also be provided to specify an alternate +delimiter character. + + [% USE userlist = datafile('/path/to/file/users') %] + [% USE things = datafile('items', delim = '|') %] + +The format of the file is intentionally simple. The first line +defines the field names, delimited by colons with optional surrounding +whitespace. Subsequent lines then defines records containing data +items, also delimited by colons. e.g. + + id : name : email : tel + abw : Andy Wardley : abw@cre.canon.co.uk : 555-1234 + neilb : Neil Bowers : neilb@cre.canon.co.uk : 555-9876 + +Each line is read, split into composite fields, and then used to +initialise a hash array containing the field names as relevant keys. +The plugin returns a blessed list reference containing the hash +references in the order as defined in the file. + + [% FOREACH user = userlist %] + [% user.id %]: [% user.name %] + [% END %] + +The first line of the file B<must> contain the field definitions. +After the first line, blank lines will be ignored, along with comment +line which start with a '#'. + +=head1 BUGS + +Should handle file names relative to INCLUDE_PATH. +Doesn't permit use of ':' in a field. Some escaping mechanism is required. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.66, 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: diff --git a/lib/Template/Plugin/Date.pm b/lib/Template/Plugin/Date.pm new file mode 100644 index 0000000..7351686 --- /dev/null +++ b/lib/Template/Plugin/Date.pm @@ -0,0 +1,361 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Date +# +# DESCRIPTION +# +# Plugin to generate formatted date strings. +# +# AUTHORS +# Thierry-Michel Barral <kktos@electron-libre.com> +# Andy Wardley <abw@cre.canon.co.uk> +# +# COPYRIGHT +# Copyright (C) 2000 Thierry-Michel Barral, Andy Wardley. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: Date.pm,v 2.71 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Date; + +use strict; +use vars qw( $VERSION $FORMAT @LOCALE_SUFFIX ); +use base qw( Template::Plugin ); +use Template::Plugin; + +use POSIX (); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.71 $ =~ /(\d+)\.(\d+)/); +$FORMAT = '%H:%M:%S %d-%b-%Y'; # default strftime() format +@LOCALE_SUFFIX = qw( .ISO8859-1 .ISO_8859-15 .US-ASCII .UTF-8 ); + +#------------------------------------------------------------------------ +# new(\%options) +#------------------------------------------------------------------------ + +sub new { + my ($class, $context, $params) = @_; + bless { + $params ? %$params : () + }, $class; +} + + +#------------------------------------------------------------------------ +# now() +# +# Call time() to return the current system time in seconds since the epoch. +#------------------------------------------------------------------------ + +sub now { + return time(); +} + + +#------------------------------------------------------------------------ +# format() +# format($time) +# format($time, $format) +# format($time, $format, $locale) +# format($time, $format, $locale, $gmt_flag) +# format(\%named_params); +# +# Returns a formatted time/date string for the specified time, $time, +# (or the current system time if unspecified) using the $format, $locale, +# and $gmt values specified as arguments or internal values set defined +# at construction time). Specifying a Perl-true value for $gmt will +# override the local time zone and force the output to be for GMT. +# Any or all of the arguments may be specified as named parameters which +# get passed as a hash array reference as the final argument. +# ------------------------------------------------------------------------ + +sub format { + my $self = shift; + my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { }; + my $time = shift(@_) || $params->{ time } || $self->{ time } + || $self->now(); + my $format = @_ ? shift(@_) + : ($params->{ format } || $self->{ format } || $FORMAT); + my $locale = @_ ? shift(@_) + : ($params->{ locale } || $self->{ locale }); + my $gmt = @_ ? shift(@_) + : ($params->{ gmt } || $self->{ gmt }); + my (@date, $datestr); + + if ($time =~ /^\d+$/) { + # $time is now in seconds since epoch + if ($gmt) { + @date = (gmtime($time))[0..6]; + } + else { + @date = (localtime($time))[0..6]; + } + } + else { + # if $time is numeric, then we assume it's seconds since the epoch + # otherwise, we try to parse it as a 'H:M:S D:M:Y' string + @date = (split(/(?:\/| |:|-)/, $time))[2,1,0,3..5]; + return (undef, Template::Exception->new('date', + "bad time/date string: expects 'h:m:s d:m:y' got: '$time'")) + unless @date >= 6 && defined $date[5]; + $date[4] -= 1; # correct month number 1-12 to range 0-11 + $date[5] -= 1900; # convert absolute year to years since 1900 + $time = &POSIX::mktime(@date); + } + + if ($locale) { + # format the date in a specific locale, saving and subsequently + # restoring the current locale. + my $old_locale = &POSIX::setlocale(&POSIX::LC_ALL); + + # some systems expect locales to have a particular suffix + for my $suffix ('', @LOCALE_SUFFIX) { + my $try_locale = $locale.$suffix; + my $setlocale = &POSIX::setlocale(&POSIX::LC_ALL, $try_locale); + if (defined $setlocale && $try_locale eq $setlocale) { + $locale = $try_locale; + last; + } + } + $datestr = &POSIX::strftime($format, @date); + &POSIX::setlocale(&POSIX::LC_ALL, $old_locale); + } + else { + $datestr = &POSIX::strftime($format, @date); + } + + return $datestr; +} + +sub calc { + my $self = shift; + eval { require "Date/Calc.pm" }; + $self->throw("failed to load Date::Calc: $@") if $@; + return Template::Plugin::Date::Calc->new('no context'); +} + +sub manip { + my $self = shift; + eval { require "Date/Manip.pm" }; + $self->throw("failed to load Date::Manip: $@") if $@; + return Template::Plugin::Date::Manip->new('no context'); +} + + +sub throw { + my $self = shift; + die (Template::Exception->new('date', join(', ', @_))); +} + + +package Template::Plugin::Date::Calc; +use base qw( Template::Plugin ); +use vars qw( $AUTOLOAD ); +*throw = \&Template::Plugin::Date::throw; + +sub AUTOLOAD { + my $self = shift; + my $method = $AUTOLOAD; + + $method =~ s/.*:://; + return if $method eq 'DESTROY'; + + my $sub = \&{"Date::Calc::$method"}; + $self->throw("no such Date::Calc method: $method") + unless $sub; + + &$sub(@_); +} + +package Template::Plugin::Date::Manip; +use base qw( Template::Plugin ); +use vars qw( $AUTOLOAD ); +*throw = \&Template::Plugin::Date::throw; + +sub AUTOLOAD { + my $self = shift; + my $method = $AUTOLOAD; + + $method =~ s/.*:://; + return if $method eq 'DESTROY'; + + my $sub = \&{"Date::Manip::$method"}; + $self->throw("no such Date::Manip method: $method") + unless $sub; + + &$sub(@_); +} + + +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::Date - Plugin to generate formatted date strings + +=head1 SYNOPSIS + + [% USE date %] + + # use current time and default format + [% date.format %] + + # specify time as seconds since epoch or 'h:m:s d-m-y' string + [% date.format(960973980) %] + [% date.format('4:20:36 21/12/2000') %] + + # specify format + [% date.format(mytime, '%H:%M:%S') %] + + # specify locale + [% date.format(date.now, '%a %d %b %y', 'en_GB') %] + + # named parameters + [% date.format(mytime, format = '%H:%M:%S') %] + [% date.format(locale = 'en_GB') %] + [% date.format(time = date.now, + format = '%H:%M:%S', + locale = 'en_GB) %] + + # specify default format to plugin + [% USE date(format = '%H:%M:%S', locale = 'de_DE') %] + + [% date.format %] + ... + +=head1 DESCRIPTION + +The Date plugin provides an easy way to generate formatted time and date +strings by delegating to the POSIX strftime() routine. + +The plugin can be loaded via the familiar USE directive. + + [% USE date %] + +This creates a plugin object with the default name of 'date'. An alternate +name can be specified as such: + + [% USE myname = date %] + +The plugin provides the format() method which accepts a time value, a +format string and a locale name. All of these parameters are optional +with the current system time, default format ('%H:%M:%S %d-%b-%Y') and +current locale being used respectively, if undefined. Default values +for the time, format and/or locale may be specified as named parameters +in the USE directive. + + [% USE date(format = '%a %d-%b-%Y', locale = 'fr_FR') %] + +When called without any parameters, the format() method returns a string +representing the current system time, formatted by strftime() according +to the default format and for the default locale (which may not be the +current one, if locale is set in the USE directive). + + [% date.format %] + +The plugin allows a time/date to be specified as seconds since the epoch, +as is returned by time(). + + File last modified: [% date.format(filemod_time) %] + +The time/date can also be specified as a string of the form 'h:m:s d/m/y'. +Any of the characters : / - or space may be used to delimit fields. + + [% USE day = date(format => '%A', locale => 'en_GB') %] + [% day.format('4:20:00 9-13-2000') %] + +Output: + + Tuesday + +A format string can also be passed to the format() method, and a locale +specification may follow that. + + [% date.format(filemod, '%d-%b-%Y') %] + [% date.format(filemod, '%d-%b-%Y', 'en_GB') %] + +A fourth parameter allows you to force output in GMT, in the case of +seconds-since-the-epoch input: + + [% date.format(filemod, '%d-%b-%Y', 'en_GB', 1) %] + +Note that in this case, if the local time is not GMT, then also specifying +'%Z' (time zone) in the format parameter will lead to an extremely +misleading result. + +Any or all of these parameters may be named. Positional parameters +should always be in the order ($time, $format, $locale). + + [% date.format(format => '%H:%M:%S') %] + [% date.format(time => filemod, format => '%H:%M:%S') %] + [% date.format(mytime, format => '%H:%M:%S') %] + [% date.format(mytime, format => '%H:%M:%S', locale => 'fr_FR') %] + [% date.format(mytime, format => '%H:%M:%S', gmt => 1) %] + ...etc... + +The now() method returns the current system time in seconds since the +epoch. + + [% date.format(date.now, '%A') %] + +The calc() method can be used to create an interface to the Date::Calc +module (if installed on your system). + + [% calc = date.calc %] + [% calc.Monday_of_Week(22, 2001).join('/') %] + +The manip() method can be used to create an interface to the Date::Manip +module (if installed on your system). + + [% manip = date.manip %] + [% manip.UnixDate("Noon Yesterday","%Y %b %d %H:%M") %] + +=head1 AUTHORS + +Thierry-Michel Barral E<lt>kktos@electron-libre.comE<gt> wrote the original +plugin. + +Andy Wardley E<lt>abw@cre.canon.co.ukE<gt> provided some minor +fixups/enhancements, a test script and documentation. + +Mark D. Mills E<lt>mark@hostile.orgE<gt> cloned Date::Manip from the +cute Date::Calc sub-plugin. + +=head1 VERSION + +2.71, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + + + +=head1 COPYRIGHT + +Copyright (C) 2000 Thierry-Michel Barral, Andy Wardley. + +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>, L<POSIX|POSIX> + diff --git a/lib/Template/Plugin/Directory.pm b/lib/Template/Plugin/Directory.pm new file mode 100644 index 0000000..ec6247e --- /dev/null +++ b/lib/Template/Plugin/Directory.pm @@ -0,0 +1,410 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Directory +# +# DESCRIPTION +# Plugin for encapsulating information about a file system directory. +# +# AUTHORS +# Michael Stevens <michael@etla.org>, with some mutilations from +# Andy Wardley <abw@kfs.org>. +# +# COPYRIGHT +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +# REVISION +# $Id: Directory.pm,v 2.64 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Directory; + +require 5.004; + +use strict; +use Cwd; +use File::Spec; +use Template::Plugin::File; +use vars qw( $VERSION ); +use base qw( Template::Plugin::File ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); + + +#------------------------------------------------------------------------ +# new(\%config) +# +# Constructor method. +#------------------------------------------------------------------------ + +sub new { + my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { }; + my ($class, $context, $path) = @_; + + return $class->throw('no directory specified') + unless defined $path and length $path; + + my $self = $class->SUPER::new($context, $path, $config); + my ($dir, @files, $name, $item, $abs, $rel, $check); + $self->{ files } = [ ]; + $self->{ dirs } = [ ]; + $self->{ list } = [ ]; + $self->{ _dir } = { }; + + # don't read directory if 'nostat' or 'noscan' set + return $self if $config->{ nostat } || $config->{ noscan }; + + $self->throw("$path: not a directory") + unless $self->{ isdir }; + + $self->scan($config); + + return $self; +} + + +#------------------------------------------------------------------------ +# scan(\%config) +# +# Scan directory for files and sub-directories. +#------------------------------------------------------------------------ + +sub scan { + my ($self, $config) = @_; + $config ||= { }; + local *DH; + my ($dir, @files, $name, $abs, $rel, $item); + + # set 'noscan' in config if recurse isn't set, to ensure Directories + # created don't try to scan deeper + $config->{ noscan } = 1 unless $config->{ recurse }; + + $dir = $self->{ abs }; + opendir(DH, $dir) or return $self->throw("$dir: $!"); + + @files = readdir DH; + closedir(DH) + or return $self->throw("$dir close: $!"); + + my ($path, $files, $dirs, $list) = @$self{ qw( path files dirs list ) }; + @$files = @$dirs = @$list = (); + + foreach $name (sort @files) { + next if $name =~ /^\./; + $abs = File::Spec->catfile($dir, $name); + $rel = File::Spec->catfile($path, $name); + + if (-d $abs) { + $item = Template::Plugin::Directory->new(undef, $rel, $config); + push(@$dirs, $item); + } + else { + $item = Template::Plugin::File->new(undef, $rel, $config); + push(@$files, $item); + } + push(@$list, $item); + $self->{ _dir }->{ $name } = $item; + } + + return ''; +} + + +#------------------------------------------------------------------------ +# file($filename) +# +# Fetch a named file from this directory. +#------------------------------------------------------------------------ + +sub file { + my ($self, $name) = @_; + return $self->{ _dir }->{ $name }; +} + + +#------------------------------------------------------------------------ +# present($view) +# +# Present self to a Template::View +#------------------------------------------------------------------------ + +sub present { + my ($self, $view) = @_; + $view->view_directory($self); +} + + +#------------------------------------------------------------------------ +# content($view) +# +# Present directory content to a Template::View. +#------------------------------------------------------------------------ + +sub content { + my ($self, $view) = @_; + return $self->{ list } unless $view; + my $output = ''; + foreach my $file (@{ $self->{ list } }) { + $output .= $file->present($view); + } + return $output; +} + + +#------------------------------------------------------------------------ +# throw($msg) +# +# Throw a 'Directory' exception. +#------------------------------------------------------------------------ + +sub throw { + my ($self, $error) = @_; + die (Template::Exception->new('Directory', $error)); +} + +__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::Directory - Plugin for generating directory listings + +=head1 SYNOPSIS + + [% USE dir = Directory(dirpath) %] + + # files returns list of regular files + [% FOREACH file = dir.files %] + [% file.name %] [% file.path %] ... + [% END %] + + # dirs returns list of sub-directories + [% FOREACH subdir = dir.dirs %] + [% subdir.name %] [% subdir.path %] ... + [% END %] + + # list returns both interleaved in order + [% FOREACH item = dir.list %] + [% IF item.isdir %] + Directory: [% item.name %] + [% ELSE + File: [% item.name %] + [% END %] + [% END %] + + # define a VIEW to display dirs/files + [% VIEW myview %] + [% BLOCK file %] + File: [% item.name %] + [% END %] + + [% BLOCK directory %] + Directory: [% item.name %] + [% item.content(myview) | indent -%] + [% END %] + [% END %] + + # display directory content using view + [% myview.print(dir) %] + +=head1 DESCRIPTION + +This Template Toolkit plugin provides a simple interface to directory +listings. It is derived from the Template::Plugin::File module and +uses Template::Plugin::File object instances to represent files within +a directory. Sub-directories within a directory are represented by +further Template::Plugin::Directory instances. + +The constructor expects a directory name as an argument. + + [% USE dir = Directory('/tmp') %] + +It then provides access to the files and sub-directories contained within +the directory. + + # regular files (not directories) + [% FOREACH file = dir.files %] + [% file.name %] + [% END %] + + # directories only + [% FOREACH file = dir.dirs %] + [% file.name %] + [% END %] + + # files and/or directories + [% FOREACH file = dir.list %] + [% file.name %] ([% file.isdir ? 'directory' : 'file' %]) + [% END %] + + [% USE Directory('foo/baz') %] + +The plugin constructor will throw a 'Directory' error if the specified +path does not exist, is not a directory or fails to stat() (see +L<Template::Plugin::File>). Otherwise, it will scan the directory and +create lists named 'files' containing files, 'dirs' containing +directories and 'list' containing both files and directories combined. +The 'nostat' option can be set to disable all file/directory checks +and directory scanning. + +Each file in the directory will be represented by a +Template::Plugin::File object instance, and each directory by another +Template::Plugin::Directory. If the 'recurse' flag is set, then those +directories will contain further nested entries, and so on. With the +'recurse' flag unset, as it is by default, then each is just a place +marker for the directory and does not contain any further content +unless its scan() method is explicitly called. The 'isdir' flag can +be tested against files and/or directories, returning true if the item +is a directory or false if it is a regular file. + + [% FOREACH file = dir.list %] + [% IF file.isdir %] + * Directory: [% file.name %] + [% ELSE %] + * File: [% file.name %] + [% END %] + [% END %] + +This example shows how you might walk down a directory tree, displaying +content as you go. With the recurse flag disabled, as is the default, +we need to explicitly call the scan() method on each directory, to force +it to lookup files and further sub-directories contained within. + + [% USE dir = Directory(dirpath) %] + * [% dir.path %] + [% INCLUDE showdir %] + + [% BLOCK showdir -%] + [% FOREACH file = dir.list -%] + [% IF file.isdir -%] + * [% file.name %] + [% file.scan -%] + [% INCLUDE showdir dir=file FILTER indent(4) -%] + [% ELSE -%] + - [% f.name %] + [% END -%] + [% END -%] + [% END %] + +This example is adapted (with some re-formatting for clarity) from +a test in F<t/directry.t> which produces the following output: + + * test/dir + - file1 + - file2 + * sub_one + - bar + - foo + * sub_two + - waz.html + - wiz.html + - xyzfile + +The 'recurse' flag can be set (disabled by default) to cause the +constructor to automatically recurse down into all sub-directories, +creating a new Template::Plugin::Directory object for each one and +filling it with any further content. In this case there is no need +to explicitly call the scan() method. + + [% USE dir = Directory(dirpath, recurse=1) %] + ... + + [% IF file.isdir -%] + * [% file.name %] + [% INCLUDE showdir dir=file FILTER indent(4) -%] + [% ELSE -%] + ... + +From version 2.01, the Template Toolkit provides support for views. +A view can be defined as a VIEW ... END block and should contain +BLOCK definitions for files ('file') and directories ('directory'). + + [% VIEW myview %] + [% BLOCK file %] + - [% item.name %] + [% END %] + + [% BLOCK directory %] + * [% item.name %] + [% item.content(myview) FILTER indent %] + [% END %] + [% END %] + +Then the view print() method can be called, passing the +Directory object as an argument. + + [% USE dir = Directory(dirpath, recurse=1) %] + [% myview.print(dir) %] + +When a directory is presented to a view, either as [% myview.print(dir) %] +or [% dir.present(view) %], then the 'directory' BLOCK within the 'myview' +VIEW is processed, with the 'item' variable set to alias the Directory object. + + [% BLOCK directory %] + * [% item.name %] + [% item.content(myview) FILTER indent %] + [% END %] + +The directory name is first printed and the content(view) method is +then called to present each item within the directory to the view. +Further directories will be mapped to the 'directory' block, and files +will be mapped to the 'file' block. + +With the recurse option disabled, as it is by default, the 'directory' +block should explicitly call a scan() on each directory. + + [% VIEW myview %] + [% BLOCK file %] + - [% item.name %] + [% END %] + + [% BLOCK directory %] + * [% item.name %] + [% item.scan %] + [% item.content(myview) FILTER indent %] + [% END %] + [% END %] + + [% USE dir = Directory(dirpath) %] + [% myview.print(dir) %] + +=head1 TODO + +Might be nice to be able to specify accept/ignore options to catch +a subset of files. + +=head1 AUTHORS + +Michael Stevens E<lt>michael@etla.orgE<gt> wrote the original Directory plugin +on which this is based. Andy Wardley E<lt>abw@wardley.orgE<gt> split it into +separate File and Directory plugins, added some extra code and documentation +for VIEW support, and made a few other minor tweaks. + +=head1 VERSION + +2.64, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + + + +=head1 COPYRIGHT + +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>, L<Template::Plugin::File|Template::Plugin::File>, L<Template::View|Template::View> + diff --git a/lib/Template/Plugin/Dumper.pm b/lib/Template/Plugin/Dumper.pm new file mode 100644 index 0000000..5dbf1f6 --- /dev/null +++ b/lib/Template/Plugin/Dumper.pm @@ -0,0 +1,179 @@ +#============================================================================== +# +# Template::Plugin::Dumper +# +# DESCRIPTION +# +# A Template Plugin to provide a Template Interface to Data::Dumper +# +# AUTHOR +# Simon Matthews <sam@knowledgepool.com> +# +# COPYRIGHT +# +# Copyright (C) 2000 Simon Matthews. All Rights Reserved +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#------------------------------------------------------------------------------ +# +# $Id: Dumper.pm,v 2.64 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================== + +package Template::Plugin::Dumper; + +require 5.004; + +use strict; +use Template::Plugin; +use Data::Dumper; + +use vars qw( $VERSION $DEBUG @DUMPER_ARGS $AUTOLOAD ); +use base qw( Template::Plugin ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0 unless defined $DEBUG; +@DUMPER_ARGS = qw( Indent Pad Varname Purity Useqq Terse Freezer + Toaster Deepcopy Quotekeys Bless Maxdepth ); + +#============================================================================== +# ----- CLASS METHODS ----- +#============================================================================== + +#------------------------------------------------------------------------ +# new($context, \@params) +#------------------------------------------------------------------------ + +sub new { + my ($class, $context, $params) = @_; + my ($key, $val); + $params ||= { }; + + + foreach my $arg (@DUMPER_ARGS) { + no strict 'refs'; + if (defined ($val = $params->{ lc $arg }) + or defined ($val = $params->{ $arg })) { + ${"Data\::Dumper\::$arg"} = $val; + } + } + + bless { + _CONTEXT => $context, + }, $class; +} + +sub dump { + my $self = shift; + my $content = Dumper @_; + return $content; +} + + +sub dump_html { + my $self = shift; + my $content = Dumper @_; + for ($content) { + s/&/&/g; + s/</</g; + s/>/>/g; + s/\n/<br>\n/g; + } + return $content; +} + +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::Dumper - Plugin interface to Data::Dumper + +=head1 SYNOPSIS + + [% USE Dumper %] + + [% Dumper.dump(variable) %] + [% Dumper.dump_html(variable) %] + +=head1 DESCRIPTION + +This is a very simple Template Toolkit Plugin Interface to the Data::Dumper +module. A Dumper object will be instantiated via the following directive: + + [% USE Dumper %] + +As a standard plugin, you can also specify its name in lower case: + + [% USE dumper %] + +The Data::Dumper 'Pad', 'Indent' and 'Varname' options are supported +as constructor arguments to affect the output generated. See L<Data::Dumper> +for further details. + + [% USE dumper(Indent=0, Pad="<br>") %] + +These options can also be specified in lower case. + + [% USE dumper(indent=0, pad="<br>") %] + +=head1 METHODS + +There are two methods supported by the Dumper object. Each will +output into the template the contents of the variables passed to the +object method. + +=head2 dump() + +Generates a raw text dump of the data structure(s) passed + + [% USE Dumper %] + [% Dumper.dump(myvar) %] + [% Dumper.dump(myvar, yourvar) %] + +=head2 dump_html() + +Generates a dump of the data structures, as per dump(), but with the +characters E<lt>, E<gt> and E<amp> converted to their equivalent HTML +entities and newlines converted to E<lt>brE<gt>. + + [% USE Dumper %] + [% Dumper.dump_html(myvar) %] + +=head1 AUTHOR + +Simon Matthews E<lt>sam@knowledgepool.comE<gt> + +=head1 VERSION + +2.64, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + + + +=head1 COPYRIGHT + +Copyright (C) 2000 Simon Matthews 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::Plugin|Template::Plugin>, L<Data::Dumper|Data::Dumper> + diff --git a/lib/Template/Plugin/File.pm b/lib/Template/Plugin/File.pm new file mode 100644 index 0000000..d1d542e --- /dev/null +++ b/lib/Template/Plugin/File.pm @@ -0,0 +1,416 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::File +# +# DESCRIPTION +# Plugin for encapsulating information about a system file. +# +# AUTHOR +# Originally written by Michael Stevens <michael@etla.org> as the +# Directory plugin, then mutilated by Andy Wardley <abw@kfs.org> +# into separate File and Directory plugins, with some additional +# code for working with views, etc. +# +# COPYRIGHT +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +# REVISION +# $Id: File.pm,v 2.64 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::File; + +require 5.004; + +use strict; +use Cwd; +use File::Spec; +use File::Basename; +use Template::Plugin; + +use vars qw( $VERSION ); +use base qw( Template::Plugin ); +use vars qw( @STAT_KEYS ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); + +@STAT_KEYS = qw( dev ino mode nlink uid gid rdev size + atime mtime ctime blksize blocks ); + + +#------------------------------------------------------------------------ +# new($context, $file, \%config) +# +# Create a new File object. Takes the pathname of the file as +# the argument following the context and an optional +# hash reference of configuration parameters. +#------------------------------------------------------------------------ + +sub new { + my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { }; + my ($class, $context, $path) = @_; + my ($root, $home, @stat, $abs); + + return $class->throw('no file specified') + unless defined $path and length $path; + + # path, dir, name, root, home + + if (File::Spec->file_name_is_absolute($path)) { + $root = ''; + } + elsif (($root = $config->{ root })) { + # strip any trailing '/' from root + $root =~ s[/$][]; + } + else { + $root = ''; + } + + my ($name, $dir, $ext) = fileparse($path, '\.\w+'); + # fixup various items + $dir =~ s[/$][]; + $dir = '' if $dir eq '.'; + $name = $name . $ext; + $ext =~ s/^\.//g; + my @fields = File::Spec->splitdir($dir); + shift @fields if @fields && ! length $fields[0]; + $home = join('/', ('..') x @fields); + $abs = File::Spec->catfile($root ? $root : (), $path); + + my $self = { + path => $path, + name => $name, + root => $root, + home => $home, + dir => $dir, + ext => $ext, + abs => $abs, + user => '', + group => '', + isdir => '', + stat => defined $config->{ stat } ? $config->{ stat } + : ! $config->{ nostat }, + map { ($_ => '') } @STAT_KEYS, + }; + + if ($self->{ stat }) { + (@stat = stat( $abs )) + || return $class->throw("$abs: $!"); + @$self{ @STAT_KEYS } = @stat; + unless ($config->{ noid }) { + $self->{ user } = eval { getpwuid( $self->{ uid }) || $self->{ uid } }; + $self->{ group } = eval { getgrgid( $self->{ gid }) || $self->{ gid } }; + } + $self->{ isdir } = -d $abs; + } + + bless $self, $class; +} + + +#------------------------------------------------------------------------- +# rel($file) +# +# Generate a relative filename for some other file relative to this one. +#------------------------------------------------------------------------ + +sub rel { + my ($self, $path) = @_; + $path = $path->{ path } if ref $path eq ref $self; # assumes same root + return $path if $path =~ m[^/]; + return $path unless $self->{ home }; + return $self->{ home } . '/' . $path; +} + + +#------------------------------------------------------------------------ +# present($view) +# +# Present self to a Template::View. +#------------------------------------------------------------------------ + +sub present { + my ($self, $view) = @_; + $view->view_file($self); +} + + +sub throw { + my ($self, $error) = @_; + die (Template::Exception->new('File', $error)); +} + +__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::File - Plugin providing information about files + +=head1 SYNOPSIS + + [% USE File(filepath) %] + [% File.path %] # full path + [% File.name %] # filename + [% File.dir %] # directory + +=head1 DESCRIPTION + +This plugin provides an abstraction of a file. It can be used to +fetch details about files from the file system, or to represent abstract +files (e.g. when creating an index page) that may or may not exist on +a file system. + +A file name or path should be specified as a constructor argument. e.g. + + [% USE File('foo.html') %] + [% USE File('foo/bar/baz.html') %] + [% USE File('/foo/bar/baz.html') %] + +The file should exist on the current file system (unless 'nostat' +option set, see below) as an absolute file when specified with as +leading '/' as per '/foo/bar/baz.html', or otherwise as one relative +to the current working directory. The constructor performs a stat() +on the file and makes the 13 elements returned available as the plugin +items: + + dev ino mode nlink uid gid rdev size + atime mtime ctime blksize blocks + +e.g. + + [% USE File('/foo/bar/baz.html') %] + + [% File.mtime %] + [% File.mode %] + ... + +In addition, the 'user' and 'group' items are set to contain the user +and group names as returned by calls to getpwuid() and getgrgid() for +the file 'uid' and 'gid' elements, respectively. On Win32 platforms +on which getpwuid() and getgrid() are not available, these values are +undefined. + + [% USE File('/tmp/foo.html') %] + [% File.uid %] # e.g. 500 + [% File.user %] # e.g. abw + +This user/group lookup can be disabled by setting the 'noid' option. + + [% USE File('/tmp/foo.html', noid=1) %] + [% File.uid %] # e.g. 500 + [% File.user %] # nothing + +The 'isdir' flag will be set if the file is a directory. + + [% USE File('/tmp') %] + [% File.isdir %] # 1 + +If the stat() on the file fails (e.g. file doesn't exists, bad +permission, etc) then the constructor will throw a 'File' exception. +This can be caught within a TRY...CATCH block. + + [% TRY %] + [% USE File('/tmp/myfile') %] + File exists! + [% CATCH File %] + File error: [% error.info %] + [% END %] + +Note the capitalisation of the exception type, 'File' to indicate an +error thrown by the 'File' plugin, to distinguish it from a regular +'file' exception thrown by the Template Toolkit. + +Note that the 'File' plugin can also be referenced by the lower case +name 'file'. However, exceptions are always thrown of the 'File' +type, regardless of the capitalisation of the plugin named used. + + [% USE file('foo.html') %] + [% file.mtime %] + +As with any other Template Toolkit plugin, an alternate name can be +specified for the object created. + + [% USE foo = file('foo.html') %] + [% foo.mtime %] + +The 'nostat' option can be specified to prevent the plugin constructor +from performing a stat() on the file specified. In this case, the +file does not have to exist in the file system, no attempt will be made +to verify that it does, and no error will be thrown if it doesn't. +The entries for the items usually returned by stat() will be set +empty. + + [% USE file('/some/where/over/the/rainbow.html', nostat=1) + [% file.mtime %] # nothing + +All File plugins, regardless of the nostat option, have set a number +of items relating to the original path specified. + +=over 4 + +=item path + +The full, original file path specified to the constructor. + + [% USE file('/foo/bar.html') %] + [% file.path %] # /foo/bar.html + +=item name + +The name of the file without any leading directories. + + [% USE file('/foo/bar.html') %] + [% file.name %] # bar.html + +=item dir + +The directory element of the path with the filename removed. + + [% USE file('/foo/bar.html') %] + [% file.name %] # /foo + +=item ext + +The file extension, if any, appearing at the end of the path following +a '.' (not included in the extension). + + [% USE file('/foo/bar.html') %] + [% file.ext %] # html + +=item home + +This contains a string of the form '../..' to represent the upward path +from a file to its root directory. + + [% USE file('bar.html') %] + [% file.home %] # nothing + + [% USE file('foo/bar.html') %] + [% file.home %] # .. + + [% USE file('foo/bar/baz.html') %] + [% file.home %] # ../.. + +=item root + +The 'root' item can be specified as a constructor argument, indicating +a root directory in which the named file resides. This is otherwise +set empty. + + [% USE file('foo/bar.html', root='/tmp') %] + [% file.root %] # /tmp + +=item abs + +This returns the absolute file path by constructing a path from the +'root' and 'path' options. + + [% USE file('foo/bar.html', root='/tmp') %] + [% file.path %] # foo/bar.html + [% file.root %] # /tmp + [% file.abs %] # /tmp/foo/bar.html + +=back + +In addition, the following method is provided: + +=over 4 + +=item rel(path) + +This returns a relative path from the current file to another path specified +as an argument. It is constructed by appending the path to the 'home' +item. + + [% USE file('foo/bar/baz.html') %] + [% file.rel('wiz/waz.html') %] # ../../wiz/waz.html + +=back + +=head1 EXAMPLES + + [% USE file('/foo/bar/baz.html') %] + + [% file.path %] # /foo/bar/baz.html + [% file.dir %] # /foo/bar + [% file.name %] # baz.html + [% file.home %] # ../.. + [% file.root %] # '' + [% file.abspath %] # /foo/bar/baz.html + [% file.ext %] # html + [% file.mtime %] # 987654321 + [% file.atime %] # 987654321 + [% file.uid %] # 500 + [% file.user %] # abw + + [% USE file('foo.html') %] + + [% file.path %] # foo.html + [% file.dir %] # '' + [% file.name %] # foo.html + [% file.root %] # '' + [% file.home %] # '' + [% file.abspath %] # foo.html + + [% USE file('foo/bar/baz.html') %] + + [% file.path %] # foo/bar/baz.html + [% file.dir %] # foo/bar + [% file.name %] # baz.html + [% file.root %] # '' + [% file.home %] # ../.. + [% file.abspath %] # foo/bar/baz.html + + [% USE file('foo/bar/baz.html', root='/tmp') %] + + [% file.path %] # foo/bar/baz.html + [% file.dir %] # foo/bar + [% file.name %] # baz.html + [% file.root %] # /tmp + [% file.home %] # ../.. + [% file.abspath %] # /tmp/foo/bar/baz.html + + # calculate other file paths relative to this file and its root + [% USE file('foo/bar/baz.html', root => '/tmp/tt2') %] + [% file.path('baz/qux.html') %] # ../../baz/qux.html + [% file.dir('wiz/woz.html') %] # ../../wiz/woz.html + + +=head1 AUTHORS + +Michael Stevens E<lt>michael@etla.orgE<gt> wrote the original Directory plugin +on which this is based. Andy Wardley E<lt>abw@wardley.orgE<gt> split it into +separate File and Directory plugins, added some extra code and documentation +for VIEW support, and made a few other minor tweaks. + +=head1 VERSION + +2.64, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + + + +=head1 COPYRIGHT + +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>, L<Template::Plugin::Directory|Template::Plugin::Directory>, L<Template::View|Template::View> + diff --git a/lib/Template/Plugin/Filter.pm b/lib/Template/Plugin/Filter.pm new file mode 100644 index 0000000..38da7ba --- /dev/null +++ b/lib/Template/Plugin/Filter.pm @@ -0,0 +1,436 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Filter +# +# DESCRIPTION +# Template Toolkit module implementing a base class plugin +# object which acts like a filter and can be used with the +# FILTER directive. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 2001 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. +# +# REVISION +# $Id: Filter.pm,v 1.30 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Filter; + +require 5.004; + +use strict; +use Template::Plugin; + +use base qw( Template::Plugin ); +use vars qw( $VERSION $DYNAMIC ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.30 $ =~ /(\d+)\.(\d+)/); +$DYNAMIC = 0 unless defined $DYNAMIC; + + +sub new { + my ($class, $context, @args) = @_; + my $config = @args && ref $args[-1] eq 'HASH' ? pop(@args) : { }; + + # look for $DYNAMIC + my $dynamic; + { + no strict 'refs'; + $dynamic = ${"$class\::DYNAMIC"}; + } + $dynamic = $DYNAMIC unless defined $dynamic; + + my $self = bless { + _CONTEXT => $context, + _DYNAMIC => $dynamic, + _ARGS => \@args, + _CONFIG => $config, + }, $class; + + return $self->init($config) + || $class->error($self->error()); +} + + +sub init { + my ($self, $config) = @_; + return $self; +} + + +sub factory { + my $self = shift; + + if ($self->{ _DYNAMIC }) { + return $self->{ _DYNAMIC_FILTER } ||= [ sub { + my ($context, @args) = @_; + my $config = ref $args[-1] eq 'HASH' ? pop(@args) : { }; + + return sub { + $self->filter(shift, \@args, $config); + }; + }, 1 ]; + } + else { + return $self->{ _STATIC_FILTER } ||= sub { + $self->filter(shift); + }; + } +} + + +sub filter { + my ($self, $text, $args, $config) = @_; + return $text; +} + + +sub merge_config { + my ($self, $newcfg) = @_; + my $owncfg = $self->{ _CONFIG }; + return $owncfg unless $newcfg; + return { %$owncfg, %$newcfg }; +} + + +sub merge_args { + my ($self, $newargs) = @_; + my $ownargs = $self->{ _ARGS }; + return $ownargs unless $newargs; + return [ @$ownargs, @$newargs ]; +} + + +sub install_filter { + my ($self, $name) = @_; + $self->{ _CONTEXT }->define_filter( $name => $self->factory() ); + return $self; +} + + + +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::Filter - Base class for plugin filters + +=head1 SYNOPSIS + + package MyOrg::Template::Plugin::MyFilter; + + use Template::Plugin::Filter; + use base qw( Template::Plugin::Filter ); + + sub filter { + my ($self, $text) = @_; + + # ...mungify $text... + + return $text; + } + + # now load it... + [% USE MyFilter %] + + # ...and use the returned object as a filter + [% FILTER $MyFilter %] + ... + [% END %] + +=head1 DESCRIPTION + +This module implements a base class for plugin filters. It hides +the underlying complexity involved in creating and using filters +that get defined and made available by loading a plugin. + +To use the module, simply create your own plugin module that is +inherited from the Template::Plugin::Filter class. + + package MyOrg::Template::Plugin::MyFilter; + + use Template::Plugin::Filter; + use base qw( Template::Plugin::Filter ); + +Then simply define your filter() method. When called, you get +passed a reference to your plugin object ($self) and the text +to be filtered. + + sub filter { + my ($self, $text) = @_; + + # ...mungify $text... + + return $text; + } + +To use your custom plugin, you have to make sure that the Template +Toolkit knows about your plugin namespace. + + my $tt2 = Template->new({ + PLUGIN_BASE => 'MyOrg::Template::Plugin', + }); + +Or for individual plugins you can do it like this: + + my $tt2 = Template->new({ + PLUGINS => { + MyFilter => 'MyOrg::Template::Plugin::MyFilter', + }, + }); + +Then you USE your plugin in the normal way. + + [% USE MyFilter %] + +The object returned is stored in the variable of the same name, +'MyFilter'. When you come to use it as a FILTER, you should add +a dollar prefix. This indicates that you want to use the filter +stored in the variable 'MyFilter' rather than the filter named +'MyFilter', which is an entirely different thing (see later for +information on defining filters by name). + + [% FILTER $MyFilter %] + ...text to be filtered... + [% END %] + +You can, of course, assign it to a different variable. + + [% USE blat = MyFilter %] + + [% FILTER $blat %] + ...text to be filtered... + [% END %] + +Any configuration parameters passed to the plugin constructor from the +USE directive are stored internally in the object for inspection by +the filter() method (or indeed any other method). Positional +arguments are stored as a reference to a list in the _ARGS item while +named configuration parameters are stored as a reference to a hash +array in the _CONFIG item. + +For example, loading a plugin as shown here: + + [% USE blat = MyFilter 'foo' 'bar' baz = 'blam' %] + +would allow the filter() method to do something like this: + + sub filter { + my ($self, $text) = @_; + + my $args = $self->{ _ARGS }; # [ 'foo', 'bar' ] + my $conf = $self->{ _CONFIG }; # { baz => 'blam' } + + # ...munge $text... + + return $text; + } + +By default, plugins derived from this module will create static +filters. A static filter is created once when the plugin gets +loaded via the USE directive and re-used for all subsequent +FILTER operations. That means that any argument specified with +the FILTER directive are ignored. + +Dynamic filters, on the other hand, are re-created each time +they are used by a FILTER directive. This allows them to act +on any parameters passed from the FILTER directive and modify +their behaviour accordingly. + +There are two ways to create a dynamic filter. The first is to +define a $DYNAMIC class variable set to a true value. + + package MyOrg::Template::Plugin::MyFilter; + + use Template::Plugin::Filter; + use base qw( Template::Plugin::Filter ); + use vars qw( $DYNAMIC ); + + $DYNAMIC = 1; + +The other way is to set the internal _DYNAMIC value within the init() +method which gets called by the new() constructor. + + sub init { + my $self = shift; + $self->{ _DYNAMIC } = 1; + return $self; + } + +When this is set to a true value, the plugin will automatically +create a dynamic filter. The outcome is that the filter() method +will now also get passed a reference to an array of postional +arguments and a reference to a hash array of named parameters. + +So, using a plugin filter like this: + + [% FILTER $blat 'foo' 'bar' baz = 'blam' %] + +would allow the filter() method to work like this: + + sub filter { + my ($self, $text, $args, $conf) = @_; + + # $args = [ 'foo', 'bar' ] + # $conf = { baz => 'blam' } + + } + +In this case can pass parameters to both the USE and FILTER directives, +so your filter() method should probably take that into account. + + [% USE MyFilter 'foo' wiz => 'waz' %] + + [% FILTER $MyFilter 'bar' biz => 'baz' %] + ... + [% END %] + +You can use the merge_args() and merge_config() methods to do a quick +and easy job of merging the local (e.g. FILTER) parameters with the +internal (e.g. USE) values and returning new sets of conglomerated +data. + + sub filter { + my ($self, $text, $args, $conf) = @_; + + $args = $self->merge_args($args); + $conf = $self->merge_config($conf); + + # $args = [ 'foo', 'bar' ] + # $conf = { wiz => 'waz', biz => 'baz' } + ... + } + +You can also have your plugin install itself as a named filter by +calling the install_filter() method from the init() method. You +should provide a name for the filter, something that you might +like to make a configuration option. + + sub init { + my $self = shift; + my $name = $self->{ _CONFIG }->{ name } || 'myfilter'; + $self->install_filter($name); + return $self; + } + +This allows the plugin filter to be used as follows: + + [% USE MyFilter %] + + [% FILTER myfilter %] + ... + [% END %] + +or + + [% USE MyFilter name = 'swipe' %] + + [% FILTER swipe %] + ... + [% END %] + +Alternately, you can allow a filter name to be specified as the +first positional argument. + + sub init { + my $self = shift; + my $name = $self->{ _ARGS }->[0] || 'myfilter'; + $self->install_filter($name); + return $self; + } + + [% USE MyFilter 'swipe' %] + + [% FILTER swipe %] + ... + [% END %] + +=head1 EXAMPLE + +Here's a complete example of a plugin filter module. + + package My::Template::Plugin::Change; + use Template::Plugin::Filter; + use base qw( Template::Plugin::Filter ); + + sub init { + my $self = shift; + + $self->{ _DYNAMIC } = 1; + + # first arg can specify filter name + $self->install_filter($self->{ _ARGS }->[0] || 'change'); + + return $self; + } + + + sub filter { + my ($self, $text, $args, $config) = @_; + + $config = $self->merge_config($config); + my $regex = join('|', keys %$config); + + $text =~ s/($regex)/$config->{ $1 }/ge; + + return $text; + } + + 1; + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +1.30, 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>, L<Template::Filters|Template::Filters>, L<Template::Manual::Filters|Template::Manual::Filters> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/Format.pm b/lib/Template/Plugin/Format.pm new file mode 100644 index 0000000..bba55d2 --- /dev/null +++ b/lib/Template/Plugin/Format.pm @@ -0,0 +1,124 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Format +# +# DESCRIPTION +# +# Simple Template Toolkit Plugin which creates formatting functions. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Format.pm,v 2.64 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Format; + +require 5.004; + +use strict; +use vars qw( @ISA $VERSION ); +use base qw( Template::Plugin ); +use Template::Plugin; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); + + +sub new { + my ($class, $context, $format) = @_;; + return defined $format + ? make_formatter($format) + : \&make_formatter; +} + + +sub make_formatter { + my $format = shift; + $format = '%s' unless defined $format; + return sub { + my @args = @_; + push(@args, '') unless @args; + return sprintf($format, @args); + } +} + + +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::Format - Plugin to create formatting functions + +=head1 SYNOPSIS + + [% USE format %] + [% commented = format('# %s') %] + [% commented('The cat sat on the mat') %] + + [% USE bold = format('<b>%s</b>') %] + [% bold('Hello') %] + +=head1 DESCRIPTION + +The format plugin constructs sub-routines which format text according to +a printf()-like format string. + +=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: diff --git a/lib/Template/Plugin/GD/Constants.pm b/lib/Template/Plugin/GD/Constants.pm new file mode 100644 index 0000000..6fc8e7c --- /dev/null +++ b/lib/Template/Plugin/GD/Constants.pm @@ -0,0 +1,138 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Constants +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD constants +# in the GD.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: Constants.pm,v 1.55 2004/01/13 16:20:46 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Constants; + +require 5.004; + +use strict; +use GD qw(/^gd/ /^GD/); +use Template::Plugin; +use base qw( Template::Plugin ); +use vars qw( @ISA $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + my $self = { }; + bless $self, $class; + + # + # GD has exported various gd* and GD_* contstants. Find them. + # + foreach my $v ( keys(%Template::Plugin::GD::Constants::) ) { + $self->{$v} = eval($v) if ( $v =~ /^gd/ || $v =~ /^GD_/ ); + } + return $self; +} + +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::GD::Constants - Interface to GD module constants + +=head1 SYNOPSIS + + [% USE gdc = GD.Constants %] + + # --> the constants gdc.gdBrushed, gdc.gdSmallFont, gdc.GD_CMP_IMAGE + # are now available + +=head1 EXAMPLES + + [% FILTER null; + USE gdc = GD.Constants; + USE im = GD.Image(200,100); + black = im.colorAllocate(0 ,0, 0); + red = im.colorAllocate(255,0, 0); + r = im.string(gdc.gdLargeFont, 10, 10, "Large Red Text", red); + im.png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Constants plugin provides access to the various GD module's +constants (such as gdBrushed, gdSmallFont, gdTransparent, GD_CMP_IMAGE +etc). When GD.pm is used in perl it exports various contstants +into the caller's namespace. This plugin makes those exported +constants available as template variables. + +See L<Template::Plugin::GD::Image> and L<GD> for further examples and +details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +Lincoln D. Stein wrote the GD.pm interface to the GD library. + + +=head1 VERSION + +1.55, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +The GD.pm interface is copyright 1995-2000, Lincoln D. Stein. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Image|Template::Plugin::GD::Image>, L<Template::Plugin::GD::Polygon|Template::Plugin::GD::Polygon>, L<GD|GD> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/area.pm b/lib/Template/Plugin/GD/Graph/area.pm new file mode 100644 index 0000000..d09d024 --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/area.pm @@ -0,0 +1,148 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::area +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::area +# package in the GD::Graph.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: area.pm,v 1.57 2004/01/13 16:20:51 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::area; + +require 5.004; + +use strict; +use GD::Graph::area; +use Template::Plugin; +use base qw( GD::Graph::area Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + + +sub set_legend +{ + my $self = shift; + + $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_); +} + +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::GD::Graph::area - Create area graphs with axes and legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.area(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"], + [ 5, 12, 24, 33, 19, 8, 6, 15, 21], + [ -1, -2, -5, -6, -3, 1.5, 1, 1.3, 2] + ]; + + USE my_graph = GD.Graph.area(); + my_graph.set( + two_axes => 1, + zero_axis => 1, + transparent => 0, + ); + my_graph.set_legend('left axis', 'right axis' ); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.area plugin provides an interface to the GD::Graph::area +class defined by the GD::Graph module. It allows one or more (x,y) data +sets to be plotted as lines with the area between the line and x-axis +shaded, in addition to axes and legends. + +See L<GD::Graph> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph module was written by Martien Verbruggen. + + +=head1 VERSION + +1.57, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/bars.pm b/lib/Template/Plugin/GD/Graph/bars.pm new file mode 100644 index 0000000..9bc08c5 --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/bars.pm @@ -0,0 +1,191 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::bars +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::bars +# package in the GD::Graph.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: bars.pm,v 1.57 2004/01/13 16:20:56 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::bars; + +require 5.004; + +use strict; +use GD::Graph::bars; +use Template::Plugin; +use base qw( GD::Graph::bars Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + + +sub set_legend +{ + my $self = shift; + + $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_); +} + +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::GD::Graph::bars - Create bar graphs with axes and legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.bars(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"], + [ 1, 2, 5, 6, 3, 1.5, 1, 3, 4], + ]; + + USE my_graph = GD.Graph.bars(); + + my_graph.set( + x_label => 'X Label', + y_label => 'Y label', + title => 'A Simple Bar Chart', + y_max_value => 8, + y_tick_number => 8, + y_label_skip => 2, + + # shadows + bar_spacing => 8, + shadow_depth => 4, + shadowclr => 'dred', + + transparent => 0, + ); + my_graph.plot(data).png | stdout(1); + END; + -%] + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"], + [ 5, 12, 24, 33, 19, 8, 6, 15, 21], + [ 1, 2, 5, 6, 3, 1.5, 1, 3, 4], + ]; + + USE my_graph = GD.Graph.bars(); + + my_graph.set( + x_label => 'X Label', + y_label => 'Y label', + title => 'Two data sets', + + # shadows + bar_spacing => 8, + shadow_depth => 4, + shadowclr => 'dred', + + long_ticks => 1, + y_max_value => 40, + y_tick_number => 8, + y_label_skip => 2, + bar_spacing => 3, + + accent_treshold => 200, + + transparent => 0, + ); + my_graph.set_legend( 'Data set 1', 'Data set 2' ); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.bars plugin provides an interface to the GD::Graph::bars +class defined by the GD::Graph module. It allows one or more (x,y) data +sets to be plotted with each point represented by a bar, in addition +to axes and legends. + +See L<GD::Graph> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph module was written by Martien Verbruggen. + + +=head1 VERSION + +1.57, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/bars3d.pm b/lib/Template/Plugin/GD/Graph/bars3d.pm new file mode 100644 index 0000000..79a930b --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/bars3d.pm @@ -0,0 +1,166 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::bars3d +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::bars3d +# package in the GD::Graph3D.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: bars3d.pm,v 1.57 2004/01/13 16:20:56 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::bars3d; + +require 5.004; + +use strict; +use GD::Graph::bars3d; +use Template::Plugin; +use base qw( GD::Graph::bars3d Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + + +sub set_legend +{ + my $self = shift; + + $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_); +} + +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::GD::Graph::bars3d - Create 3D bar graphs with axes and legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.bars3d(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + data = [ + ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec", ], + [-5, -4, -3, -3, -1, 0, 2, 1, 3, 4, 6, 7], + [4, 3, 5, 6, 3,1.5, -1, -3, -4, -6, -7, -8], + [1, 2, 2, 3, 4, 3, 1, -1, 0, 2, 3, 2], + ]; + + USE my_graph = GD.Graph.bars3d(); + + my_graph.set( + x_label => 'Month', + y_label => 'Measure of success', + title => 'A 3d Bar Chart', + + y_max_value => 8, + y_min_value => -8, + y_tick_number => 16, + y_label_skip => 2, + box_axis => 0, + line_width => 3, + zero_axis_only => 1, + x_label_position => 1, + y_label_position => 1, + + x_label_skip => 3, + x_tick_offset => 2, + + transparent => 0, + ); + my_graph.set_legend("Us", "Them", "Others"); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.bars3d plugin provides an interface to the GD::Graph::bars3d +class defined by the GD::Graph3d module. It allows one or more (x,y) data +sets to be plotted as y versus x bars with a 3-dimensional appearance, +together with axes and legends. + +See L<GD::Graph3d> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph3d module was written by Jeremy Wadsack. + + +=head1 VERSION + +1.57, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph3d is copyright (C) 1999,2000 Wadsack-Allen. 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::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>, L<GD::Graph3d|GD::Graph3d> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/lines.pm b/lib/Template/Plugin/GD/Graph/lines.pm new file mode 100644 index 0000000..678cc64 --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/lines.pm @@ -0,0 +1,178 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::lines +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::lines +# package in the GD::Graph.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: lines.pm,v 1.57 2004/01/13 16:20:56 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::lines; + +require 5.004; + +use strict; +use GD::Graph::lines; +use Template::Plugin; +use base qw( GD::Graph::lines Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + + +sub set_legend +{ + my $self = shift; + + $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_); +} + +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::GD::Graph::lines - Create line graphs with axes and legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.lines(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + USE g = GD.Graph.lines(300,200); + x = [1, 2, 3, 4]; + y = [5, 4, 2, 3]; + g.set( + x_label => 'X Label', + y_label => 'Y label', + title => 'Title' + ); + g.plot([x, y]).png | stdout(1); + END; + -%] + + [% FILTER null; + data = [ + ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec", ], + [-5, -4, -3, -3, -1, 0, 2, 1, 3, 4, 6, 7], + [4, 3, 5, 6, 3,1.5, -1, -3, -4, -6, -7, -8], + [1, 2, 2, 3, 4, 3, 1, -1, 0, 2, 3, 2], + ]; + + USE my_graph = GD.Graph.lines(); + + my_graph.set( + x_label => 'Month', + y_label => 'Measure of success', + title => 'A Simple Line Graph', + + y_max_value => 8, + y_min_value => -8, + y_tick_number => 16, + y_label_skip => 2, + box_axis => 0, + line_width => 3, + zero_axis_only => 1, + x_label_position => 1, + y_label_position => 1, + + x_label_skip => 3, + x_tick_offset => 2, + + transparent => 0, + ); + my_graph.set_legend("Us", "Them", "Others"); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.lines plugin provides an interface to the GD::Graph::lines +class defined by the GD::Graph module. It allows one or more (x,y) data +sets to be plotted as y versus x lines with axes and legends. + +See L<GD::Graph> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph module was written by Martien Verbruggen. + + +=head1 VERSION + +1.57, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/lines3d.pm b/lib/Template/Plugin/GD/Graph/lines3d.pm new file mode 100644 index 0000000..1f12715 --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/lines3d.pm @@ -0,0 +1,166 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::lines3d +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::lines3d +# package in the GD::Graph3D.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: lines3d.pm,v 1.57 2004/01/13 16:20:56 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::lines3d; + +require 5.004; + +use strict; +use GD::Graph::lines3d; +use Template::Plugin; +use base qw( GD::Graph::lines3d Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + + +sub set_legend +{ + my $self = shift; + + $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_); +} + +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::GD::Graph::lines3d - Create 3D line graphs with axes and legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.lines3d(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + data = [ + ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec", ], + [-5, -4, -3, -3, -1, 0, 2, 1, 3, 4, 6, 7], + [4, 3, 5, 6, 3,1.5, -1, -3, -4, -6, -7, -8], + [1, 2, 2, 3, 4, 3, 1, -1, 0, 2, 3, 2], + ]; + + USE my_graph = GD.Graph.lines3d(); + + my_graph.set( + x_label => 'Month', + y_label => 'Measure of success', + title => 'A 3d Line Graph', + + y_max_value => 8, + y_min_value => -8, + y_tick_number => 16, + y_label_skip => 2, + box_axis => 0, + line_width => 3, + zero_axis_only => 1, + x_label_position => 1, + y_label_position => 1, + + x_label_skip => 3, + x_tick_offset => 2, + + transparent => 0, + ); + my_graph.set_legend("Us", "Them", "Others"); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.lines3d plugin provides an interface to the GD::Graph::lines3d +class defined by the GD::Graph3d module. It allows one or more (x,y) data +sets to be plotted as y versus x lines with a 3-dimensional appearance, +together with axes and legends. + +See L<GD::Graph3d> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph3d module was written by Jeremy Wadsack. + + +=head1 VERSION + +1.57, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph3d is copyright (C) 1999,2000 Wadsack-Allen. 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::Plugin|Template::Plugin>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph>, L<GD::Graph3d|GD::Graph3d> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/linespoints.pm b/lib/Template/Plugin/GD/Graph/linespoints.pm new file mode 100644 index 0000000..8dc48d9 --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/linespoints.pm @@ -0,0 +1,158 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::linespoints +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::linespoints +# package in the GD::Graph.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: linespoints.pm,v 1.57 2004/01/13 16:20:56 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::linespoints; + +require 5.004; + +use strict; +use GD::Graph::linespoints; +use Template::Plugin; +use base qw( GD::Graph::linespoints Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + + +sub set_legend +{ + my $self = shift; + + $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_); +} + +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::GD::Graph::linespoints - Create line/point graphs with axes and legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.linespoints(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"], + [50, 52, 53, 54, 55, 56, 57, 58, 59], + [60, 61, 61, 63, 68, 66, 65, 61, 58], + [70, 72, 71, 74, 78, 73, 75, 71, 68], + ]; + + USE my_graph = GD.Graph.linespoints; + + my_graph.set( + x_label => 'X Label', + y_label => 'Y label', + title => 'A Lines and Points Graph', + y_max_value => 80, + y_tick_number => 6, + y_label_skip => 2, + y_long_ticks => 1, + x_tick_length => 2, + markers => [ 1, 5 ], + skip_undef => 1, + transparent => 0, + ); + my_graph.set_legend('data set 1', 'data set 2', 'data set 3'); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.linespoints plugin provides an interface to the +GD::Graph::linespoints class defined by the GD::Graph module. It allows +one or more (x,y) data sets to be plotted as y versus x lines, plus +symbols placed at each point, in addition to axes and legends. + +See L<GD::Graph> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph module was written by Martien Verbruggen. + + +=head1 VERSION + +1.57, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/mixed.pm b/lib/Template/Plugin/GD/Graph/mixed.pm new file mode 100644 index 0000000..10dd533 --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/mixed.pm @@ -0,0 +1,176 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::mixed +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::mixed +# package in the GD::Graph.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: mixed.pm,v 1.57 2004/01/13 16:20:56 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::mixed; + +require 5.004; + +use strict; +use GD::Graph::mixed; +use Template::Plugin; +use base qw( GD::Graph::mixed Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + + +sub set_legend +{ + my $self = shift; + + $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_); +} + +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::GD::Graph::mixed - Create mixed graphs with axes and legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.mixed(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"], + [ 1, 2, 5, 6, 3, 1.5, -1, -3, -4], + [ -4, -3, 1, 1, -3, -1.5, -2, -1, 0], + [ 9, 8, 9, 8.4, 7.1, 7.5, 8, 3, -3], + [ 0.1, 0.2, 0.5, 0.4, 0.3, 0.5, 0.1, 0, 0.4], + [ -0.1, 2, 5, 4, -3, 2.5, 3.2, 4, -4], + ]; + + USE my_graph = GD.Graph.mixed(); + + my_graph.set( + types => ['lines', 'lines', 'points', 'area', 'linespoints'], + default_type => 'points', + ); + + my_graph.set( + + x_label => 'X Label', + y_label => 'Y label', + title => 'A Mixed Type Graph', + + y_max_value => 10, + y_min_value => -5, + y_tick_number => 3, + y_label_skip => 0, + x_plot_values => 0, + y_plot_values => 0, + + long_ticks => 1, + x_ticks => 0, + + legend_marker_width => 24, + line_width => 3, + marker_size => 5, + + bar_spacing => 8, + + transparent => 0, + ); + + my_graph.set_legend('one', 'two', 'three', 'four', 'five', 'six'); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.mixed plugin provides an interface to the GD::Graph::mixed +class defined by the GD::Graph module. It allows one or more (x,y) data +sets to be plotted with various styles (lines, points, bars, areas etc). + +See L<GD::Graph> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph module was written by Martien Verbruggen. + + +=head1 VERSION + +1.57, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/pie.pm b/lib/Template/Plugin/GD/Graph/pie.pm new file mode 100644 index 0000000..e72e26c --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/pie.pm @@ -0,0 +1,141 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::pie +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::pie +# package in the GD::Graph.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: pie.pm,v 1.55 2004/01/13 16:20:56 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::pie; + +require 5.004; + +use strict; +use GD::Graph::pie; +use Template::Plugin; +use base qw( GD::Graph::pie Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + +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::GD::Graph::pie - Create pie charts with legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.pie(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th"], + [ 4, 2, 3, 4, 3, 3.5] + ]; + + USE my_graph = GD.Graph.pie( 250, 200 ); + + my_graph.set( + title => 'A Pie Chart', + label => 'Label', + axislabelclr => 'black', + pie_height => 36, + + transparent => 0, + ); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.pie plugin provides an interface to the GD::Graph::pie +class defined by the GD::Graph module. It allows an (x,y) data set to +be plotted as a pie chart. The x values are typically strings. + +See L<GD::Graph> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph module was written by Martien Verbruggen. + + +=head1 VERSION + +1.55, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/pie3d.pm b/lib/Template/Plugin/GD/Graph/pie3d.pm new file mode 100644 index 0000000..5f677e0 --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/pie3d.pm @@ -0,0 +1,145 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::pie3d +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::pie3d +# package in the GD::Graph3D.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: pie3d.pm,v 1.55 2004/01/13 16:20:56 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::pie3d; + +require 5.004; + +use strict; +use GD::Graph::pie3d; +use Template::Plugin; +use base qw( GD::Graph::pie3d Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + +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::GD::Graph::pie3d - Create 3D pie charts with legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.pie3d(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th"], + [ 4, 2, 3, 4, 3, 3.5] + ]; + + USE my_graph = GD.Graph.pie3d( 250, 200 ); + + my_graph.set( + title => 'A Pie Chart', + label => 'Label', + axislabelclr => 'black', + pie_height => 36, + + transparent => 0, + ); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.pie3d plugin provides an interface to the GD::Graph::pie3d +class defined by the GD::Graph module. It allows an (x,y) data set to +be plotted as a 3d pie chart. The x values are typically strings. + +Note that GD::Graph::pie already produces a 3d effect, so GD::Graph::pie3d +is just a wrapper around GD::Graph::pie. Similarly, the plugin +GD.Graph.pie3d is effectively the same as the plugin GD.Graph.pie. + +See L<GD::Graph3d> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph3d module was written by Jeremy Wadsack. The GD::Graph module was written by Martien Verbruggen. + + +=head1 VERSION + +1.55, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph3d is copyright (c) 1999,2000 Wadsack-Allen. All Rights Reserved. GD::Graph is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::points|Template::Plugin::GD::Graph::points>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<GD::Graph|GD::Graph>, L<GD::Graph3d|GD::Graph3d> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Graph/points.pm b/lib/Template/Plugin/GD/Graph/points.pm new file mode 100644 index 0000000..97acf51 --- /dev/null +++ b/lib/Template/Plugin/GD/Graph/points.pm @@ -0,0 +1,155 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Graph::points +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Graph::points +# package in the GD::Graph.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: points.pm,v 1.57 2004/01/13 16:20:56 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Graph::points; + +require 5.004; + +use strict; +use GD::Graph::points; +use Template::Plugin; +use base qw( GD::Graph::points Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.57 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return $class->SUPER::new(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + + +sub set_legend +{ + my $self = shift; + + $self->SUPER::set_legend(ref $_[0] ? @{$_[0]} : @_); +} + +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::GD::Graph::points - Create point graphs with axes and legends + +=head1 SYNOPSIS + + [% USE g = GD.Graph.points(x_size, y_size); %] + +=head1 EXAMPLES + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"], + [ 5, 12, 24, 33, 19, 8, 6, 15, 21], + [ 1, 2, 5, 6, 3, 1.5, 2, 3, 4], + ]; + USE my_graph = GD.Graph.points(); + my_graph.set( + x_label => 'X Label', + y_label => 'Y label', + title => 'A Points Graph', + y_max_value => 40, + y_tick_number => 8, + y_label_skip => 2, + legend_placement => 'RC', + long_ticks => 1, + marker_size => 6, + markers => [ 1, 7, 5 ], + + transparent => 0, + ); + my_graph.set_legend('one', 'two'); + my_graph.plot(data).png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Graph.points plugin provides an interface to the GD::Graph::points +class defined by the GD::Graph module. It allows one or more (x,y) data +sets to be plotted as points, in addition to axes and legends. + +See L<GD::Graph> for more details. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Graph module was written by Martien Verbruggen. + + +=head1 VERSION + +1.57, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Graph is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Graph::lines|Template::Plugin::GD::Graph::lines>, L<Template::Plugin::GD::Graph::lines3d|Template::Plugin::GD::Graph::lines3d>, L<Template::Plugin::GD::Graph::bars|Template::Plugin::GD::Graph::bars>, L<Template::Plugin::GD::Graph::bars3d|Template::Plugin::GD::Graph::bars3d>, L<Template::Plugin::GD::Graph::linespoints|Template::Plugin::GD::Graph::linespoints>, L<Template::Plugin::GD::Graph::area|Template::Plugin::GD::Graph::area>, L<Template::Plugin::GD::Graph::mixed|Template::Plugin::GD::Graph::mixed>, L<Template::Plugin::GD::Graph::pie|Template::Plugin::GD::Graph::pie>, L<Template::Plugin::GD::Graph::pie3d|Template::Plugin::GD::Graph::pie3d>, L<GD::Graph|GD::Graph> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Image.pm b/lib/Template/Plugin/GD/Image.pm new file mode 100644 index 0000000..46a06d7 --- /dev/null +++ b/lib/Template/Plugin/GD/Image.pm @@ -0,0 +1,184 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Image +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Image +# class in the GD.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: Image.pm,v 1.55 2004/01/13 16:20:46 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Image; + +require 5.004; + +use strict; +use GD; +use Template::Plugin; +use base qw( GD Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return new GD::Image(@_); +} + +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::GD::Image - Interface to GD Graphics Library + +=head1 SYNOPSIS + + [% USE im = GD.Image(x_size, y_size) %] + +=head1 EXAMPLES + + [% FILTER null; + USE gdc = GD.Constants; + USE im = GD.Image(200,100); + black = im.colorAllocate(0 ,0, 0); + red = im.colorAllocate(255,0, 0); + r = im.string(gdc.gdLargeFont, 10, 10, "Large Red Text", red); + im.png | stdout(1); + END; + -%] + + [% FILTER null; + USE im = GD.Image(100,100); + # allocate some colors + black = im.colorAllocate(0, 0, 0); + red = im.colorAllocate(255,0, 0); + blue = im.colorAllocate(0, 0, 255); + # Draw a blue oval + im.arc(50,50,95,75,0,360,blue); + # And fill it with red + im.fill(50,50,red); + # Output binary image in PNG format + im.png | stdout(1); + END; + -%] + + [% FILTER null; + USE im = GD.Image(100,100); + USE c = GD.Constants; + USE poly = GD.Polygon; + + # allocate some colors + white = im.colorAllocate(255,255,255); + black = im.colorAllocate(0, 0, 0); + red = im.colorAllocate(255,0, 0); + blue = im.colorAllocate(0, 0,255); + green = im.colorAllocate(0, 255,0); + + # make the background transparent and interlaced + im.transparent(white); + im.interlaced('true'); + + # Put a black frame around the picture + im.rectangle(0,0,99,99,black); + + # Draw a blue oval + im.arc(50,50,95,75,0,360,blue); + + # And fill it with red + im.fill(50,50,red); + + # Draw a blue triangle + poly.addPt(50,0); + poly.addPt(99,99); + poly.addPt(0,99); + im.filledPolygon(poly, blue); + + # Output binary image in PNG format + im.png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Image plugin provides an interface to GD.pm's GD::Image class. +The GD::Image class is the main interface to GD.pm. + +It is very important that no extraneous template output appear before or +after the image. Since some methods return values that would otherwise +appear in the output, it is recommended that GD.Image code be wrapped in +a null filter. The methods that produce the final output (eg, png, jpeg, +gd etc) can then explicitly make their output appear by using the +stdout filter, with a non-zero argument to force binary mode (required +for non-modern operating systems). + +See L<GD> for a complete description of the GD library and all the +methods that can be called via the GD.Image plugin. +See L<Template::Plugin::GD::Constants> for a plugin that allows you +access to GD.pm's constants. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +Lincoln D. Stein wrote the GD.pm interface to the GD library. + + +=head1 VERSION + +1.55, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +The GD.pm interface is copyright 1995-2000, Lincoln D. Stein. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Polygon|Template::Plugin::GD::Polygon>, L<Template::Plugin::GD::Constants|Template::Plugin::GD::Constants>, L<GD|GD> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Polygon.pm b/lib/Template/Plugin/GD/Polygon.pm new file mode 100644 index 0000000..0d1d5c6 --- /dev/null +++ b/lib/Template/Plugin/GD/Polygon.pm @@ -0,0 +1,155 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Polygon +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Polygon +# class in the GD.pm module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: Polygon.pm,v 1.55 2004/01/13 16:20:46 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Polygon; + +require 5.004; + +use strict; +use GD; +use Template::Plugin; +use base qw( Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + return new GD::Polygon(@_); +} + +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::GD::Polygon - Interface to GD module Polygon class + +=head1 SYNOPSIS + + [% USE poly = GD.Polygon; + poly.addPt(50,0); + poly.addPt(99,99); + %] + +=head1 EXAMPLES + + [% FILTER null; + USE im = GD.Image(100,100); + USE c = GD.Constants; + + # allocate some colors + white = im.colorAllocate(255,255,255); + black = im.colorAllocate(0, 0, 0); + red = im.colorAllocate(255,0, 0); + blue = im.colorAllocate(0, 0,255); + green = im.colorAllocate(0, 255,0); + + # make the background transparent and interlaced + im.transparent(white); + im.interlaced('true'); + + # Put a black frame around the picture + im.rectangle(0,0,99,99,black); + + # Draw a blue oval + im.arc(50,50,95,75,0,360,blue); + + # And fill it with red + im.fill(50,50,red); + + # Draw a blue triangle by defining a polygon + USE poly = GD.Polygon; + poly.addPt(50,0); + poly.addPt(99,99); + poly.addPt(0,99); + im.filledPolygon(poly, blue); + + # Output binary image in PNG format + im.png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Polygon plugin provides an interface to GD.pm's GD::Polygon class. + +See L<GD> for a complete description of the GD library and all the +methods that can be called via the GD.Polygon plugin. +See L<Template::Plugin::GD::Image> for the main interface to the +GD functions. +See L<Template::Plugin::GD::Constants> for a plugin that allows you +access to GD.pm's constants. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +Lincoln D. Stein wrote the GD.pm interface to the GD library. + + +=head1 VERSION + +1.55, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +The GD.pm interface is copyright 1995-2000, Lincoln D. Stein. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Image|Template::Plugin::GD::Image>, L<Template::Plugin::GD::Constants|Template::Plugin::GD::Constants>, L<GD|GD> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Text.pm b/lib/Template/Plugin/GD/Text.pm new file mode 100644 index 0000000..f18b2e0 --- /dev/null +++ b/lib/Template/Plugin/GD/Text.pm @@ -0,0 +1,140 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Text +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Text +# module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: Text.pm,v 1.55 2004/01/13 16:20:46 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Text; + +require 5.004; + +use strict; +use GD::Text; +use Template::Plugin; +use base qw( GD::Text Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + return new GD::Text(@_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + +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::GD::Text - Text utilities for use with GD + +=head1 SYNOPSIS + + [% USE gd_text = GD.Text %] + +=head1 EXAMPLES + + [% + USE gd_c = GD.Constants; + USE t = GD.Text; + x = t.set_text('Some text'); + r = t.get('width', 'height', 'char_up', 'char_down'); + r.join(":"); "\n"; # returns 54:13:13:0. + -%] + + [% + USE gd_c = GD.Constants; + USE t = GD.Text(text => 'FooBar Banana', font => gd_c.gdGiantFont); + t.get('width'); "\n"; # returns 117. + -%] + +=head1 DESCRIPTION + +The GD.Text plugin provides an interface to the GD::Text module. +It allows attributes of strings such as width and height in pixels +to be computed. + +See L<GD::Text> for more details. See +L<Template::Plugin::GD::Text::Align> and +L<Template::Plugin::GD::Text::Wrap> for plugins that +allow you to render aligned or wrapped text in GD images. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Text module was written by Martien Verbruggen. + + +=head1 VERSION + +1.55, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Text is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Text::Wrap|Template::Plugin::GD::Text::Wrap>, L<Template::Plugin::GD::Text::Align|Template::Plugin::GD::Text::Align>, L<GD|GD>, L<GD::Text|GD::Text> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Text/Align.pm b/lib/Template/Plugin/GD/Text/Align.pm new file mode 100644 index 0000000..8b79069 --- /dev/null +++ b/lib/Template/Plugin/GD/Text/Align.pm @@ -0,0 +1,147 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Text::Align +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Text::Align +# module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: Align.pm,v 1.55 2004/01/13 16:21:46 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Text::Align; + +require 5.004; + +use strict; +use GD::Text::Align; +use Template::Plugin; +use base qw( GD::Text::Align Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + my $gd = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + return $class->SUPER::new($gd, @_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + +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::GD::Text::Align - Draw aligned strings in GD images + +=head1 SYNOPSIS + + [% USE align = GD.Text.Align(gd_image); %] + +=head1 EXAMPLES + + [% FILTER null; + USE im = GD.Image(100,100); + USE gdc = GD.Constants; + # allocate some colors + black = im.colorAllocate(0, 0, 0); + red = im.colorAllocate(255,0, 0); + blue = im.colorAllocate(0, 0, 255); + # Draw a blue oval + im.arc(50,50,95,75,0,360,blue); + + USE a = GD.Text.Align(im); + a.set_font(gdc.gdLargeFont); + a.set_text("Hello"); + a.set(colour => red, halign => "center"); + a.draw(50,70,0); + + # Output image in PNG format + im.png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Text.Align plugin provides an interface to the GD::Text::Align +module. It allows text to be drawn in GD images with various alignments +and orientations. + +See L<GD::Text::Align> for more details. See +L<Template::Plugin::GD::Text::Wrap> for a plugin +that allow you to render wrapped text in GD images. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Text module was written by Martien Verbruggen. + + +=head1 VERSION + +1.55, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Text is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Text|Template::Plugin::GD::Text>, L<Template::Plugin::GD::Text::Wrap|Template::Plugin::GD::Text::Wrap>, L<GD|GD>, L<GD::Text::Align|GD::Text::Align> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/GD/Text/Wrap.pm b/lib/Template/Plugin/GD/Text/Wrap.pm new file mode 100644 index 0000000..0438599 --- /dev/null +++ b/lib/Template/Plugin/GD/Text/Wrap.pm @@ -0,0 +1,183 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::GD::Text::Wrap +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the GD::Text::Wrap +# module. +# +# AUTHOR +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 2001 Craig Barratt. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: Wrap.pm,v 1.55 2004/01/13 16:21:46 abw Exp $ +# +#============================================================================ + +package Template::Plugin::GD::Text::Wrap; + +require 5.004; + +use strict; +use GD::Text::Wrap; +use Template::Plugin; +use base qw( GD::Text::Wrap Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.55 $ =~ /(\d+)\.(\d+)/); + +sub new +{ + my $class = shift; + my $context = shift; + my $gd = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + return $class->SUPER::new($gd, @_); +} + +sub set +{ + my $self = shift; + + push(@_, %{pop(@_)}) if ( @_ & 1 && ref($_[@_-1]) eq "HASH" ); + $self->SUPER::set(@_); +} + +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::GD::Text::Wrap - Break and wrap strings in GD images + +=head1 SYNOPSIS + + [% USE align = GD.Text.Wrap(gd_image); %] + +=head1 EXAMPLES + + [% FILTER null; + USE gd = GD.Image(200,400); + USE gdc = GD.Constants; + black = gd.colorAllocate(0, 0, 0); + green = gd.colorAllocate(0, 255, 0); + txt = "This is some long text. " | repeat(10); + USE wrapbox = GD.Text.Wrap(gd, + line_space => 4, + color => green, + text => txt, + ); + wrapbox.set_font(gdc.gdMediumBoldFont); + wrapbox.set(align => 'center', width => 160); + wrapbox.draw(20, 20); + gd.png | stdout(1); + END; + -%] + + [% txt = BLOCK -%] + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, + sed diam nonummy nibh euismod tincidunt ut laoreet dolore + magna aliquam erat volutpat. + [% END -%] + [% FILTER null; + # + # This example follows the example in GD::Text::Wrap, except + # we create a second image that is a copy just enough of the + # first image to hold the final text, plus a border. + # + USE gd = GD.Image(400,400); + USE gdc = GD.Constants; + green = gd.colorAllocate(0, 255, 0); + blue = gd.colorAllocate(0, 0, 255); + USE wrapbox = GD.Text.Wrap(gd, + line_space => 4, + color => green, + text => txt, + ); + wrapbox.set_font(gdc.gdMediumBoldFont); + wrapbox.set(align => 'center', width => 140); + rect = wrapbox.get_bounds(5, 5); + x0 = rect.0; + y0 = rect.1; + x1 = rect.2 + 9; + y1 = rect.3 + 9; + gd.filledRectangle(0, 0, x1, y1, blue); + gd.rectangle(0, 0, x1, y1, green); + wrapbox.draw(x0, y0); + nx = x1 + 1; + ny = y1 + 1; + USE gd2 = GD.Image(nx, ny); + gd2.copy(gd, 0, 0, 0, 0, x1, y1); + gd2.png | stdout(1); + END; + -%] + +=head1 DESCRIPTION + +The GD.Text.Wrap plugin provides an interface to the GD::Text::Wrap +module. It allows multiples line of text to be drawn in GD images with +various wrapping and alignment. + +See L<GD::Text::Wrap> for more details. See +L<Template::Plugin::GD::Text::Align> for a plugin +that allow you to draw text with various alignment +and orientation. + +=head1 AUTHOR + +Craig Barratt E<lt>craig@arraycomm.comE<gt> + + +The GD::Text module was written by Martien Verbruggen. + + +=head1 VERSION + +1.55, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Craig Barratt E<lt>craig@arraycomm.comE<gt> + +GD::Text is copyright 1999 Martien Verbruggen. + +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>, L<Template::Plugin::GD|Template::Plugin::GD>, L<Template::Plugin::GD::Text::Align|Template::Plugin::GD::Text::Align>, L<GD|GD>, L<GD::Text::Wrap|GD::Text::Wrap> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/HTML.pm b/lib/Template/Plugin/HTML.pm new file mode 100644 index 0000000..5cb63e0 --- /dev/null +++ b/lib/Template/Plugin/HTML.pm @@ -0,0 +1,197 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::HTML +# +# DESCRIPTION +# +# Template Toolkit plugin providing useful functionality for generating +# HTML. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +#---------------------------------------------------------------------------- +# +# $Id: HTML.pm,v 2.56 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::HTML; + +require 5.004; + +use strict; +use vars qw( $VERSION ); +use base qw( Template::Plugin ); +use Template::Plugin; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.56 $ =~ /(\d+)\.(\d+)/); + +sub new { + my ($class, $context, @args) = @_; + my $hash = ref $args[-1] eq 'HASH' ? pop @args : { }; + bless { + _SORTED => $hash->{ sorted } || 0, + }, $class; +} + +sub element { + my ($self, $name, $attr) = @_; + ($name, $attr) = %$name if ref $name eq 'HASH'; + return '' unless defined $name and length $name; + $attr = $self->attributes($attr); + $attr = " $attr" if $attr; + return "<$name$attr>"; +} + +sub attributes { + my ($self, $hash) = @_; + return '' unless UNIVERSAL::isa($hash, 'HASH'); + + my @keys = keys %$hash; + @keys = sort @keys if $self->{ _SORTED }; + + join(' ', map { + "$_=\"" . $self->escape( $hash->{ $_ } ) . '"'; + } @keys); +} + +sub escape { + my ($self, $text) = @_; + for ($text) { + s/&/&/g; + s/</</g; + s/>/>/g; + s/"/"/g; + } + $text; +} + +sub url { + my ($self, $text) = @_; + return undef unless defined $text; + $text =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg; + return $text; +} + + +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::HTML - Plugin to create HTML elements + +=head1 SYNOPSIS + + [% USE HTML %] + + [% HTML.escape("if (a < b && c > d) ..." %] + + [% HTML.element(table => { border => 1, cellpadding => 2 }) %] + + [% HTML.attributes(border => 1, cellpadding => 2) %] + +=head1 DESCRIPTION + +The HTML plugin is very new and very basic, implementing a few useful +methods for generating HTML. It is likely to be extended in the future +or integrated with a larger project to generate HTML elements in a generic +way (as discussed recently on the mod_perl mailing list). + +=head1 METHODS + +=head2 escape(text) + +Returns the source text with any HTML reserved characters such as +E<lt>, E<gt>, etc., correctly esacped to their entity equivalents. + +=head2 attributes(hash) + +Returns the elements of the hash array passed by reference correctly +formatted (e.g. values quoted and correctly escaped) as attributes for +an HTML element. + +=head2 element(type, attributes) + +Generates an HTML element of the specified type and with the attributes +provided as an optional hash array reference as the second argument or +as named arguments. + + [% HTML.element(table => { border => 1, cellpadding => 2 }) %] + [% HTML.element('table', border=1, cellpadding=2) %] + [% HTML.element(table => attribs) %] + +=head1 DEBUGGING + +The HTML plugin accepts a 'sorted' option as a constructor argument +which, when set to any true value, causes the attributes generated by +the attributes() method (either directly or via element()) to be +returned in sorted order. Order of attributes isn't important in +HTML, but this is provided mainly for the purposes of debugging where +it is useful to have attributes generated in a deterministic order +rather than whatever order the hash happened to feel like returning +the keys in. + + [% USE HTML(sorted=1) %] + [% HTML.element( foo => { charlie => 1, bravo => 2, alpha => 3 } ) %] + +generates: + + <foo alpha="3" bravo="2" charlie="1"> + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.56, 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: diff --git a/lib/Template/Plugin/Image.pm b/lib/Template/Plugin/Image.pm new file mode 100644 index 0000000..4eb509c --- /dev/null +++ b/lib/Template/Plugin/Image.pm @@ -0,0 +1,425 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Image +# +# DESCRIPTION +# Plugin for encapsulating information about an image. +# +# AUTHOR +# Andy Wardley <abw@wardley.org> +# +# COPYRIGHT +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +# REVISION +# $Id: Image.pm,v 1.13 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Image; + +require 5.004; + +use strict; +use Template::Exception; +use Template::Plugin; +use File::Spec; +#use Image::Info; +#use Image::Size; + +use base qw( Template::Plugin ); +use vars qw( $VERSION $AUTOLOAD ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.13 $ =~ /(\d+)\.(\d+)/); + +BEGIN { + if (eval { require Image::Info; }) { + *img_info = \&Image::Info::image_info; + } + elsif (eval { require Image::Size; }) { + *img_info = sub { + my $file = shift; + my @stuff = Image::Size::imgsize($file); + return { "width" => $stuff[0], + "height" => $stuff[1], + "error" => + # imgsize returns either a three letter file type + # or an error message as third value + (defined($stuff[2]) && length($stuff[2]) > 3 + ? $stuff[2] + : undef), + }; + } + } + else { + die(Template::Exception->new("image", + "Couldn't load Image::Info or Image::Size: $@")); + } + +} + +#------------------------------------------------------------------------ +# new($context, $name, \%config) +# +# Create a new Image object. Takes the pathname of the file as +# the argument following the context and an optional +# hash reference of configuration parameters. +#------------------------------------------------------------------------ + +sub new { + my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { }; + my ($class, $context, $name) = @_; + my ($root, $file, $type); + + # name can be a positional or named argument + $name = $config->{ name } unless defined $name; + + return $class->throw('no image file specified') + unless defined $name and length $name; + + # name can be specified as an absolute path or relative + # to a root directory + + if ($root = $config->{ root }) { + $file = File::Spec->catfile($root, $name); + } + else { + $file = $name; + } + + # Make a note of whether we are using Image::Size or + # Image::Info -- at least for the test suite + $type = $INC{"Image/Size.pm"} ? "Image::Size" : "Image::Info"; + + # do we want to check to see if file exists? + + bless { + name => $name, + file => $file, + root => $root, + type => $type, + }, $class; +} + +#------------------------------------------------------------------------ +# init() +# +# Calls image_info on $self->{ file } +#------------------------------------------------------------------------ + +sub init { + my $self = shift; + return $self if $self->{ size }; + + my $image = img_info($self->{ file }); + return $self->throw($image->{ error }) if defined $image->{ error }; + + @$self{ keys %$image } = values %$image; + $self->{ size } = [ $image->{ width }, $image->{ height } ]; + + $self->{ modtime } = (stat $self->{ file })[10]; + + return $self; +} + +#------------------------------------------------------------------------ +# attr() +# +# Return the width and height as HTML/XML attributes. +#------------------------------------------------------------------------ + +sub attr { + my $self = shift; + my $size = $self->size(); + return "width=\"$size->[0]\" height=\"$size->[1]\""; +} + +#------------------------------------------------------------------------ +# modtime() +# +# Return last modification time as a time_t: +# +# [% date.format(image.modtime, "%Y/%m/%d") %] +#------------------------------------------------------------------------ + +sub modtime { + my $self = shift; + $self->init; + return $self->{ modtime }; +} + +#------------------------------------------------------------------------ +# tag(\%options) +# +# Return an XHTML img tag. +#------------------------------------------------------------------------ + +sub tag { + my $self = shift; + my $options = ref $_[0] eq 'HASH' ? shift : { @_ }; + + my $tag = "<img src=\"$self->{ name }\" " . $self->attr(); + + if (%$options) { + while (my ($key, $val) = each %$options) { + $tag .= " $key=\"$val\""; + } + } + + $tag .= ' />'; + + return $tag; +} + + +sub throw { + my ($self, $error) = @_; + die (Template::Exception->new('Image', $error)); +} + +sub AUTOLOAD { + my $self = shift; + (my $a = $AUTOLOAD) =~ s/.*:://; + + $self->init; + return $self->{ $a }; +} + +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::Image - Plugin access to image sizes + +=head1 SYNOPSIS + + [% USE Image(filename) %] + [% Image.width %] + [% Image.height %] + [% Image.size.join(', ') %] + [% Image.attr %] + [% Image.tag %] + +=head1 DESCRIPTION + +This plugin provides an interface to the Image::Info or Image::Size +modules for determining the size of image files. + +You can specify the plugin name as either 'Image' or 'image'. The +plugin object created will then have the same name. The file name of +the image should be specified as a positional or named argument. + + [% # all these are valid, take your pick %] + [% USE Image('foo.gif') %] + [% USE image('bar.gif') %] + [% USE Image 'ping.gif' %] + [% USE image(name='baz.gif') %] + [% USE Image name='pong.gif' %] + +You can also provide an alternate name for an Image plugin object. + + [% USE img1 = image 'foo.gif' %] + [% USE img2 = image 'bar.gif' %] + +The 'width' and 'height' methods return the width and height of the +image, respectively. The 'size' method returns a reference to a 2 +element list containing the width and height. + + [% USE image 'foo.gif' %] + width: [% image.width %] + height: [% image.height %] + size: [% image.size.join(', ') %] + +The 'attr' method returns the height and width as HTML/XML attributes. + + [% USE image 'foo.gif' %] + [% image.attr %] + +Typical output: + + width="60" height="20" + +The 'tag' method returns a complete XHTML tag referencing the image. + + [% USE image 'foo.gif' %] + [% image.tag %] + +Typical output: + + <img src="foo.gif" width="60" height="20" /> + +You can provide any additional attributes that should be added to the +XHTML tag. + + + [% USE image 'foo.gif' %] + [% image.tag(border=0, class="logo") %] + +Typical output: + + <img src="foo.gif" width="60" height="20" border="0" class="logo" /> + +The 'modtime' method returns the ctime of the file in question, suitable +for use with date.format: + + [% USE image 'foo.gif' %] + [% USE date %] + [% date.format(image.modtime, "%B, %e %Y") %] + +=head1 CATCHING ERRORS + +If the image file cannot be found then the above methods will throw an +'Image' error. You can enclose calls to these methods in a +TRY...CATCH block to catch any potential errors. + + [% TRY; + image.width; + CATCH; + error; # print error + END + %] + +=head1 USING Image::Info + +At run time, the plugin tries to load Image::Info in preference to +Image::Size. If Image::Info is found, then some additional methods are +available, in addition to 'size', 'width', 'height', 'attr', and 'tag'. +These additional methods are named after the elements that Image::Info +retrieves from the image itself; see L<Image::Info> for more details +-- the types of methods available depend on the type of image. +These additional methods will always include the following: + +=over 4 + +=item file_media_type + +This is the MIME type that is appropriate for the given file format. +The corresponding value is a string like: "image/png" or "image/jpeg". + +=item file_ext + +The is the suggested file name extention for a file of the given +file format. The value is a 3 letter, lowercase string like +"png", "jpg". + + +=item color_type + +The value is a short string describing what kind of values the pixels +encode. The value can be one of the following: + + Gray + GrayA + RGB + RGBA + CMYK + YCbCr + CIELab + +These names can also be prefixed by "Indexed-" if the image is +composed of indexes into a palette. Of these, only "Indexed-RGB" is +likely to occur. + +(It is similar to the TIFF field PhotometricInterpretation, but this +name was found to be too long, so we used the PNG inpired term +instead.) + +=item resolution + +The value of this field normally gives the physical size of the image +on screen or paper. When the unit specifier is missing then this field +denotes the squareness of pixels in the image. + +The syntax of this field is: + + <res> <unit> + <xres> "/" <yres> <unit> + <xres> "/" <yres> + +The E<lt>resE<gt>, E<lt>xresE<gt> and E<lt>yresE<gt> fields are +numbers. The E<lt>unitE<gt> is a string like C<dpi>, C<dpm> or +C<dpcm> (denoting "dots per inch/cm/meter). + +=item SamplesPerPixel + +This says how many channels there are in the image. For some image +formats this number might be higher than the number implied from the +C<color_type>. + +=item BitsPerSample + +This says how many bits are used to encode each of samples. The value +is a reference to an array containing numbers. The number of elements +in the array should be the same as C<SamplesPerPixel>. + +=item Comment + +Textual comments found in the file. The value is a reference to an +array if there are multiple comments found. + +=item Interlace + +If the image is interlaced, then this tell which interlace method is +used. + +=item Compression + +This tell which compression algorithm is used. + +=item Gamma + +A number. + + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +1.13, 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: diff --git a/lib/Template/Plugin/Iterator.pm b/lib/Template/Plugin/Iterator.pm new file mode 100644 index 0000000..0f33b2f --- /dev/null +++ b/lib/Template/Plugin/Iterator.pm @@ -0,0 +1,118 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Iterator +# +# DESCRIPTION +# +# Plugin to create a Template::Iterator from a list of items and optional +# configuration parameters. +# +# 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: Iterator.pm,v 2.62 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Iterator; + +require 5.004; + +use strict; +use vars qw( $VERSION ); +use base qw( Template::Plugin ); +use Template::Plugin; +use Template::Iterator; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.62 $ =~ /(\d+)\.(\d+)/); + +#------------------------------------------------------------------------ +# new($context, \@data, \%args) +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $context = shift; + Template::Iterator->new(@_); +} + +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::Iterator - Plugin to create iterators (Template::Iterator) + +=head1 SYNOPSIS + + [% USE iterator(list, args) %] + + [% FOREACH item = iterator %] + [% '<ul>' IF iterator.first %] + <li>[% item %] + [% '</ul>' IF iterator.last %] + [% END %] + +=head1 DESCRIPTION + +The iterator plugin provides a way to create a Template::Iterator object +to iterate over a data set. An iterator is implicitly automatically by the +FOREACH directive. This plugin allows the iterator to be explicitly created +with a given name. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.62, 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>, L<Template::Iterator|Template::Iterator> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/Pod.pm b/lib/Template/Plugin/Pod.pm new file mode 100644 index 0000000..e5f82c2 --- /dev/null +++ b/lib/Template/Plugin/Pod.pm @@ -0,0 +1,116 @@ +#============================================================================== +# +# Template::Plugin::Pod +# +# DESCRIPTION +# Pod parser and object model. +# +# 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. +# +# REVISION +# $Id: Pod.pm,v 2.62 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Pod; + +require 5.004; + +use strict; +use Template::Plugin; +use vars qw( $VERSION ); +use base qw( Template::Plugin ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.62 $ =~ /(\d+)\.(\d+)/); + +use Pod::POM; + +#------------------------------------------------------------------------ +# new($context, \%config) +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $context = shift; + + Pod::POM->new(@_); +} + + +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::Pod - Plugin interface to Pod::POM (Pod Object Model) + +=head1 SYNOPSIS + + [% USE Pod(podfile) %] + + [% FOREACH head1 = Pod.head1; + FOREACH head2 = head1/head2; + ... + END; + END + %] + +=head1 DESCRIPTION + +This plugin is an interface to the Pod::POM module. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.62, 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>, L<Pod::POM|Pod::POM> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/Procedural.pm b/lib/Template/Plugin/Procedural.pm new file mode 100644 index 0000000..8601225 --- /dev/null +++ b/lib/Template/Plugin/Procedural.pm @@ -0,0 +1,170 @@ +#============================================================================== +# +# Template::Plugin::Procedural +# +# DESCRIPTION +# +# A Template Plugin to provide a Template Interface to Data::Dumper +# +# AUTHOR +# Mark Fowler <mark@twoshortplanks.com> +# +# COPYRIGHT +# +# Copyright (C) 2002 Mark Fowler. All Rights Reserved +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#------------------------------------------------------------------------------ +# +# $Id: Procedural.pm,v 1.11 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================== + +package Template::Plugin::Procedural; + +require 5.004; + +use strict; + +use vars qw( $VERSION $DEBUG $AUTOLOAD ); +use base qw( Template::Plugin ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.11 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0 unless defined $DEBUG; + +#------------------------------------------------------------------------ +# load +#------------------------------------------------------------------------ + +sub load +{ + my ($class, $context) = @_; + + # create a proxy namespace that will be used for objects + my $proxy = "Template::Plugin::" . $class; + + # okay, in our proxy create the autoload routine that will + # call the right method in the real class + no strict "refs"; + *{ $proxy . "::AUTOLOAD" } = + sub + { + # work out what the method is called + $AUTOLOAD =~ s!^.*::!!; + + print STDERR "Calling '$AUTOLOAD' in '$class'\n" + if $DEBUG; + + # look up the sub for that method (but in a OO way) + my $uboat = $class->can($AUTOLOAD); + + # if it existed call it as a subroutine, not as a method + if ($uboat) + { + shift @_; + return $uboat->(@_); + } + + print STDERR "Eeek, no such method '$AUTOLOAD'\n" + if $DEBUG; + + return ""; + }; + + # create a simple new method that simply returns a blessed + # scalar as the object. + *{ $proxy . "::new" } = + sub + { + my $this; + return bless \$this, $_[0]; + }; + + return $proxy; +} + +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::Procedural - Base class for procedural plugins + +=head1 SYNOPSIS + + package Template::Plugin::LWPSimple; + use base qw(Template::Plugin::Procedural); + use LWP::Simple; # exports 'get' + 1; + + [% USE LWPSimple %] + [% LWPSimple.get("http://www.tt2.org/") %] + +=head1 DESCRIPTION + +B<Template::Plugin::Procedural> is a base class for Template Toolkit +plugins that causes defined subroutines to be called directly rather +than as a method. Essentially this means that subroutines will not +receive the class name or object as its first argument. + +This is most useful when creating plugins for modules that normally +work by exporting subroutines that do not expect such additional +arguments. + +Despite the fact that subroutines will not be called in an OO manner, +inheritance still function as normal. A class that uses +B<Template::Plugin::Procedural> can be subclassed and both subroutines +defined in the subclass and subroutines defined in the original class +will be available to the Template Toolkit and will be called without +the class/object argument. + +=head1 AUTHOR + +Mark Fowler E<lt>mark@twoshortplanks.comE<gt> + +L<http://www.twoshortplanks.com|http://www.twoshortplanks.com> + + + + +=head1 VERSION + +1.11, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2002 Mark Fowler E<lt>mark@twoshortplanks.comE<gt> + +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|Template>, L<Template::Plugin|Template::Plugin> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/String.pm b/lib/Template/Plugin/String.pm new file mode 100644 index 0000000..34dd007 --- /dev/null +++ b/lib/Template/Plugin/String.pm @@ -0,0 +1,796 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::String +# +# DESCRIPTION +# Template Toolkit plugin to implement a basic String object. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 2001 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. +# +# REVISION +# $Id: String.pm,v 2.33 2004/01/13 16:20:38 abw Exp $ +# +#============================================================================ + +package Template::Plugin::String; + +require 5.004; + +use strict; +use Template::Plugin; +use Template::Exception; + +use base qw( Template::Plugin ); +use vars qw( $VERSION $ERROR); +use overload q|""| => "text", + fallback => 1; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.33 $ =~ /(\d+)\.(\d+)/); +$ERROR = ''; + +*centre = \*center; +*append = \*push; +*prepend = \*unshift; + +#------------------------------------------------------------------------ + +sub new { + my ($class, @args) = @_; + my $context = ref $class ? undef : shift(@args); + my $config = @args && ref $args[-1] eq 'HASH' ? pop(@args) : { }; + + $class = ref($class) || $class; + + my $text = defined $config->{ text } + ? $config->{ text } + : (@args ? shift(@args) : ''); + +# print STDERR "text: [$text]\n"; +# print STDERR "class: [$class]\n"; + + my $self = bless { + text => $text, + filters => [ ], + _CONTEXT => $context, + }, $class; + + my $filter = $config->{ filter } || $config->{ filters }; + + # install any output filters specified as 'filter' or 'filters' option + $self->output_filter($filter) + if $filter; + + return $self; +} + + +sub text { + my $self = shift; + return $self->{ text } unless @{ $self->{ filters } }; + + my $text = $self->{ text }; + my $context = $self->{ _CONTEXT }; + + foreach my $dispatch (@{ $self->{ filters } }) { + my ($name, $args) = @$dispatch; + my $code = $context->filter($name, $args) + || $self->throw($context->error()); + $text = &$code($text); + } + return $text; +} + + +sub copy { + my $self = shift; + $self->new($self->{ text }); +} + + +sub throw { + my $self = shift; + + die (Template::Exception->new('String', join('', @_))); +} + + +#------------------------------------------------------------------------ +# output_filter($filter) +# +# Install automatic output filter(s) for the string. $filter can a list: +# [ 'name1', 'name2' => [ ..args.. ], name4 => { ..args.. } ] or a hash +# { name1 => '', name2 => [ args ], name3 => { args } } +#------------------------------------------------------------------------ + +sub output_filter { + my ($self, $filter) = @_; + my ($name, $args, $dispatch); + my $filters = $self->{ filters }; + my $count = 0; + + if (ref $filter eq 'HASH') { + $filter = [ %$filter ]; + } + elsif (ref $filter ne 'ARRAY') { + $filter = [ split(/\s*\W+\s*/, $filter) ]; + } + + while (@$filter) { + $name = shift @$filter; + + # args may follow as a reference (or empty string, e.g. { foo => '' } + if (@$filter && (ref($filter->[0]) || ! length $filter->[0])) { + $args = shift @$filter; + if ($args) { + $args = [ $args ] unless ref $args eq 'ARRAY'; + } + else { + $args = [ ]; + } + } + else { + $args = [ ]; + } + +# $self->DEBUG("adding output filter $name(@$args)\n"); + + push(@$filters, [ $name, $args ]); + $count++; + } + + return ''; +} + + +#------------------------------------------------------------------------ + +sub push { + my $self = shift; + $self->{ text } .= join('', @_); + return $self; +} + + +sub unshift { + my $self = shift; + $self->{ text } = join('', @_) . $self->{ text }; + return $self; +} + + +sub pop { + my $self = shift; + my $strip = shift || return $self; + $self->{ text } =~ s/$strip$//; + return $self; +} + + +sub shift { + my $self = shift; + my $strip = shift || return $self; + $self->{ text } =~ s/^$strip//; + return $self; +} + +#------------------------------------------------------------------------ + +sub center { + my ($self, $width) = @_; + my $text = $self->{ text }; + my $len = length $text; + $width ||= 0; + + if ($len < $width) { + my $lpad = int(($width - $len) / 2); + my $rpad = $width - $len - $lpad; + $self->{ text } = (' ' x $lpad) . $self->{ text } . (' ' x $rpad); + } + + return $self; +} + + +sub left { + my ($self, $width) = @_; + my $len = length $self->{ text }; + $width ||= 0; + + $self->{ text } .= (' ' x ($width - $len)) + if $width > $len; + + return $self; +} + + +sub right { + my ($self, $width) = @_; + my $len = length $self->{ text }; + $width ||= 0; + + $self->{ text } = (' ' x ($width - $len)) . $self->{ text } + if $width > $len; + + return $self; +} + + +sub format { + my ($self, $format) = @_; + $format = '%s' unless defined $format; + $self->{ text } = sprintf($format, $self->{ text }); + return $self; +} + + +sub filter { + my ($self, $name, @args) = @_; + + my $context = $self->{ _CONTEXT }; + + my $code = $context->filter($name, \@args) + || $self->throw($context->error()); + return &$code($self->{ text }); +} + + +#------------------------------------------------------------------------ + +sub upper { + my $self = CORE::shift; + $self->{ text } = uc $self->{ text }; + return $self; +} + + +sub lower { + my $self = CORE::shift; + $self->{ text } = lc $self->{ text }; + return $self; +} + + +sub capital { + my $self = CORE::shift; + $self->{ text } =~ s/^(.)/\U$1/; + return $self; +} + +#------------------------------------------------------------------------ + +sub chop { + my $self = CORE::shift; + chop $self->{ text }; + return $self; +} + + +sub chomp { + my $self = CORE::shift; + chomp $self->{ text }; + return $self; +} + + +sub trim { + my $self = CORE::shift; + for ($self->{ text }) { + s/^\s+//; + s/\s+$//; + } + return $self; +} + + +sub collapse { + my $self = CORE::shift; + for ($self->{ text }) { + s/^\s+//; + s/\s+$//; + s/\s+/ /g + } + return $self; + +} + +#------------------------------------------------------------------------ + +sub length { + my $self = CORE::shift; + return length $self->{ text }; +} + + +sub truncate { + my ($self, $length, $suffix) = @_; + return $self unless defined $length; + $suffix ||= ''; + return $self if CORE::length $self->{ text } <= $length; + $self->{ text } = substr($self->{ text }, 0, + $length - CORE::length($suffix)) . $suffix; + return $self; +} + + +sub repeat { + my ($self, $n) = @_; + return $self unless defined $n; + $self->{ text } = $self->{ text } x $n; + return $self; +} + + +sub replace { + my ($self, $search, $replace) = @_; + return $self unless defined $search; + $replace = '' unless defined $replace; + $self->{ text } =~ s/$search/$replace/g; + return $self; +} + + +sub remove { + my ($self, $search) = @_; + $search = '' unless defined $search; + $self->{ text } =~ s/$search//g; + return $self; +} + + +sub split { + my $self = CORE::shift; + my $split = CORE::shift; + my $limit = CORE::shift || 0; + $split = '\s+' unless defined $split; + return [ split($split, $self->{ text }, $limit) ]; +} + + +sub search { + my ($self, $pattern) = @_; + return $self->{ text } =~ /$pattern/; +} + + +sub equals { + my ($self, $comparison) = @_; + return $self->{ text } eq $comparison; +} + + +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::String - Object oriented interface for string manipulation + +=head1 SYNOPSIS + + # create String objects via USE directive + [% USE String %] + [% USE String 'initial text' %] + [% USE String text => 'initial text' %] + + # or from an existing String via new() + [% newstring = String.new %] + [% newstring = String.new('newstring text') %] + [% newstring = String.new( text => 'newstring text' ) %] + + # or from an existing String via copy() + [% newstring = String.copy %] + + # append text to string + [% String.append('text to append') %] + + # format left, right or center/centre padded + [% String.left(20) %] + [% String.right(20) %] + [% String.center(20) %] # American spelling + [% String.centre(20) %] # European spelling + + # and various other methods... + +=head1 DESCRIPTION + +This module implements a String class for doing stringy things to +text in an object-oriented way. + +You can create a String object via the USE directive, adding any +initial text value as an argument or as the named parameter 'text'. + + [% USE String %] + [% USE String 'initial text' %] + [% USE String text='initial text' %] + +The object created will be referenced as 'String' by default, but you +can provide a different variable name for the object to be assigned +to: + + [% USE greeting = String 'Hello World' %] + +Once you've got a String object, you can use it as a prototype to +create other String objects with the new() method. + + [% USE String %] + [% greeting = String.new('Hello World') %] + +The new() method also accepts an initial text string as an argument +or the named parameter 'text'. + + [% greeting = String.new( text => 'Hello World' ) %] + +You can also call copy() to create a new String as a copy of the +original. + + [% greet2 = greeting.copy %] + +The String object has a text() method to return the content of the +string. + + [% greeting.text %] + +However, it is sufficient to simply print the string and let the +overloaded stringification operator call the text() method +automatically for you. + + [% greeting %] + +Thus, you can treat String objects pretty much like any regular piece +of text, interpolating it into other strings, for example: + + [% msg = "It printed '$greeting' and then dumped core\n" %] + +You also have the benefit of numerous other methods for manipulating +the string. + + [% msg.append("PS Don't eat the yellow snow") %] + +Note that all methods operate on and mutate the contents of the string +itself. If you want to operate on a copy of the string then simply +take a copy first: + + [% msg.copy.append("PS Don't eat the yellow snow") %] + +These methods return a reference to the String object itself. This +allows you to chain multiple methods together. + + [% msg.copy.append('foo').right(72) %] + +It also means that in the above examples, the String is returned which +causes the text() method to be called, which results in the new value of +the string being printed. To suppress printing of the string, you can +use the CALL directive. + + [% foo = String.new('foo') %] + + [% foo.append('bar') %] # prints "foobar" + + [% CALL foo.append('bar') %] # nothing + +=head1 METHODS + +=head2 Construction Methods + +The following methods are used to create new String objects. + +=over 4 + +=item new() + +Creates a new string using an initial value passed as a positional +argument or the named parameter 'text'. + + [% USE String %] + [% msg = String.new('Hello World') %] + [% msg = String.new( text => 'Hello World' ) %] + +=item copy() + +Creates a new String object which contains a copy of the original string. + + [% msg2 = msg.copy %] + +=back + +=head2 Inspection Methods + +These methods are used to inspect the string content or other parameters +relevant to the string. + +=over 4 + +=item text() + +Returns the internal text value of the string. The stringification +operator is overloaded to call this method. Thus the following are +equivalent: + + [% msg.text %] + [% msg %] + +=item length() + +Returns the length of the string. + + [% USE String("foo") %] + + [% String.length %] # => 3 + +=item search($pattern) + +Searches the string for the regular expression specified in $pattern +returning true if found or false otherwise. + + [% item = String.new('foo bar baz wiz waz woz') %] + + [% item.search('wiz') ? 'WIZZY! :-)' : 'not wizzy :-(' %] + +=item split($pattern, $limit) + +Splits the string based on the delimiter $pattern and optional $limit. +Delegates to Perl's internal split() so the parameters are exactly the same. + + [% FOREACH item.split %] + ... + [% END %] + + [% FOREACH item.split('baz|waz') %] + ... + [% END %] + +=back + +=head2 Mutation Methods + +These methods modify the internal value of the string. For example: + + [% USE str=String('foobar') %] + + [% str.append('.html') %] # str => 'foobar.html' + +The value of the String 'str' is now 'foobar.html'. If you don't want +to modify the string then simply take a copy first. + + [% str.copy.append('.html') %] + +These methods all return a reference to the String object itself. This +has two important benefits. The first is that when used as above, the +String object 'str' returned by the append() method will be stringified +with a call to its text() method. This will return the newly modified +string content. In other words, a directive like: + + [% str.append('.html') %] + +will update the string and also print the new value. If you just want +to update the string but not print the new value then use CALL. + + [% CALL str.append('.html') %] + +The other benefit of these methods returning a reference to the String +is that you can chain as many different method calls together as you +like. For example: + + [% String.append('.html').trim.format(href) %] + +Here are the methods: + +=over 4 + +=item push($suffix, ...) / append($suffix, ...) + +Appends all arguments to the end of the string. The +append() method is provided as an alias for push(). + + [% msg.push('foo', 'bar') %] + [% msg.append('foo', 'bar') %] + +=item pop($suffix) + +Removes the suffix passed as an argument from the end of the String. + + [% USE String 'foo bar' %] + [% String.pop(' bar') %] # => 'foo' + +=item unshift($prefix, ...) / prepend($prefix, ...) + +Prepends all arguments to the beginning of the string. The +prepend() method is provided as an alias for unshift(). + + [% msg.unshift('foo ', 'bar ') %] + [% msg.prepend('foo ', 'bar ') %] + +=item shift($prefix) + +Removes the prefix passed as an argument from the start of the String. + + [% USE String 'foo bar' %] + [% String.shift('foo ') %] # => 'bar' + +=item left($pad) + +If the length of the string is less than $pad then the string is left +formatted and padded with spaces to $pad length. + + [% msg.left(20) %] + +=item right($pad) + +As per left() but right padding the String to a length of $pad. + + [% msg.right(20) %] + +=item center($pad) / centre($pad) + +As per left() and right() but formatting the String to be centered within +a space padded string of length $pad. The centre() method is provided as +an alias for center() to keep Yanks and Limeys happy. + + [% msg.center(20) %] # American spelling + [% msg.centre(20) %] # European spelling + +=item format($format) + +Apply a format in the style of sprintf() to the string. + + [% USE String("world") %] + [% String.format("Hello %s\n") %] # => "Hello World\n" + +=item upper() + +Converts the string to upper case. + + [% USE String("foo") %] + + [% String.upper %] # => 'FOO' + +=item lower() + +Converts the string to lower case + + [% USE String("FOO") %] + + [% String.lower %] # => 'foo' + +=item capital() + +Converts the first character of the string to upper case. + + [% USE String("foo") %] + + [% String.capital %] # => 'Foo' + +The remainder of the string is left untouched. To force the string to +be all lower case with only the first letter capitalised, you can do +something like this: + + [% USE String("FOO") %] + + [% String.lower.capital %] # => 'Foo' + +=item chop() + +Removes the last character from the string. + + [% USE String("foop") %] + + [% String.chop %] # => 'foo' + +=item chomp() + +Removes the trailing newline from the string. + + [% USE String("foo\n") %] + + [% String.chomp %] # => 'foo' + +=item trim() + +Removes all leading and trailing whitespace from the string + + [% USE String(" foo \n\n ") %] + + [% String.trim %] # => 'foo' + +=item collapse() + +Removes all leading and trailing whitespace and collapses any sequences +of multiple whitespace to a single space. + + [% USE String(" \n\r \t foo \n \n bar \n") %] + + [% String.collapse %] # => "foo bar" + +=item truncate($length, $suffix) + +Truncates the string to $length characters. + + [% USE String('long string') %] + [% String.truncate(4) %] # => 'long' + +If $suffix is specified then it will be appended to the truncated +string. In this case, the string will be further shortened by the +length of the suffix to ensure that the newly constructed string +complete with suffix is exactly $length characters long. + + [% USE msg = String('Hello World') %] + [% msg.truncate(8, '...') %] # => 'Hello...' + +=item replace($search, $replace) + +Replaces all occurences of $search in the string with $replace. + + [% USE String('foo bar foo baz') %] + [% String.replace('foo', 'wiz') %] # => 'wiz bar wiz baz' + +=item remove($search) + +Remove all occurences of $search in the string. + + [% USE String('foo bar foo baz') %] + [% String.remove('foo ') %] # => 'bar baz' + +=item repeat($count) + +Repeats the string $count times. + + [% USE String('foo ') %] + [% String.repeat(3) %] # => 'foo foo foo ' + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.33, 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: 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: diff --git a/lib/Template/Plugin/URL.pm b/lib/Template/Plugin/URL.pm new file mode 100644 index 0000000..c2246b7 --- /dev/null +++ b/lib/Template/Plugin/URL.pm @@ -0,0 +1,236 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::URL +# +# DESCRIPTION +# +# Template Toolkit Plugin for constructing URL's from a base stem +# and adaptable parameters. +# +# 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: URL.pm,v 2.64 2004/01/13 16:20:39 abw Exp $ +# +#============================================================================ + +package Template::Plugin::URL; + +require 5.004; + +use strict; +use vars qw( @ISA $VERSION ); +use Template::Plugin; + +@ISA = qw( Template::Plugin ); +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); + + +#------------------------------------------------------------------------ +# new($context, $baseurl, \%url_params) +# +# Constructor method which returns a sub-routine closure for constructing +# complex URL's from a base part and hash of additional parameters. +#------------------------------------------------------------------------ + +sub new { + my ($class, $context, $base, $args) = @_; + $args ||= { }; + + return sub { + my $newbase = shift unless ref $_[0] eq 'HASH'; + my $newargs = shift || { }; + my $combo = { %$args, %$newargs }; + my $urlargs = join('&', +# map { "$_=" . escape($combo->{ $_ }) } + map { args($_, $combo->{ $_ }) } + grep { defined $combo->{ $_ } } + sort keys %$combo); + + my $query = $newbase || $base || ''; + $query .= '?' if length $query && length $urlargs; + $query .= $urlargs if length $urlargs; + + return $query + } +} + + +sub args { + my ($key, $val) = @_; + $key = escape($key); + return map { + "$key=" . escape($_); + } ref $val eq 'ARRAY' ? @$val : $val; + +} + +#------------------------------------------------------------------------ +# escape($url) +# +# URL-encode data. Borrowed with minor modifications from CGI.pm. +# Kudos to Lincold Stein. +#------------------------------------------------------------------------ + +sub escape { + my $toencode = shift; + return undef unless defined($toencode); + $toencode=~s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg; + return $toencode; +} + +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::URL - Plugin to construct complex URLs + +=head1 SYNOPSIS + + [% USE url('/cgi-bin/foo.pl') %] + + [% url(debug = 1, id = 123) %] + # ==> /cgi/bin/foo.pl?debug=1&id=123 + + + [% USE mycgi = url('/cgi-bin/bar.pl', mode='browse', debug=1) %] + + [% mycgi %] + # ==> /cgi/bin/bar.pl?mode=browse&debug=1 + + [% mycgi(mode='submit') %] + # ==> /cgi/bin/bar.pl?mode=submit&debug=1 + + [% mycgi(debug='d2 p0', id='D4-2k[4]') %] + # ==> /cgi-bin/bar.pl?mode=browse&debug=d2%20p0&id=D4-2k%5B4%5D + + +=head1 DESCRIPTION + +The URL plugin can be used to construct complex URLs from a base stem +and a hash array of additional query parameters. + +The constructor should be passed a base URL and optionally, a hash array +reference of default parameters and values. Used from with a Template +Documents, this would look something like the following: + + [% USE url('http://www.somewhere.com/cgi-bin/foo.pl') %] + [% USE url('/cgi-bin/bar.pl', mode='browse') %] + [% USE url('/cgi-bin/baz.pl', mode='browse', debug=1) %] + +When the plugin is then called without any arguments, the default base +and parameters are returned as a formatted query string. + + [% url %] + +For the above three examples, these will produce the following outputs: + + http://www.somewhere.com/cgi-bin/foo.pl + /cgi-bin/bar.pl?mode=browse + /cgi-bin/baz.pl?mode=browse&debug=1 + +Additional parameters may be also be specified: + + [% url(mode='submit', id='wiz') %] + +Which, for the same three examples, produces: + + http://www.somewhere.com/cgi-bin/foo.pl?mode=submit&id=wiz + /cgi-bin/bar.pl?mode=browse&id=wiz + /cgi-bin/baz.pl?mode=browse&debug=1&id=wiz + +A new base URL may also be specified as the first option: + + [% url('/cgi-bin/waz.pl', test=1) %] + +producing + + /cgi-bin/waz.pl?test=1 + /cgi-bin/waz.pl?mode=browse&test=1 + /cgi-bin/waz.pl?mode=browse&debug=1&test=1 + + +The ordering of the parameters is non-deterministic due to fact that +Perl's hashes themselves are unordered. This isn't a problem as the +ordering of CGI parameters is insignificant (to the best of my knowledge). +All values will be properly escaped thanks to some code borrowed from +Lincoln Stein's CGI.pm. e.g. + + [% USE url('/cgi-bin/woz.pl') %] + [% url(name="Elrich von Benjy d'Weiro") %] + +Here the spaces and "'" character are escaped in the output: + + /cgi-bin/woz.pl?name=Elrich%20von%20Benjy%20d%27Weiro + +Alternate name may be provided for the plugin at construction time +as per regular Template Toolkit syntax. + + [% USE mycgi = url('cgi-bin/min.pl') %] + + [% mycgi(debug=1) %] + +Note that in the following line, additional parameters are seperated +by '&', while common usage on the Web is to just use '&'. '&' +is actually the Right Way to do it. See this URL for more information: +http://ppewww.ph.gla.ac.uk/~flavell/www/formgetbyurl.html + + /cgi-bin/waz.pl?mode=browse&debug=1&test=1 + +=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: diff --git a/lib/Template/Plugin/View.pm b/lib/Template/Plugin/View.pm new file mode 100644 index 0000000..c22ba16 --- /dev/null +++ b/lib/Template/Plugin/View.pm @@ -0,0 +1,127 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::View +# +# DESCRIPTION +# A user-definable view based on templates. Similar to the concept of +# a "Skin". +# +# 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. +# +# REVISION +# $Id: View.pm,v 2.63 2004/01/13 16:20:39 abw Exp $ +# +#============================================================================ + +package Template::Plugin::View; + +require 5.004; + +use strict; +use Template::Plugin; +use vars qw( $VERSION ); +use base qw( Template::Plugin ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.63 $ =~ /(\d+)\.(\d+)/); + +use Template::View; + +#------------------------------------------------------------------------ +# new($context, \%config) +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $context = shift; + my $view = Template::View->new($context, @_) + || return $class->error($Template::View::ERROR); + $view->seal(); + return $view; +} + + + +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::View - Plugin to create views (Template::View) + +=head1 SYNOPSIS + + [% USE view( + prefix = 'splash/' # template prefix/suffix + suffix = '.tt2' + bgcol = '#ffffff' # and any other variables you + style = 'Fancy HTML' # care to define as view metadata, + items = [ foo, bar.baz ] # including complex data and + foo = bar ? baz : x.y.z # expressions + %] + + [% view.title %] # access view metadata + + [% view.header(title = 'Foo!') %] # view "methods" process blocks or + [% view.footer %] # templates with prefix/suffix added + +=head1 DESCRIPTION + +This plugin module creates Template::View objects. Views are an +experimental feature and are subject to change in the near future. +In the mean time, please consult L<Template::View> for further info. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.63, 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>, L<Template::View|Template::View> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Plugin/Wrap.pm b/lib/Template/Plugin/Wrap.pm new file mode 100644 index 0000000..96c600a --- /dev/null +++ b/lib/Template/Plugin/Wrap.pm @@ -0,0 +1,162 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::Wrap +# +# DESCRIPTION +# Plugin for wrapping text via the Text::Wrap module. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-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: Wrap.pm,v 2.63 2004/01/13 16:20:40 abw Exp $ +# +#============================================================================ + +package Template::Plugin::Wrap; + +require 5.004; + +use strict; +use vars qw( @ISA $VERSION ); +use base qw( Template::Plugin ); +use Template::Plugin; +use Text::Wrap; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.63 $ =~ /(\d+)\.(\d+)/); + +sub new { + my ($class, $context, $format) = @_;; + $context->define_filter('wrap', [ \&wrap_filter_factory => 1 ]); + return \&tt_wrap; +} + +sub tt_wrap { + my $text = shift; + my $width = shift || 72; + my $itab = shift; + my $ntab = shift; + $itab = '' unless defined $itab; + $ntab = '' unless defined $ntab; + $Text::Wrap::columns = $width; + Text::Wrap::wrap($itab, $ntab, $text); +} + +sub wrap_filter_factory { + my ($context, @args) = @_; + return sub { + my $text = shift; + tt_wrap($text, @args); + } +} + + +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::Wrap - Plugin interface to Text::Wrap + +=head1 SYNOPSIS + + [% USE wrap %] + + # call wrap subroutine + [% wrap(mytext, width, initial_tab, subsequent_tab) %] + + # or use wrap FILTER + [% mytext FILTER wrap(width, initital_tab, subsequent_tab) %] + +=head1 DESCRIPTION + +This plugin provides an interface to the Text::Wrap module which +provides simple paragraph formatting. + +It defines a 'wrap' subroutine which can be called, passing the input +text and further optional parameters to specify the page width (default: +72), and tab characters for the first and subsequent lines (no defaults). + + [% USE wrap %] + + [% text = BLOCK %] + First, attach the transmutex multiplier to the cross-wired + quantum homogeniser. + [% END %] + + [% wrap(text, 40, '* ', ' ') %] + +Output: + + * First, attach the transmutex + multiplier to the cross-wired quantum + homogeniser. + +It also registers a 'wrap' filter which accepts the same three optional +arguments but takes the input text directly via the filter input. + + [% FILTER bullet = wrap(40, '* ', ' ') -%] + First, attach the transmutex multiplier to the cross-wired quantum + homogeniser. + [%- END %] + + [% FILTER bullet -%] + Then remodulate the shield to match the harmonic frequency, taking + care to correct the phase difference. + [% END %] + +Output: + + * First, attach the transmutex + multiplier to the cross-wired quantum + homogeniser. + + * Then remodulate the shield to match + the harmonic frequency, taking + care to correct the phase difference. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@wardley.orgE<gt> + +The Text::Wrap module was written by David Muir Sharnoff +E<lt>muir@idiom.comE<gt> with help from Tim Pierce and many +others. + +=head1 VERSION + +2.63, 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>, L<Text::Wrap|Text::Wrap> + diff --git a/lib/Template/Plugin/XML/DOM.pm b/lib/Template/Plugin/XML/DOM.pm new file mode 100644 index 0000000..30bac3b --- /dev/null +++ b/lib/Template/Plugin/XML/DOM.pm @@ -0,0 +1,841 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::XML::DOM +# +# DESCRIPTION +# +# Simple Template Toolkit plugin interfacing to the XML::DOM.pm module. +# +# AUTHORS +# Andy Wardley <abw@kfs.org> +# Simon Matthews <sam@knowledgepool.com> +# +# COPYRIGHT +# Copyright (C) 2000 Andy Wardley, Simon Matthews. All Rights Reserved. +# +# This module is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. +# +#---------------------------------------------------------------------------- +# +# $Id: DOM.pm,v 2.54 2004/01/13 16:21:50 abw Exp $ +# +#============================================================================ + +package Template::Plugin::XML::DOM; + +require 5.004; + +use strict; +use Template::Plugin; +use XML::DOM; + +use base qw( Template::Plugin ); +use vars qw( $VERSION $DEBUG ); + +$VERSION = 2.6; +$DEBUG = 0 unless defined $DEBUG; + + +#------------------------------------------------------------------------ +# new($context, \%config) +# +# Constructor method for XML::DOM plugin. Creates an XML::DOM::Parser +# object and initialise plugin configuration. +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $context = shift; + my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; + + my $parser ||= XML::DOM::Parser->new(%$args) + || return $class->_throw("failed to create XML::DOM::Parser\n"); + + # we've had to deprecate the old usage because it broke things big time + # with DOM trees never getting cleaned up. + return $class->_throw("XML::DOM usage has changed - you must now call parse()\n") + if @_; + + bless { + _PARSER => $parser, + _DOCS => [ ], + _CONTEXT => $context, + _PREFIX => $args->{ prefix } || '', + _SUFFIX => $args->{ suffix } || '', + _DEFAULT => $args->{ default } || '', + _VERBOSE => $args->{ verbose } || 0, + _NOSPACE => $args->{ nospace } || 0, + _DEEP => $args->{ deep } || 0, + }, $class; +} + + +#------------------------------------------------------------------------ +# parse($content, \%named_params) +# +# Parses an XML stream, provided as the first positional argument (assumed +# to be a filename unless it contains a '<' character) or specified in +# the named parameter hash as one of 'text', 'xml' (same as text), 'file' +# or 'filename'. +#------------------------------------------------------------------------ + +sub parse { + my $self = shift; + my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; + my $parser = $self->{ _PARSER }; + my ($content, $about, $method, $doc); + + # determine the input source from a positional parameter (may be a + # filename or XML text if it contains a '<' character) or by using + # named parameters which may specify one of 'file', 'filename', 'text' + # or 'xml' + + if ($content = shift) { + if ($content =~ /\</) { + $about = 'xml text'; + $method = 'parse'; + } + else { + $about = "xml file $content"; + $method = 'parsefile'; + } + } + elsif ($content = $args->{ text } || $args->{ xml }) { + $about = 'xml text'; + $method = 'parse'; + } + elsif ($content = $args->{ file } || $args->{ filename }) { + $about = "xml file $content"; + $method = 'parsefile'; + } + else { + return $self->_throw('no filename or xml text specified'); + } + + # parse the input source using the appropriate method determined above + eval { $doc = $parser->$method($content) } and not $@ + or return $self->_throw("failed to parse $about: $@"); + + # update XML::DOM::Document _UserData to contain config details + $doc->[ XML::DOM::Node::_UserData ] = { + map { ( $_ => $self->{ $_ } ) } + qw( _CONTEXT _PREFIX _SUFFIX _VERBOSE _NOSPACE _DEEP _DEFAULT ), + }; + + # keep track of all DOM docs for subsequent dispose() +# print STDERR "DEBUG: $self adding doc: $doc\n" +# if $DEBUG; + + push(@{ $self->{ _DOCS } }, $doc); + + return $doc; +} + + +#------------------------------------------------------------------------ +# _throw($errmsg) +# +# Raised a Template::Exception of type XML.DOM via die(). +#------------------------------------------------------------------------ + +sub _throw { + my ($self, $error) = @_; + die (Template::Exception->new('XML.DOM', $error)); +} + + +#------------------------------------------------------------------------ +# DESTROY +# +# Cleanup method which calls dispose() on any and all DOM documents +# created by this object. Also breaks any circular references that +# may exist with the context object. +#------------------------------------------------------------------------ + +sub DESTROY { + my $self = shift; + + # call dispose() on each document produced by this parser + foreach my $doc (@{ $self->{ _DOCS } }) { +# print STDERR "DEBUG: $self destroying $doc\n" +# if $DEBUG; + if (ref $doc) { +# print STDERR "disposing of $doc\n"; + undef $doc->[ XML::DOM::Node::_UserData ]->{ _CONTEXT }; + $doc->dispose(); + } + } + delete $self->{ _CONTEXT }; + delete $self->{ _PARSER }; +} + + + +#======================================================================== +package XML::DOM::Node; +#======================================================================== + + +#------------------------------------------------------------------------ +# present($view) +# +# Method to present node via a view (supercedes all that messy toTemplate +# stuff below). +#------------------------------------------------------------------------ + +sub present { + my ($self, $view) = @_; + + if ($self->getNodeType() == XML::DOM::ELEMENT_NODE) { + # it's an element + $view->view($self->getTagName(), $self); + } + else { + my $text = $self->toString(); + $view->view('text', $text); + } +} + +sub content { + my ($self, $view) = @_; + my $output = ''; + foreach my $node (@{ $self->getChildNodes }) { + $output .= $node->present($view); + +# abw test passing args, Aug 2001 +# $output .= $view->print($node); + } + return $output; +} + + +#------------------------------------------------------------------------ +# toTemplate($prefix, $suffix, \%named_params) +# +# Process the current node as a template. +#------------------------------------------------------------------------ + +sub toTemplate { + my $self = shift; + _template_node($self, $self->_args(@_)); +} + + +#------------------------------------------------------------------------ +# childrenToTemplate($prefix, $suffix, \%named_params) +# +# Process all the current node's children as templates. +#------------------------------------------------------------------------ + +sub childrenToTemplate { + my $self = shift; + _template_kids($self, $self->_args(@_)); +} + + +#------------------------------------------------------------------------ +# allChildrenToTemplate($prefix, $suffix, \%named_params) +# +# Process all the current node's children, and their children, and +# their children, etc., etc., as templates. Same effect as calling the +# childrenToTemplate() method with the 'deep' option set. +#------------------------------------------------------------------------ + +sub allChildrenToTemplate { + my $self = shift; + my $args = $self->_args(@_); + $args->{ deep } = 1; + _template_kids($self, $args); +} + + +#------------------------------------------------------------------------ +# _args($prefix, $suffix, \%name_params) +# +# Reads the optional positional parameters, $prefix and $suffix, and +# also examines any named parameters hash to construct a set of +# current configuration parameters. Where not specified directly, the +# object defaults are used. +#------------------------------------------------------------------------ + +sub _args { + my $self = shift; + my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; + my $doc = $self->getOwnerDocument() || $self; + my $data = $doc->[ XML::DOM::Node::_UserData ]; + + return { + prefix => @_ ? shift : $args->{ prefix } || $data->{ _PREFIX }, + suffix => @_ ? shift : $args->{ suffix } || $data->{ _SUFFIX }, + verbose => $args->{ verbose } || $data->{ _VERBOSE }, + nospace => $args->{ nospace } || $data->{ _NOSPACE }, + deep => $args->{ deep } || $data->{ _DEEP }, + default => $args->{ default } || $data->{ _DEFAULT }, + context => $data->{ _CONTEXT }, + }; +} + + + +#------------------------------------------------------------------------ +# _template_node($node, $args, $vars) +# +# Process a template for the current DOM node where the template name +# is taken from the node TagName, with any specified 'prefix' and/or +# 'suffix' applied. The 'default' argument can also be provided to +# specify a default template to be used when a specific template can't +# be found. The $args parameter referenced a hash array through which +# these configuration items are passed (see _args()). The current DOM +# node is made available to the template as the variable 'node', along +# with any other variables passed in the optional $vars hash reference. +# To permit the 'children' and 'prune' callbacks to be raised as node +# methods (see _template_kids() below), these items, if defined in the +# $vars hash, are copied into the node object where its AUTOLOAD method +# can find them. +#------------------------------------------------------------------------ + +sub _template_node { + my $node = shift || die "no XML::DOM::Node reference\n"; + my $args = shift || die "no XML::DOM args passed to _template_node\n"; + my $vars = shift || { }; + my $context = $args->{ context } || die "no context in XML::DOM args\n"; + my $template; + my $output = ''; + + # if this is not an element then it is text so output it + unless ($node->getNodeType() == XML::DOM::ELEMENT_NODE ) { + if ($args->{ verbose }) { + $output = $node->toString(); + $output =~ s/\s+$// if $args->{ nospace }; + } + } + else { + my $element = ( $args->{ prefix } || '' ) + . $node->getTagName() + . ( $args->{ suffix } || '' ); + + # locate a template by name built from prefix, tagname and suffix + # or fall back on any default template specified + eval { $template = $context->template($element) }; + eval { $template = $context->template($args->{ default }) } + if $@ && $args->{ default }; + $template = $element unless $template; + + # copy 'children' and 'prune' callbacks into node object (see AUTOLOAD) + my $doc = $node->getOwnerDocument() || $node; + my $data = $doc->[ XML::DOM::Node::_UserData ]; + + $data->{ _TT_CHILDREN } = $vars->{ children }; + $data->{ _TT_PRUNE } = $vars->{ prune }; + + # add node reference to existing vars hash + $vars->{ node } = $node; + + $output = $context->include($template, $vars); + + # break any circular references + delete $vars->{ node }; + delete $data->{ _TT_CHILDREN }; + delete $data->{ _TT_PRUNE }; + } + + return $output; +} + + +#------------------------------------------------------------------------ +# _template_kids($node, $args) +# +# Process all the children of the current node as templates, via calls +# to _template_node(). If the 'deep' argument is set, then the process +# will continue recursively. In this case, the node template is first +# processed, followed by any children of that node (i.e. depth first, +# parent before). A closure called 'children' is created and added +# to the Stash variables passed to _template_node(). This can be called +# from the parent template to process all child nodes at the current point. +# This then "prunes" the tree preventing the children from being processed +# after the parent template. A 'prune' callback is also added to prune +# the tree without processing the children. Note that _template_node() +# copies these callbacks into each parent node, allowing them to be called +# as [% node. +#------------------------------------------------------------------------ + +sub _template_kids { + my $node = shift || die "no XML::DOM::Node reference\n"; + my $args = shift || die "no XML::DOM args passed to _template_kids\n"; + my $context = $args->{ context } || die "no context in XML::DOM args\n"; + my $output = ''; + + foreach my $kid ( $node->getChildNodes() ) { + # define some callbacks to allow template to call [% content %] + # or [% prune %]. They are also inserted into each node reference + # so they can be called as [% node.content %] and [% node.prune %] + my $prune = 0; + my $vars = { }; + $vars->{ children } = sub { + $prune = 1; + _template_kids($kid, $args); + }; + $vars->{ prune } = sub { + $prune = 1; + return ''; + }; + + $output .= _template_node($kid, $args, $vars); + $output .= _template_kids($kid, $args) + if $args->{ deep } && ! $prune; + } + return $output; +} + + +#======================================================================== +package XML::DOM::Element; +#======================================================================== + +use vars qw( $AUTOLOAD ); + +sub AUTOLOAD { + my $self = shift; + my $method = $AUTOLOAD; + my $attrib; + + $method =~ s/.*:://; + return if $method eq 'DESTROY'; + + my $doc = $self->getOwnerDocument() || $self; + my $data = $doc->[ XML::DOM::Node::_UserData ]; + + # call 'content' or 'prune' callbacks, if defined (see _template_node()) + return &$attrib() + if ($method =~ /^children|prune$/) + && defined($attrib = $data->{ "_TT_\U$method" }) + && ref $attrib eq 'CODE'; + + return $attrib + if defined ($attrib = $self->getAttribute($method)); + + return ''; +} + + +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::XML::DOM - Plugin interface to XML::DOM + +=head1 SYNOPSIS + + # load plugin + [% USE dom = XML.DOM %] + + # also provide XML::Parser options + [% USE dom = XML.DOM(ProtocolEncoding =E<gt> 'ISO-8859-1') %] + + # parse an XML file + [% doc = dom.parse(filename) %] + [% doc = dom.parse(file => filename) %] + + # parse XML text + [% doc = dom.parse(xmltext) %] + [% doc = dom.parse(text => xmltext) %] + + # call any XML::DOM methods on document/element nodes + [% FOREACH node = doc.getElementsByTagName('report') %] + * [% node.getAttribute('title') %] # or just '[% node.title %]' + [% END %] + + # define VIEW to present node(s) + [% VIEW report notfound='xmlstring' %] + # handler block for a <report>...</report> element + [% BLOCK report %] + [% item.content(view) %] + [% END %] + + # handler block for a <section title="...">...</section> element + [% BLOCK section %] + <h1>[% item.title %]</h1> + [% item.content(view) %] + [% END %] + + # default template block converts item to string representation + [% BLOCK xmlstring; item.toString; END %] + + # block to generate simple text + [% BLOCK text; item; END %] + [% END %] + + # now present node (and children) via view + [% report.print(node) %] + + # or print node content via view + [% node.content(report) %] + + # following methods are soon to be deprecated in favour of views + [% node.toTemplate %] + [% node.childrenToTemplate %] + [% node.allChildrenToTemplate %] + +=head1 PRE-REQUISITES + +This plugin requires that the XML::Parser (2.19 or later) and XML::DOM +(1.27 or later) modules be installed. These are available from CPAN: + + http://www.cpan.org/modules/by-module/XML + +Note that the XML::DOM module is now distributed as part of the +'libxml-enno' bundle. + +=head1 DESCRIPTION + +This is a Template Toolkit plugin interfacing to the XML::DOM module. +The plugin loads the XML::DOM module and creates an XML::DOM::Parser +object which is stored internally. The parse() method can then be +called on the plugin to parse an XML stream into a DOM document. + + [% USE dom = XML.DOM %] + [% doc = dom.parse('/tmp/myxmlfile') %] + +NOTE: earlier versions of this XML::DOM plugin expected a filename to +be passed as an argument to the constructor. This is no longer +supported due to the fact that it caused a serious memory leak. We +apologise for the inconvenience but must insist that you change your +templates as shown: + + # OLD STYLE: now fails with a warning + [% USE dom = XML.DOM('tmp/myxmlfile') %] + + # NEW STYLE: do this instead + [% USE dom = XML.DOM %] + [% doc = dom.parse('tmp/myxmlfile') %] + +The root of the problem lies in XML::DOM creating massive circular +references in the object models it constructs. The dispose() method +must be called on each document to release the memory that it would +otherwise hold indefinately. The XML::DOM plugin object (i.e. 'dom' +in these examples) acts as a sentinel for the documents it creates +('doc' and any others). When the plugin object goes out of scope at +the end of the current template, it will automatically call dispose() +on any documents that it has created. Note that if you dispose of the +the plugin object before the end of the block (i.e. by assigning a +new value to the 'dom' variable) then the documents will also be +disposed at that point and should not be used thereafter. + + [% USE dom = XML.DOM %] + [% doc = dom.parse('/tmp/myfile') %] + [% dom = 'new value' %] # releases XML.DOM plugin and calls + # dispose() on 'doc', so don't use it! + +Any template processing parameters (see toTemplate() method and +friends, below) can be specified with the constructor and will be used +to define defaults for the object. + + [% USE dom = XML.DOM(prefix => 'theme1/') %] + +The plugin constructor will also accept configuration options destined +for the XML::Parser object: + + [% USE dom = XML.DOM(ProtocolEncoding => 'ISO-8859-1') %] + +=head1 METHODS + +=head2 parse() + +The parse() method accepts a positional parameter which contains a filename +or XML string. It is assumed to be a filename unless it contains a E<lt> +character. + + [% xmlfile = '/tmp/foo.xml' %] + [% doc = dom.parse(xmlfile) %] + + [% xmltext = BLOCK %] + <xml> + <blah><etc/></blah> + ... + </xml> + [% END %] + [% doc = dom.parse(xmltext) %] + +The named parameters 'file' (or 'filename') and 'text' (or 'xml') can also +be used: + + [% doc = dom.parse(file = xmlfile) %] + [% doc = dom.parse(text = xmltext) %] + +The parse() method returns an instance of the XML::DOM::Document object +representing the parsed document in DOM form. You can then call any +XML::DOM methods on the document node and other nodes that its methods +may return. See L<XML::DOM> for full details. + + [% FOREACH node = doc.getElementsByTagName('CODEBASE') %] + * [% node.getAttribute('href') %] + [% END %] + +This plugin also provides an AUTOLOAD method for XML::DOM::Node which +calls getAttribute() for any undefined methods. Thus, you can use the +short form of + + [% node.attrib %] + +in place of + + [% node.getAttribute('attrib') %] + +=head2 toTemplate() + +B<NOTE: This method will soon be deprecated in favour of the VIEW based +approach desribed below.> + +This method will process a template for the current node on which it is +called. The template name is constructed from the node TagName with any +optional 'prefix' and/or 'suffix' options applied. A 'default' template +can be named to be used when the specific template cannot be found. The +node object is available to the template as the 'node' variable. + +Thus, for this XML fragment: + + <page title="Hello World!"> + ... + </page> + +and this template definition: + + [% BLOCK page %] + Page: [% node.title %] + [% END %] + +the output of calling toTemplate() on the E<lt>pageE<gt> node would be: + + Page: Hello World! + +=head2 childrenToTemplate() + +B<NOTE: This method will soon be deprecated in favour of the VIEW based +approach desribed below.> + +Effectively calls toTemplate() for the current node and then for each of +the node's children. By default, the parent template is processed first, +followed by each of the children. The 'children' closure can be called +from within the parent template to have them processed and output +at that point. This then suppresses the children from being processed +after the parent template. + +Thus, for this XML fragment: + + <foo> + <bar id="1"/> + <bar id="2"/> + </foo> + +and these template definitions: + + [% BLOCK foo %] + start of foo + end of foo + [% END %] + + [% BLOCK bar %] + bar [% node.id %] + [% END %] + +the output of calling childrenToTemplate() on the parent E<lt>fooE<gt> node +would be: + + start of foo + end of foo + bar 1 + bar 2 + +Adding a call to [% children %] in the 'foo' template: + + [% BLOCK foo %] + start of foo + [% children %] + end of foo + [% END %] + +then creates output as: + + start of foo + bar 1 + bar 2 + end of foo + +The 'children' closure can also be called as a method of the node, if you +prefer: + + [% BLOCK foo %] + start of foo + [% node.children %] + end of foo + [% END %] + +The 'prune' closure is also defined and can be called as [% prune %] or +[% node.prune %]. It prunes the currrent node, preventing any descendants +from being further processed. + + [% BLOCK anynode %] + [% node.toString; node.prune %] + [% END %] + +=head2 allChildrenToTemplate() + +B<NOTE: This method will soon be deprecated in favour of the VIEW based +approach desribed below.> + +Similar to childrenToTemplate() but processing all descendants (i.e. children +of children and so on) recursively. This is identical to calling the +childrenToTemplate() method with the 'deep' flag set to any true value. + +=head1 PRESENTING DOM NODES USING VIEWS + +You can define a VIEW to present all or part of a DOM tree by automatically +mapping elements onto templates. Consider a source document like the +following: + + <report> + <section title="Introduction"> + <p> + Blah blah. + <ul> + <li>Item 1</li> + <li>item 2</li> + </ul> + </p> + </section> + <section title="The Gory Details"> + ... + </section> + </report> + +We can load it up via the XML::DOM plugin and fetch the node for the +E<lt>reportE<gt> element. + + [% USE dom = XML.DOM; + doc = dom.parse(file => filename); + report = doc.getElementsByTagName('report') + %] + +We can then define a VIEW as follows to present this document fragment in +a particular way. The L<Template::Manual::Views> documentation +contains further details on the VIEW directive and various configuration +options it supports. + + [% VIEW report_view notfound='xmlstring' %] + # handler block for a <report>...</report> element + [% BLOCK report %] + [% item.content(view) %] + [% END %] + + # handler block for a <section title="...">...</section> element + [% BLOCK section %] + <h1>[% item.title %]</h1> + [% item.content(view) %] + [% END %] + + # default template block converts item to string representation + [% BLOCK xmlstring; item.toString; END %] + + # block to generate simple text + [% BLOCK text; item; END %] + [% END %] + +Each BLOCK defined within the VIEW represents a presentation style for +a particular element or elements. The current node is available via the +'item' variable. Elements that contain other content can generate it +according to the current view by calling [% item.content(view) %]. +Elements that don't have a specific template defined are mapped to the +'xmlstring' template via the 'notfound' parameter specified in the VIEW +header. This replicates the node as an XML string, effectively allowing +general XML/XHTML markup to be passed through unmodified. + +To present the report node via the view, we simply call: + + [% report_view.print(report) %] + +The output from the above example would look something like this: + + <h1>Introduction</h1> + <p> + Blah blah. + <ul> + <li>Item 1</li> + <li>item 2</li> + </ul> + </p> + + <h1>The Gory Details</h1> + ... + +To print just the content of the report node (i.e. don't process the +'report' template for the report node), you can call: + + [% report.content(report_view) %] + +=head1 AUTHORS + +This plugin module was written by Andy Wardley E<lt>abw@wardley.orgE<gt> +and Simon Matthews E<lt>sam@knowledgepool.comE<gt>. + +The XML::DOM module is by Enno Derksen E<lt>enno@att.comE<gt> and Clark +Cooper E<lt>coopercl@sch.ge.comE<gt>. It extends the the XML::Parser +module, also by Clark Cooper which itself is built on James Clark's expat +library. + +=head1 VERSION + +2.6, distributed as part of the +Template Toolkit version 2.13, released on 30 January 2004. + + + +=head1 HISTORY + +Version 2.5 : updated for use with version 1.27 of the XML::DOM module. + +=over 4 + +=item * + +XML::DOM 1.27 now uses array references as the underlying data type +for DOM nodes instead of hash array references. User data is now +bound to the _UserData node entry instead of being forced directly +into the node hash. + +=back + +=head1 BUGS + +The childrenToTemplate() and allChildrenToTemplate() methods can easily +slip into deep recursion. + +The 'verbose' and 'nospace' options are not documented. They may +change in the near future. + +=head1 COPYRIGHT + +Copyright (C) 2000-2001 Andy Wardley, Simon Matthews. 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::Plugin|Template::Plugin>, L<XML::DOM|XML::DOM>, L<XML::Parser|XML::Parser> + diff --git a/lib/Template/Plugin/XML/RSS.pm b/lib/Template/Plugin/XML/RSS.pm new file mode 100644 index 0000000..32da7d8 --- /dev/null +++ b/lib/Template/Plugin/XML/RSS.pm @@ -0,0 +1,194 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::XML::RSS +# +# DESCRIPTION +# +# Template Toolkit plugin which interfaces to Jonathan Eisenzopf's XML::RSS +# module. RSS is the Rich Site Summary format. +# +# 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: RSS.pm,v 2.64 2004/01/13 16:21:50 abw Exp $ +# +#============================================================================ + +package Template::Plugin::XML::RSS; + +require 5.004; + +use strict; +use vars qw( $VERSION ); +use base qw( Template::Plugin ); +use Template::Plugin; +use XML::RSS; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/); + +sub load { + return $_[0]; +} + +sub new { + my ($class, $context, $filename) = @_; + + return $class->fail('No filename specified') + unless $filename; + + my $rss = XML::RSS->new + or return $class->fail('failed to create XML::RSS'); + + # Attempt to determine if $filename is an XML string or + # a filename. Based on code from the XML.XPath plugin. + eval { + if ($filename =~ /\</) { + $rss->parse($filename); + } + else { + $rss->parsefile($filename) + } + } and not $@ + or return $class->fail("failed to parse $filename: $@"); + + return $rss; +} + +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::XML::RSS - Plugin interface to XML::RSS + +=head1 SYNOPSIS + + [% USE news = XML.RSS($filename) %] + + [% FOREACH item = news.items %] + [% item.title %] + [% item.link %] + [% END %] + +=head1 PRE-REQUISITES + +This plugin requires that the XML::Parser and XML::RSS modules be +installed. These are available from CPAN: + + http://www.cpan.org/modules/by-module/XML + +=head1 DESCRIPTION + +This Template Toolkit plugin provides a simple interface to the +XML::RSS module. + + [% USE news = XML.RSS('mysite.rdf') %] + +It creates an XML::RSS object, which is then used to parse the RSS +file specified as a parameter in the USE directive. A reference to +the XML::RSS object is then returned. + +An RSS (Rich Site Summary) file is typically used to store short news +'headlines' describing different links within a site. This example is +extracted from http://slashdot.org/slashdot.rdf. + + <?xml version="1.0"?><rdf:RDF + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://my.netscape.com/rdf/simple/0.9/"> + + <channel> + <title>Slashdot:News for Nerds. Stuff that Matters.</title> + <link>http://slashdot.org</link> + <description>News for Nerds. Stuff that Matters</description> + </channel> + + <image> + <title>Slashdot</title> + <url>http://slashdot.org/images/slashdotlg.gif</url> + <link>http://slashdot.org</link> + </image> + + <item> + <title>DVD CCA Battle Continues Next Week</title> + <link>http://slashdot.org/article.pl?sid=00/01/12/2051208</link> + </item> + + <item> + <title>Matrox to fund DRI Development</title> + <link>http://slashdot.org/article.pl?sid=00/01/13/0718219</link> + </item> + + <item> + <title>Mike Shaver Leaving Netscape</title> + <link>http://slashdot.org/article.pl?sid=00/01/13/0711258</link> + </item> + + </rdf:RDF> + +The attributes of the channel and image elements can be retrieved directly +from the plugin object using the familiar dotted compound notation: + + [% news.channel.title %] + [% news.channel.link %] + [% news.channel.etc... %] + + [% news.image.title %] + [% news.image.url %] + [% news.image.link %] + [% news.image.etc... %] + +The list of news items can be retrieved using the 'items' method: + + [% FOREACH item = news.items %] + [% item.title %] + [% item.link %] + [% END %] + +=head1 AUTHORS + +This plugin was written by Andy Wardley E<lt>abw@wardley.orgE<gt>, +inspired by an article in Web Techniques by Randal Schwartz +E<lt>merlyn@stonehenge.comE<gt>. + +The XML::RSS module, which implements all of the functionality that +this plugin delegates to, was written by Jonathan Eisenzopf +E<lt>eisen@pobox.comE<gt>. + +=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>, L<XML::RSS|XML::RSS>, L<XML::Parser|XML::Parser> + diff --git a/lib/Template/Plugin/XML/Simple.pm b/lib/Template/Plugin/XML/Simple.pm new file mode 100644 index 0000000..aaa4479 --- /dev/null +++ b/lib/Template/Plugin/XML/Simple.pm @@ -0,0 +1,124 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::XML::Simple +# +# DESCRIPTION +# Template Toolkit plugin interfacing to the XML::Simple.pm module. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 2001 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: Simple.pm,v 2.63 2004/01/13 16:21:50 abw Exp $ +# +#============================================================================ + +package Template::Plugin::XML::Simple; + +require 5.004; + +use strict; +use Template::Plugin; +use XML::Simple; + +use base qw( Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.63 $ =~ /(\d+)\.(\d+)/); + + +#------------------------------------------------------------------------ +# new($context, $file_or_text, \%config) +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $context = shift; + my $input = shift; + my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; + + XMLin($input, %$args); +} + + + +#------------------------------------------------------------------------ +# _throw($errmsg) +# +# Raise a Template::Exception of type XML.Simple via die(). +#------------------------------------------------------------------------ + +sub _throw { + my ($self, $error) = @_; + die (Template::Exception->new('XML.Simple', $error)); +} + + +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::XML::Simple - Plugin interface to XML::Simple + +=head1 SYNOPSIS + + # load plugin and specify XML file to parse + [% USE xml = XML.Simple(xml_file_or_text) %] + +=head1 DESCRIPTION + +This is a Template Toolkit plugin interfacing to the XML::Simple module. + +=head1 PRE-REQUISITES + +This plugin requires that the XML::Parser and XML::Simple modules be +installed. These are available from CPAN: + + http://www.cpan.org/modules/by-module/XML + +=head1 AUTHORS + +This plugin wrapper module was written by Andy Wardley +E<lt>abw@wardley.orgE<gt>. + +The XML::Simple module which implements all the core functionality +was written by Grant McLean E<lt>grantm@web.co.nzE<gt>. + +=head1 VERSION + +2.63, 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>, L<XML::Simple|XML::Simple>, L<XML::Parser|XML::Parser> + diff --git a/lib/Template/Plugin/XML/Style.pm b/lib/Template/Plugin/XML/Style.pm new file mode 100644 index 0000000..7613f2f --- /dev/null +++ b/lib/Template/Plugin/XML/Style.pm @@ -0,0 +1,357 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::XML::Style +# +# DESCRIPTION +# Template Toolkit plugin which performs some basic munging of XML +# to perform simple stylesheet like transformations. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 2001 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. +# +# REVISION +# $Id: Style.pm,v 2.34 2004/01/13 16:21:50 abw Exp $ +# +#============================================================================ + +package Template::Plugin::XML::Style; + +require 5.004; + +use strict; +use Template::Plugin::Filter; + +use base qw( Template::Plugin::Filter ); +use vars qw( $VERSION $DYNAMIC $FILTER_NAME ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.34 $ =~ /(\d+)\.(\d+)/); +$DYNAMIC = 1; +$FILTER_NAME = 'xmlstyle'; + + +#------------------------------------------------------------------------ +# new($context, \%config) +#------------------------------------------------------------------------ + +sub init { + my $self = shift; + my $name = $self->{ _ARGS }->[0] || $FILTER_NAME; + $self->install_filter($name); + return $self; +} + + +sub filter { + my ($self, $text, $args, $config) = @_; + + # munge start tags + $text =~ s/ < ([\w\.\:]+) ( \s+ [^>]+ )? > + / $self->start_tag($1, $2, $config) + /gsex; + + # munge end tags + $text =~ s/ < \/ ([\w\.\:]+) > + / $self->end_tag($1, $config) + /gsex; + + return $text; + +} + + +sub start_tag { + my ($self, $elem, $textattr, $config) = @_; + $textattr ||= ''; + my ($pre, $post); + + # look for an element match in the stylesheet + my $match = $config->{ $elem } + || $self->{ _CONFIG }->{ $elem } + || return "<$elem$textattr>"; + + # merge element attributes into copy of stylesheet attributes + my $attr = { %{ $match->{ attributes } || { } } }; + while ($textattr =~ / \s* ([\w\.\:]+) = " ([^"]+) " /gsx ) { + $attr->{ $1 } = $2; + } + $textattr = join(' ', map { "$_=\"$attr->{$_}\"" } keys %$attr); + $textattr = " $textattr" if $textattr; + + $elem = $match->{ element } || $elem; + $pre = $match->{ pre_start } || ''; + $post = $match->{ post_start } || ''; + + return "$pre<$elem$textattr>$post"; +} + + +sub end_tag { + my ($self, $elem, $config) = @_; + my ($pre, $post); + + # look for an element match in the stylesheet + my $match = $config->{ $elem } + || $self->{ _CONFIG }->{ $elem } + || return "</$elem>"; + + $elem = $match->{ element } || $elem; + $pre = $match->{ pre_end } || ''; + $post = $match->{ post_end } || ''; + + return "$pre</$elem>$post"; +} + + +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::XML::Style - Simple XML stylesheet transfomations + +=head1 SYNOPSIS + + [% USE xmlstyle + table = { + attributes = { + border = 0 + cellpadding = 4 + cellspacing = 1 + } + } + %] + + [% FILTER xmlstyle %] + <table> + <tr> + <td>Foo</td> <td>Bar</td> <td>Baz</td> + </tr> + </table> + [% END %] + +=head1 DESCRIPTION + +This plugin defines a filter for performing simple stylesheet based +transformations of XML text. + +Named parameters are used to define those XML elements which require +transformation. These may be specified with the USE directive when +the plugin is loaded and/or with the FILTER directive when the plugin +is used. + +This example shows how the default attributes C<border="0"> and +C<cellpadding="4"> can be added to E<lt>tableE<gt> elements. + + [% USE xmlstyle + table = { + attributes = { + border = 0 + cellpadding = 4 + } + } + %] + + [% FILTER xmlstyle %] + <table> + ... + </table> + [% END %] + +This produces the output: + + <table border="0" cellpadding="4"> + ... + </table> + +Parameters specified within the USE directive are applied automatically each +time the C<xmlstyle> FILTER is used. Additional parameters passed to the +FILTER directive apply for only that block. + + [% USE xmlstyle + table = { + attributes = { + border = 0 + cellpadding = 4 + } + } + %] + + [% FILTER xmlstyle + tr = { + attributes = { + valign="top" + } + } + %] + <table> + <tr> + ... + </tr> + </table> + [% END %] + +Of course, you may prefer to define your stylesheet structures once and +simply reference them by name. Passing a hash reference of named parameters +is just the same as specifying the named parameters as far as the Template +Toolkit is concerned. + + [% style_one = { + table = { ... } + tr = { ... } + } + style_two = { + table = { ... } + td = { ... } + } + style_three = { + th = { ... } + tv = { ... } + } + %] + + [% USE xmlstyle style_one %] + + [% FILTER xmlstyle style_two %] + # style_one and style_two applied here + [% END %] + + [% FILTER xmlstyle style_three %] + # style_one and style_three applied here + [% END %] + +Any attributes defined within the source tags will override those specified +in the style sheet. + + [% USE xmlstyle + div = { attributes = { align = 'left' } } + %] + + + [% FILTER xmlstyle %] + <div>foo</div> + <div align="right">bar</div> + [% END %] + +The output produced is: + + <div align="left">foo</div> + <div align="right">bar</div> + +The filter can also be used to change the element from one type to another. + + [% FILTER xmlstyle + th = { + element = 'td' + attributes = { bgcolor='red' } + } + %] + <tr> + <th>Heading</th> + </tr> + <tr> + <td>Value</td> + </tr> + [% END %] + +The output here is as follows. Notice how the end tag C<E<lt>/thE<gt>> is +changed to C<E<lt>/tdE<gt>> as well as the start tag. + + <tr> + <td bgcolor="red">Heading</td> + </tr> + <tr> + <td>Value</td> + </tr> + +You can also define text to be added immediately before or after the +start or end tags. For example: + + [% FILTER xmlstyle + table = { + pre_start = '<div align="center">' + post_end = '</div>' + } + th = { + element = 'td' + attributes = { bgcolor='red' } + post_start = '<b>' + pre_end = '</b>' + } + %] + <table> + <tr> + <th>Heading</th> + </tr> + <tr> + <td>Value</td> + </tr> + </table> + [% END %] + +The output produced is: + + <div align="center"> + <table> + <tr> + <td bgcolor="red"><b>Heading</b></td> + </tr> + <tr> + <td>Value</td> + </tr> + </table> + </div> + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.34, 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: diff --git a/lib/Template/Plugin/XML/XPath.pm b/lib/Template/Plugin/XML/XPath.pm new file mode 100644 index 0000000..adf9292 --- /dev/null +++ b/lib/Template/Plugin/XML/XPath.pm @@ -0,0 +1,284 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugin::XML::XPath +# +# DESCRIPTION +# +# Template Toolkit plugin interfacing to the XML::XPath.pm module. +# +# 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: XPath.pm,v 2.69 2004/01/13 16:21:50 abw Exp $ +# +#============================================================================ + +package Template::Plugin::XML::XPath; + +require 5.004; + +use strict; +use Template::Exception; +use Template::Plugin; +use XML::XPath; + +use base qw( Template::Plugin ); +use vars qw( $VERSION ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.69 $ =~ /(\d+)\.(\d+)/); + + +#------------------------------------------------------------------------ +# new($context, \%config) +# +# Constructor method for XML::XPath plugin. Creates an XML::XPath +# object and initialises plugin configuration. +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $context = shift; + my $args = ref $_[-1] eq 'HASH' ? pop(@_) : { }; + my ($content, $about); + + # determine the input source from a positional parameter (may be a + # filename or XML text if it contains a '<' character) or by using + # named parameters which may specify one of 'file', 'filename', 'text' + # or 'xml' + + if ($content = shift) { + if ($content =~ /\</) { + $about = 'xml text'; + $args->{ xml } = $content; + } + else { + $about = "xml file $content"; + $args->{ filename } = $content; + } + } + elsif ($content = $args->{ text } || $args->{ xml }) { + $about = 'xml text'; + $args->{ xml } = $content; + } + elsif ($content = $args->{ file } || $args->{ filename }) { + $about = "xml file $content"; + $args->{ filename } = $content; + } + else { + return $class->_throw('no filename or xml text specified'); + } + + return XML::XPath->new(%$args) + or $class->_throw("failed to create XML::XPath::Parser\n"); +} + + + +#------------------------------------------------------------------------ +# _throw($errmsg) +# +# Raise a Template::Exception of type XML.XPath via die(). +#------------------------------------------------------------------------ + +sub _throw { + my ($self, $error) = @_; +# print STDERR "about to throw $error\n"; + die (Template::Exception->new('XML.XPath', $error)); +} + + +#======================================================================== +package XML::XPath::Node::Element; +#======================================================================== + +#------------------------------------------------------------------------ +# present($view) +# +# Method to present an element node via a view. +#------------------------------------------------------------------------ + +sub present { + my ($self, $view) = @_; + $view->view($self->getName(), $self); +} + +sub content { + my ($self, $view) = @_; + my $output = ''; + foreach my $node (@{ $self->getChildNodes }) { + $output .= $node->present($view); + } + return $output; +} + +#---------------------------------------------------------------------- +# starttag(), endtag() +# +# Methods to output the start & end tag, e.g. <foo bar="baz"> & </foo> +#---------------------------------------------------------------------- + +sub starttag { + my ($self) = @_; + my $output = "<". $self->getName(); + foreach my $attr ($self->getAttributes()) + { + $output .= $attr->toString(); + } + $output .= ">"; + return $output; +} + +sub endtag { + my ($self) = @_; + return "</". $self->getName() . ">"; +} + +#======================================================================== +package XML::XPath::Node::Text; +#======================================================================== + +#------------------------------------------------------------------------ +# present($view) +# +# Method to present a text node via a view. +#------------------------------------------------------------------------ + +sub present { + my ($self, $view) = @_; + $view->view('text', $self->string_value); +} + + +#======================================================================== +package XML::XPath::Node::Comment; +#======================================================================== + +sub present { return ''; } +sub starttag { return ''; } +sub endtag { return ''; } + + +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::XML::XPath - Plugin interface to XML::XPath + +=head1 SYNOPSIS + + # load plugin and specify XML file to parse + [% USE xpath = XML.XPath(xmlfile) %] + [% USE xpath = XML.XPath(file => xmlfile) %] + [% USE xpath = XML.XPath(filename => xmlfile) %] + + # load plugin and specify XML text to parse + [% USE xpath = XML.XPath(xmltext) %] + [% USE xpath = XML.XPath(xml => xmltext) %] + [% USE xpath = XML.XPath(text => xmltext) %] + + # then call any XPath methods (see XML::XPath docs) + [% FOREACH page = xpath.findnodes('/html/body/page') %] + [% page.getAttribute('title') %] + [% END %] + + # define VIEW to present node(s) + [% VIEW repview notfound='xmlstring' %] + # handler block for a <report>...</report> element + [% BLOCK report %] + [% item.content(view) %] + [% END %] + + # handler block for a <section title="...">...</section> element + [% BLOCK section %] + <h1>[% item.getAttribute('title') | html %]</h1> + [% item.content(view) %] + [% END %] + + # default template block passes tags through and renders + # out the children recursivly + [% BLOCK xmlstring; + item.starttag; item.content(view); item.endtag; + END %] + + # block to generate simple text + [% BLOCK text; item | html; END %] + [% END %] + + # now present node (and children) via view + [% repview.print(page) %] + + # or print node content via view + [% page.content(repview) %] + +=head1 PRE-REQUISITES + +This plugin requires that the XML::Parser and XML::XPath modules be +installed. These are available from CPAN: + + http://www.cpan.org/modules/by-module/XML + +=head1 DESCRIPTION + +This is a Template Toolkit plugin interfacing to the XML::XPath module. + +All methods implemented by the XML::XPath modules are available. In +addition, the XML::XPath::Node::Element module implements +present($view) and content($view) methods method for seamless +integration with Template Toolkit VIEWs. The XML::XPath::Node::Text +module is also adorned with a present($view) method which presents +itself via the view using the 'text' template. + +To aid the reconstruction of XML, methods starttag and endtag are +added to XML::XPath::Node::Element which return the start and +end tag for that element. This means that you can easily do: + + [% item.starttag %][% item.content(view) %][% item.endtag %] + +To render out the start tag, followed by the content rendered in the +view "view", followed by the end tag. + +=head1 AUTHORS + +This plugin module was written by Andy Wardley E<lt>abw@wardley.orgE<gt>. + +The XML::XPath module is by Matt Sergeant E<lt>matt@sergeant.orgE<gt>. + +=head1 VERSION + +2.69, 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>, L<XML::XPath|XML::XPath>, L<XML::Parser|XML::Parser> + diff --git a/lib/Template/Plugins.pm b/lib/Template/Plugins.pm new file mode 100644 index 0000000..1904efc --- /dev/null +++ b/lib/Template/Plugins.pm @@ -0,0 +1,1041 @@ +#============================================================= -*-Perl-*- +# +# Template::Plugins +# +# DESCRIPTION +# Plugin provider which handles the loading of plugin modules and +# instantiation of plugin objects. +# +# AUTHORS +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Plugins.pm,v 2.70 2004/01/13 16:19:15 abw Exp $ +# +#============================================================================ + +package Template::Plugins; + +require 5.004; + +use strict; +use base qw( Template::Base ); +use vars qw( $VERSION $DEBUG $STD_PLUGINS ); +use Template::Constants; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.70 $ =~ /(\d+)\.(\d+)/); + +$STD_PLUGINS = { + 'autoformat' => 'Template::Plugin::Autoformat', + 'cgi' => 'Template::Plugin::CGI', + 'datafile' => 'Template::Plugin::Datafile', + 'date' => 'Template::Plugin::Date', + 'debug' => 'Template::Plugin::Debug', + 'directory' => 'Template::Plugin::Directory', + 'dbi' => 'Template::Plugin::DBI', + 'dumper' => 'Template::Plugin::Dumper', + 'file' => 'Template::Plugin::File', + 'format' => 'Template::Plugin::Format', + 'html' => 'Template::Plugin::HTML', + 'image' => 'Template::Plugin::Image', + 'iterator' => 'Template::Plugin::Iterator', + 'pod' => 'Template::Plugin::Pod', + 'table' => 'Template::Plugin::Table', + 'url' => 'Template::Plugin::URL', + 'view' => 'Template::Plugin::View', + 'wrap' => 'Template::Plugin::Wrap', + 'xmlstyle' => 'Template::Plugin::XML::Style', +}; + + +#======================================================================== +# -- PUBLIC METHODS -- +#======================================================================== + +#------------------------------------------------------------------------ +# fetch($name, \@args, $context) +# +# General purpose method for requesting instantiation of a plugin +# object. The name of the plugin is passed as the first parameter. +# The internal FACTORY lookup table is consulted to retrieve the +# appropriate factory object or class name. If undefined, the _load() +# method is called to attempt to load the module and return a factory +# class/object which is then cached for subsequent use. A reference +# to the calling context should be passed as the third parameter. +# This is passed to the _load() class method. The new() method is +# then called against the factory class name or prototype object to +# instantiate a new plugin object, passing any arguments specified by +# list reference as the second parameter. e.g. where $factory is the +# class name 'MyClass', the new() method is called as a class method, +# $factory->new(...), equivalent to MyClass->new(...) . Where +# $factory is a prototype object, the new() method is called as an +# object method, $myobject->new(...). This latter approach allows +# plugins to act as Singletons, cache shared data, etc. +# +# Returns a reference to a plugin, (undef, STATUS_DECLINE) to decline +# the request or ($error, STATUS_ERROR) on error. +#------------------------------------------------------------------------ + +sub fetch { + my ($self, $name, $args, $context) = @_; + my ($factory, $plugin, $error); + + $self->debug("fetch($name, ", + defined $args ? ('[ ', join(', ', @$args), ' ]') : '<no args>', ', ', + defined $context ? $context : '<no context>', + ')') if $self->{ DEBUG }; + + # NOTE: + # the $context ref gets passed as the first parameter to all regular + # plugins, but not to those loaded via LOAD_PERL; to hack around + # this until we have a better implementation, we pass the $args + # reference to _load() and let it unshift the first args in the + # LOAD_PERL case + + $args ||= [ ]; + unshift @$args, $context; + + $factory = $self->{ FACTORY }->{ $name } ||= do { + ($factory, $error) = $self->_load($name, $context); + return ($factory, $error) if $error; ## RETURN + $factory; + }; + + # call the new() method on the factory object or class name + eval { + if (ref $factory eq 'CODE') { + defined( $plugin = &$factory(@$args) ) + || die "$name plugin failed\n"; + } + else { + defined( $plugin = $factory->new(@$args) ) + || die "$name plugin failed: ", $factory->error(), "\n"; + } + }; + if ($error = $@) { +# chomp $error; + return $self->{ TOLERANT } + ? (undef, Template::Constants::STATUS_DECLINED) + : ($error, Template::Constants::STATUS_ERROR); + } + + return $plugin; +} + + + +#======================================================================== +# -- PRIVATE METHODS -- +#======================================================================== + +#------------------------------------------------------------------------ +# _init(\%config) +# +# Private initialisation method. +#------------------------------------------------------------------------ + +sub _init { + my ($self, $params) = @_; + my ($pbase, $plugins, $factory) = + @$params{ qw( PLUGIN_BASE PLUGINS PLUGIN_FACTORY ) }; + + $plugins ||= { }; + if (ref $pbase ne 'ARRAY') { + $pbase = $pbase ? [ $pbase ] : [ ]; + } + push(@$pbase, 'Template::Plugin'); + + $self->{ PLUGIN_BASE } = $pbase; + $self->{ PLUGINS } = { %$STD_PLUGINS, %$plugins }; + $self->{ TOLERANT } = $params->{ TOLERANT } || 0; + $self->{ LOAD_PERL } = $params->{ LOAD_PERL } || 0; + $self->{ FACTORY } = $factory || { }; + $self->{ DEBUG } = ( $params->{ DEBUG } || 0 ) + & Template::Constants::DEBUG_PLUGINS; + + return $self; +} + + + +#------------------------------------------------------------------------ +# _load($name, $context) +# +# Private method which attempts to load a plugin module and determine the +# correct factory name or object by calling the load() class method in +# the loaded module. +#------------------------------------------------------------------------ + +sub _load { + my ($self, $name, $context) = @_; + my ($factory, $module, $base, $pkg, $file, $ok, $error); + + if ($module = $self->{ PLUGINS }->{ $name }) { + # plugin module name is explicitly stated in PLUGIN_NAME + $pkg = $module; + ($file = $module) =~ s|::|/|g; + $file =~ s|::|/|g; + $self->debug("loading $module.pm (PLUGIN_NAME)") + if $self->{ DEBUG }; + $ok = eval { require "$file.pm" }; + $error = $@; + } + else { + # try each of the PLUGIN_BASE values to build module name + ($module = $name) =~ s/\./::/g; + + foreach $base (@{ $self->{ PLUGIN_BASE } }) { + $pkg = $base . '::' . $module; + ($file = $pkg) =~ s|::|/|g; + + $self->debug("loading $file.pm (PLUGIN_BASE)") + if $self->{ DEBUG }; + + $ok = eval { require "$file.pm" }; + last unless $@; + + $error .= "$@\n" + unless ($@ =~ /^Can\'t locate $file\.pm/); + } + } + + if ($ok) { + $self->debug("calling $pkg->load()") if $self->{ DEBUG }; + + $factory = eval { $pkg->load($context) }; + $error = ''; + if ($@ || ! $factory) { + $error = $@ || 'load() returned a false value'; + } + } + elsif ($self->{ LOAD_PERL }) { + # fallback - is it a regular Perl module? + ($file = $module) =~ s|::|/|g; + eval { require "$file.pm" }; + if ($@) { + $error = $@; + } + else { + # this is a regular Perl module so the new() constructor + # isn't expecting a $context reference as the first argument; + # so we construct a closure which removes it before calling + # $module->new(@_); + $factory = sub { + shift; + $module->new(@_); + }; + $error = ''; + } + } + + if ($factory) { + $self->debug("$name => $factory") if $self->{ DEBUG }; + return $factory; + } + elsif ($error) { + return $self->{ TOLERANT } + ? (undef, Template::Constants::STATUS_DECLINED) + : ($error, Template::Constants::STATUS_ERROR); + } + else { + return (undef, Template::Constants::STATUS_DECLINED); + } +} + + +#------------------------------------------------------------------------ +# _dump() +# +# Debug method which constructs and returns text representing the current +# state of the object. +#------------------------------------------------------------------------ + +sub _dump { + my $self = shift; + my $output = "[Template::Plugins] {\n"; + my $format = " %-16s => %s\n"; + my $key; + + foreach $key (qw( TOLERANT LOAD_PERL )) { + $output .= sprintf($format, $key, $self->{ $key }); + } + + local $" = ', '; + my $fkeys = join(", ", keys %{$self->{ FACTORY }}); + my $plugins = $self->{ PLUGINS }; + $plugins = join('', map { + sprintf(" $format", $_, $plugins->{ $_ }); + } keys %$plugins); + $plugins = "{\n$plugins }"; + + $output .= sprintf($format, 'PLUGIN_BASE', "[ @{ $self->{ PLUGIN_BASE } } ]"); + $output .= sprintf($format, 'PLUGINS', $plugins); + $output .= sprintf($format, 'FACTORY', $fkeys); + $output .= '}'; + return $output; +} + + +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::Plugins - Plugin provider module + +=head1 SYNOPSIS + + use Template::Plugins; + + $plugin_provider = Template::Plugins->new(\%options); + + ($plugin, $error) = $plugin_provider->fetch($name, @args); + +=head1 DESCRIPTION + +The Template::Plugins module defines a provider class which can be used +to load and instantiate Template Toolkit plugin modules. + +=head1 METHODS + +=head2 new(\%params) + +Constructor method which instantiates and returns a reference to a +Template::Plugins object. A reference to a hash array of configuration +items may be passed as a parameter. These are described below. + +Note that the Template.pm front-end module creates a Template::Plugins +provider, passing all configuration items. Thus, the examples shown +below in the form: + + $plugprov = Template::Plugins->new({ + PLUGIN_BASE => 'MyTemplate::Plugin', + LOAD_PERL => 1, + ... + }); + +can also be used via the Template module as: + + $ttengine = Template->new({ + PLUGIN_BASE => 'MyTemplate::Plugin', + LOAD_PERL => 1, + ... + }); + +as well as the more explicit form of: + + $plugprov = Template::Plugins->new({ + PLUGIN_BASE => 'MyTemplate::Plugin', + LOAD_PERL => 1, + ... + }); + + $ttengine = Template->new({ + LOAD_PLUGINS => [ $plugprov ], + }); + +=head2 fetch($name, @args) + +Called to request that a plugin of a given name be provided. The relevant +module is first loaded (if necessary) and the load() class method called +to return the factory class name (usually the same package name) or a +factory object (a prototype). The new() method is then called as a +class or object method against the factory, passing all remaining +parameters. + +Returns a reference to a new plugin object or ($error, STATUS_ERROR) +on error. May also return (undef, STATUS_DECLINED) to decline to +serve the request. If TOLERANT is set then all errors will be +returned as declines. + +=head1 CONFIGURATION OPTIONS + +The following list details the configuration options that can be provided +to the Template::Plugins new() constructor. + +=over 4 + + + + +=item PLUGINS + +The PLUGINS options can be used to provide a reference to a hash array +that maps plugin names to Perl module names. A number of standard +plugins are defined (e.g. 'table', 'cgi', 'dbi', etc.) which map to +their corresponding Template::Plugin::* counterparts. These can be +redefined by values in the PLUGINS hash. + + my $plugins = Template::Plugins->new({ + PLUGINS => { + cgi => 'MyOrg::Template::Plugin::CGI', + foo => 'MyOrg::Template::Plugin::Foo', + bar => 'MyOrg::Template::Plugin::Bar', + }, + }); + +The USE directive is used to create plugin objects and does so by +calling the plugin() method on the current Template::Context object. +If the plugin name is defined in the PLUGINS hash then the +corresponding Perl module is loaded via require(). The context then +calls the load() class method which should return the class name +(default and general case) or a prototype object against which the +new() method can be called to instantiate individual plugin objects. + +If the plugin name is not defined in the PLUGINS hash then the PLUGIN_BASE +and/or LOAD_PERL options come into effect. + + + + + +=item PLUGIN_BASE + +If a plugin is not defined in the PLUGINS hash then the PLUGIN_BASE is used +to attempt to construct a correct Perl module name which can be successfully +loaded. + +The PLUGIN_BASE can be specified as a single value or as a reference +to an array of multiple values. The default PLUGIN_BASE value, +'Template::Plugin', is always added the the end of the PLUGIN_BASE +list (a single value is first converted to a list). Each value should +contain a Perl package name to which the requested plugin name is +appended. + +example 1: + + my $plugins = Template::Plugins->new({ + PLUGIN_BASE => 'MyOrg::Template::Plugin', + }); + + [% USE Foo %] # => MyOrg::Template::Plugin::Foo + or Template::Plugin::Foo + +example 2: + + my $plugins = Template::Plugins->new({ + PLUGIN_BASE => [ 'MyOrg::Template::Plugin', + 'YourOrg::Template::Plugin' ], + }); + + [% USE Foo %] # => MyOrg::Template::Plugin::Foo + or YourOrg::Template::Plugin::Foo + or Template::Plugin::Foo + + + + + + +=item LOAD_PERL + +If a plugin cannot be loaded using the PLUGINS or PLUGIN_BASE +approaches then the provider can make a final attempt to load the +module without prepending any prefix to the module path. This allows +regular Perl modules (i.e. those that don't reside in the +Template::Plugin or some other such namespace) to be loaded and used +as plugins. + +By default, the LOAD_PERL option is set to 0 and no attempt will be made +to load any Perl modules that aren't named explicitly in the PLUGINS +hash or reside in a package as named by one of the PLUGIN_BASE +components. + +Plugins loaded using the PLUGINS or PLUGIN_BASE receive a reference to +the current context object as the first argument to the new() +constructor. Modules loaded using LOAD_PERL are assumed to not +conform to the plugin interface. They must provide a new() class +method for instantiating objects but it will not receive a reference +to the context as the first argument. Plugin modules should provide a +load() class method (or inherit the default one from the +Template::Plugin base class) which is called the first time the plugin +is loaded. Regular Perl modules need not. In all other respects, +regular Perl objects and Template Toolkit plugins are identical. + +If a particular Perl module does not conform to the common, but not +unilateral, new() constructor convention then a simple plugin wrapper +can be written to interface to it. + + + + +=item TOLERANT + +The TOLERANT flag is used by the various Template Toolkit provider +modules (Template::Provider, Template::Plugins, Template::Filters) to +control their behaviour when errors are encountered. By default, any +errors are reported as such, with the request for the particular +resource (template, plugin, filter) being denied and an exception +raised. When the TOLERANT flag is set to any true values, errors will +be silently ignored and the provider will instead return +STATUS_DECLINED. This allows a subsequent provider to take +responsibility for providing the resource, rather than failing the +request outright. If all providers decline to service the request, +either through tolerated failure or a genuine disinclination to +comply, then a 'E<lt>resourceE<gt> not found' exception is raised. + + + + +=item DEBUG + +The DEBUG option can be used to enable debugging messages from the +Template::Plugins module by setting it to include the DEBUG_PLUGINS +value. + + use Template::Constants qw( :debug ); + + my $template = Template->new({ + DEBUG => DEBUG_FILTERS | DEBUG_PLUGINS, + }); + + + + +=back + + + +=head1 TEMPLATE TOOLKIT PLUGINS + +The following plugin modules are distributed with the Template +Toolkit. Some of the plugins interface to external modules (detailed +below) which should be downloaded from any CPAN site and installed +before using the plugin. + +=head2 Autoformat + +The Autoformat plugin is an interface to Damian Conway's Text::Autoformat +Perl module which provides advanced text wrapping and formatting. See +L<Template::Plugin::Autoformat> and L<Text::Autoformat> for further +details. + + [% USE autoformat(left=10, right=20) %] + [% autoformat(mytext) %] # call autoformat sub + [% mytext FILTER autoformat %] # or use autoformat filter + +The Text::Autoformat module is available from CPAN: + + http://www.cpan.org/modules/by-module/Text/ + +=head2 CGI + +The CGI plugin is a wrapper around Lincoln Stein's +E<lt>lstein@genome.wi.mit.eduE<gt> CGI.pm module. The plugin is +distributed with the Template Toolkit (see L<Template::Plugin::CGI>) +and the CGI module itself is distributed with recent versions Perl, +or is available from CPAN. + + [% USE CGI %] + [% CGI.param('param_name') %] + [% CGI.start_form %] + [% CGI.popup_menu( Name => 'color', + Values => [ 'Green', 'Brown' ] ) %] + [% CGI.end_form %] + +=head2 Datafile + +Provides an interface to data stored in a plain text file in a simple +delimited format. The first line in the file specifies field names +which should be delimiter by any non-word character sequence. +Subsequent lines define data using the same delimiter as int he first +line. Blank lines and comments (lines starting '#') are ignored. See +L<Template::Plugin::Datafile> for further details. + +/tmp/mydata: + + # define names for each field + id : email : name : tel + # here's the data + fred : fred@here.com : Fred Smith : 555-1234 + bill : bill@here.com : Bill White : 555-5678 + +example: + + [% USE userlist = datafile('/tmp/mydata') %] + + [% FOREACH user = userlist %] + [% user.name %] ([% user.id %]) + [% END %] + +=head2 Date + +The Date plugin provides an easy way to generate formatted time and date +strings by delegating to the POSIX strftime() routine. See +L<Template::Plugin::Date> and L<POSIX> for further details. + + [% USE date %] + [% date.format %] # current time/date + + File last modified: [% date.format(template.modtime) %] + +=head2 Directory + +The Directory plugin provides a simple interface to a directory and +the files within it. See L<Template::Plugin::Directory> for further +details. + + [% USE dir = Directory('/tmp') %] + [% FOREACH file = dir.files %] + # all the plain files in the directory + [% END %] + [% FOREACH file = dir.dirs %] + # all the sub-directories + [% END %] + +=head2 DBI + +The DBI plugin, developed by Simon Matthews +E<lt>sam@knowledgepool.comE<gt>, brings the full power of Tim Bunce's +E<lt>Tim.Bunce@ig.co.ukE<gt> database interface module (DBI) to your +templates. See L<Template::Plugin::DBI> and L<DBI> for further details. + + [% USE DBI('dbi:driver:database', 'user', 'pass') %] + + [% FOREACH user = DBI.query( 'SELECT * FROM users' ) %] + [% user.id %] [% user.name %] + [% END %] + +The DBI and relevant DBD modules are available from CPAN: + + http://www.cpan.org/modules/by-module/DBI/ + +=head2 Dumper + +The Dumper plugin provides an interface to the Data::Dumper module. See +L<Template::Plugin::Dumper> and L<Data::Dumper> for futher details. + + [% USE dumper(indent=0, pad="<br>") %] + [% dumper.dump(myvar, yourvar) %] + +=head2 File + +The File plugin provides a general abstraction for files and can be +used to fetch information about specific files within a filesystem. +See L<Template::Plugin::File> for further details. + + [% USE File('/tmp/foo.html') %] + [% File.name %] # foo.html + [% File.dir %] # /tmp + [% File.mtime %] # modification time + +=head2 Filter + +This module implements a base class plugin which can be subclassed +to easily create your own modules that define and install new filters. + + package MyOrg::Template::Plugin::MyFilter; + + use Template::Plugin::Filter; + use base qw( Template::Plugin::Filter ); + + sub filter { + my ($self, $text) = @_; + + # ...mungify $text... + + return $text; + } + + # now load it... + [% USE MyFilter %] + + # ...and use the returned object as a filter + [% FILTER $MyFilter %] + ... + [% END %] + +See L<Template::Plugin::Filter> for further details. + +=head2 Format + +The Format plugin provides a simple way to format text according to a +printf()-like format. See L<Template::Plugin::Format> for further +details. + + [% USE bold = format('<b>%s</b>') %] + [% bold('Hello') %] + +=head2 GD::Image, GD::Polygon, GD::Constants + +These plugins provide access to the GD graphics library via Lincoln +D. Stein's GD.pm interface. These plugins allow PNG, JPEG and other +graphical formats to be generated. + + [% FILTER null; + USE im = GD.Image(100,100); + # allocate some colors + black = im.colorAllocate(0, 0, 0); + red = im.colorAllocate(255,0, 0); + blue = im.colorAllocate(0, 0, 255); + # Draw a blue oval + im.arc(50,50,95,75,0,360,blue); + # And fill it with red + im.fill(50,50,red); + # Output image in PNG format + im.png | stdout(1); + END; + -%] + +See L<Template::Plugin::GD::Image> for further details. + +=head2 GD::Text, GD::Text::Align, GD::Text::Wrap + +These plugins provide access to Martien Verbruggen's GD::Text, +GD::Text::Align and GD::Text::Wrap modules. These plugins allow the +layout, alignment and wrapping of text when drawing text in GD images. + + [% FILTER null; + USE gd = GD.Image(200,400); + USE gdc = GD.Constants; + black = gd.colorAllocate(0, 0, 0); + green = gd.colorAllocate(0, 255, 0); + txt = "This is some long text. " | repeat(10); + USE wrapbox = GD.Text.Wrap(gd, + line_space => 4, + color => green, + text => txt, + ); + wrapbox.set_font(gdc.gdMediumBoldFont); + wrapbox.set(align => 'center', width => 160); + wrapbox.draw(20, 20); + gd.png | stdout(1); + END; + -%] + +See L<Template::Plugin::GD::Text>, L<Template::Plugin::GD::Text::Align> +and L<Template::Plugin::GD::Text::Wrap> for further details. + +=head2 GD::Graph::lines, GD::Graph::bars, GD::Graph::points, GD::Graph::linespoin +ts, GD::Graph::area, GD::Graph::mixed, GD::Graph::pie + +These plugins provide access to Martien Verbruggen's GD::Graph module +that allows graphs, plots and charts to be created. These plugins allow +graphs, plots and charts to be generated in PNG, JPEG and other +graphical formats. + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th"], + [ 4, 2, 3, 4, 3, 3.5] + ]; + USE my_graph = GD.Graph.pie(250, 200); + my_graph.set( + title => 'A Pie Chart', + label => 'Label', + axislabelclr => 'black', + pie_height => 36, + transparent => 0, + ); + my_graph.plot(data).png | stdout(1); + END; + -%] + +See +L<Template::Plugin::GD::Graph::lines>, +L<Template::Plugin::GD::Graph::bars>, +L<Template::Plugin::GD::Graph::points>, +L<Template::Plugin::GD::Graph::linespoints>, +L<Template::Plugin::GD::Graph::area>, +L<Template::Plugin::GD::Graph::mixed>, +L<Template::Plugin::GD::Graph::pie>, and +L<GD::Graph>, +for more details. + +=head2 GD::Graph::bars3d, GD::Graph::lines3d, GD::Graph::pie3d + +These plugins provide access to Jeremy Wadsack's GD::Graph3d +module. This allows 3D bar charts and 3D lines plots to +be generated. + + [% FILTER null; + data = [ + ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"], + [ 1, 2, 5, 6, 3, 1.5, 1, 3, 4], + ]; + USE my_graph = GD.Graph.bars3d(); + my_graph.set( + x_label => 'X Label', + y_label => 'Y label', + title => 'A 3d Bar Chart', + y_max_value => 8, + y_tick_number => 8, + y_label_skip => 2, + # shadows + bar_spacing => 8, + shadow_depth => 4, + shadowclr => 'dred', + transparent => 0, + my_graph.plot(data).png | stdout(1); + END; + -%] + +See +L<Template::Plugin::GD::Graph::lines3d>, +L<Template::Plugin::GD::Graph::bars3d>, and +L<Template::Plugin::GD::Graph::pie3d> +for more details. + +=head2 HTML + +The HTML plugin is very new and very basic, implementing a few useful +methods for generating HTML. It is likely to be extended in the future +or integrated with a larger project to generate HTML elements in a generic +way (as discussed recently on the mod_perl mailing list). + + [% USE HTML %] + [% HTML.escape("if (a < b && c > d) ..." %] + [% HTML.attributes(border => 1, cellpadding => 2) %] + [% HTML.element(table => { border => 1, cellpadding => 2 }) %] + +See L<Template::Plugin::HTML> for further details. + +=head2 Iterator + +The Iterator plugin provides a way to create a Template::Iterator +object to iterate over a data set. An iterator is created +automatically by the FOREACH directive and is aliased to the 'loop' +variable. This plugin allows an iterator to be explicitly created +with a given name, or the default plugin name, 'iterator'. See +L<Template::Plugin::Iterator> for further details. + + [% USE iterator(list, args) %] + + [% FOREACH item = iterator %] + [% '<ul>' IF iterator.first %] + <li>[% item %] + [% '</ul>' IF iterator.last %] + [% END %] + +=head2 Pod + +This plugin provides an interface to the L<Pod::POM|Pod::POM> module +which parses POD documents into an internal object model which can +then be traversed and presented through the Template Toolkit. + + [% USE Pod(podfile) %] + + [% FOREACH head1 = Pod.head1; + FOREACH head2 = head1/head2; + ... + END; + END + %] + +=head2 String + +The String plugin implements an object-oriented interface for +manipulating strings. See L<Template::Plugin::String> for further +details. + + [% USE String 'Hello' %] + [% String.append(' World') %] + + [% msg = String.new('Another string') %] + [% msg.replace('string', 'text') %] + + The string "[% msg %]" is [% msg.length %] characters long. + +=head2 Table + +The Table plugin allows you to format a list of data items into a +virtual table by specifying a fixed number of rows or columns, with +an optional overlap. See L<Template::Plugin::Table> for further +details. + + [% USE table(list, rows=10, overlap=1) %] + + [% FOREACH item = table.col(3) %] + [% item %] + [% END %] + +=head2 URL + +The URL plugin provides a simple way of contructing URLs from a base +part and a variable set of parameters. See L<Template::Plugin::URL> +for further details. + + [% USE mycgi = url('/cgi-bin/bar.pl', debug=1) %] + + [% mycgi %] + # ==> /cgi/bin/bar.pl?debug=1 + + [% mycgi(mode='submit') %] + # ==> /cgi/bin/bar.pl?mode=submit&debug=1 + +=head2 Wrap + +The Wrap plugin uses the Text::Wrap module by David Muir Sharnoff +E<lt>muir@idiom.comE<gt> (with help from Tim Pierce and many many others) +to provide simple paragraph formatting. See L<Template::Plugin::Wrap> +and L<Text::Wrap> for further details. + + [% USE wrap %] + [% wrap(mytext, 40, '* ', ' ') %] # use wrap sub + [% mytext FILTER wrap(40) -%] # or wrap FILTER + +The Text::Wrap module is available from CPAN: + + http://www.cpan.org/modules/by-module/Text/ + +=head2 XML::DOM + +The XML::DOM plugin gives access to the XML Document Object Module via +Clark Cooper E<lt>cooper@sch.ge.comE<gt> and Enno Derksen's +E<lt>enno@att.comE<gt> XML::DOM module. See L<Template::Plugin::XML::DOM> +and L<XML::DOM> for further details. + + [% USE dom = XML.DOM %] + [% doc = dom.parse(filename) %] + + [% FOREACH node = doc.getElementsByTagName('CODEBASE') %] + * [% node.getAttribute('href') %] + [% END %] + +The plugin requires the XML::DOM module, available from CPAN: + + http://www.cpan.org/modules/by-module/XML/ + +=head2 XML::RSS + +The XML::RSS plugin is a simple interface to Jonathan Eisenzopf's +E<lt>eisen@pobox.comE<gt> XML::RSS module. A RSS (Rich Site Summary) +file is typically used to store short news 'headlines' describing +different links within a site. This plugin allows you to parse RSS +files and format the contents accordingly using templates. +See L<Template::Plugin::XML::RSS> and L<XML::RSS> for further details. + + [% USE news = XML.RSS(filename) %] + + [% FOREACH item = news.items %] + <a href="[% item.link %]">[% item.title %]</a> + [% END %] + +The XML::RSS module is available from CPAN: + + http://www.cpan.org/modules/by-module/XML/ + +=head2 XML::Simple + +This plugin implements an interface to the L<XML::Simple|XML::Simple> +module. + + [% USE xml = XML.Simple(xml_file_or_text) %] + + [% xml.head.title %] + +See L<Template::Plugin::XML::Simple> for further details. + +=head2 XML::Style + +This plugin defines a filter for performing simple stylesheet based +transformations of XML text. + + [% USE xmlstyle + table = { + attributes = { + border = 0 + cellpadding = 4 + cellspacing = 1 + } + } + %] + + [% FILTER xmlstyle %] + <table> + <tr> + <td>Foo</td> <td>Bar</td> <td>Baz</td> + </tr> + </table> + [% END %] + +See L<Template::Plugin::XML::Style> for further details. + +=head2 XML::XPath + +The XML::XPath plugin provides an interface to Matt Sergeant's +E<lt>matt@sergeant.orgE<gt> XML::XPath module. See +L<Template::Plugin::XML::XPath> and L<XML::XPath> for further details. + + [% USE xpath = XML.XPath(xmlfile) %] + [% FOREACH page = xpath.findnodes('/html/body/page') %] + [% page.getAttribute('title') %] + [% END %] + +The plugin requires the XML::XPath module, available from CPAN: + + http://www.cpan.org/modules/by-module/XML/ + + + + +=head1 BUGS / ISSUES + +=over 4 + +=item * + +It might be worthwhile being able to distinguish between absolute +module names and those which should be applied relative to PLUGIN_BASE +directories. For example, use 'MyNamespace::MyModule' to denote +absolute module names (e.g. LOAD_PERL), and 'MyNamespace.MyModule' to +denote relative to PLUGIN_BASE. + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.70, 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|Template>, L<Template::Plugin|Template::Plugin>, L<Template::Context|Template::Context> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Provider.pm b/lib/Template/Provider.pm new file mode 100644 index 0000000..0826a18 --- /dev/null +++ b/lib/Template/Provider.pm @@ -0,0 +1,1449 @@ +#============================================================= -*-Perl-*- +# +# Template::Provider +# +# DESCRIPTION +# This module implements a class which handles the loading, compiling +# and caching of templates. Multiple Template::Provider objects can +# be stacked and queried in turn to effect a Chain-of-Command between +# them. A provider will attempt to return the requested template, +# an error (STATUS_ERROR) or decline to provide the template +# (STATUS_DECLINE), allowing subsequent providers to attempt to +# deliver it. See 'Design Patterns' for further details. +# +# AUTHOR +# Andy Wardley <abw@wardley.org> +# +# COPYRIGHT +# Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +# TODO: +# * optional provider prefix (e.g. 'http:') +# * fold ABSOLUTE and RELATIVE test cases into one regex? +# +#---------------------------------------------------------------------------- +# +# $Id: Provider.pm,v 2.79 2004/01/13 16:19:16 abw Exp $ +# +#============================================================================ + +package Template::Provider; + +require 5.004; + +use strict; +use vars qw( $VERSION $DEBUG $ERROR $DOCUMENT $STAT_TTL $MAX_DIRS ); +use base qw( Template::Base ); +use Template::Config; +use Template::Constants; +use Template::Document; +use File::Basename; +use File::Spec; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.79 $ =~ /(\d+)\.(\d+)/); + +# name of document class +$DOCUMENT = 'Template::Document' unless defined $DOCUMENT; + +# maximum time between performing stat() on file to check staleness +$STAT_TTL = 1 unless defined $STAT_TTL; + +# maximum number of directories in an INCLUDE_PATH, to prevent runaways +$MAX_DIRS = 64 unless defined $MAX_DIRS; + +use constant PREV => 0; +use constant NAME => 1; +use constant DATA => 2; +use constant LOAD => 3; +use constant NEXT => 4; +use constant STAT => 5; + +$DEBUG = 0 unless defined $DEBUG; + +#======================================================================== +# -- PUBLIC METHODS -- +#======================================================================== + +#------------------------------------------------------------------------ +# fetch($name) +# +# Returns a compiled template for the name specified by parameter. +# The template is returned from the internal cache if it exists, or +# loaded and then subsequently cached. The ABSOLUTE and RELATIVE +# configuration flags determine if absolute (e.g. '/something...') +# and/or relative (e.g. './something') paths should be honoured. The +# INCLUDE_PATH is otherwise used to find the named file. $name may +# also be a reference to a text string containing the template text, +# or a file handle from which the content is read. The compiled +# template is not cached in these latter cases given that there is no +# filename to cache under. A subsequent call to store($name, +# $compiled) can be made to cache the compiled template for future +# fetch() calls, if necessary. +# +# Returns a compiled template or (undef, STATUS_DECLINED) if the +# template could not be found. On error (e.g. the file was found +# but couldn't be read or parsed), the pair ($error, STATUS_ERROR) +# is returned. The TOLERANT configuration option can be set to +# downgrade any errors to STATUS_DECLINE. +#------------------------------------------------------------------------ + +sub fetch { + my ($self, $name) = @_; + my ($data, $error); + + if (ref $name) { + # $name can be a reference to a scalar, GLOB or file handle + ($data, $error) = $self->_load($name); + ($data, $error) = $self->_compile($data) + unless $error; + $data = $data->{ data } + unless $error; + } + elsif (File::Spec->file_name_is_absolute($name)) { + # absolute paths (starting '/') allowed if ABSOLUTE set + ($data, $error) = $self->{ ABSOLUTE } + ? $self->_fetch($name) + : $self->{ TOLERANT } + ? (undef, Template::Constants::STATUS_DECLINED) + : ("$name: absolute paths are not allowed (set ABSOLUTE option)", + Template::Constants::STATUS_ERROR); + } + elsif ($name =~ m[^\.+/]) { + # anything starting "./" is relative to cwd, allowed if RELATIVE set + ($data, $error) = $self->{ RELATIVE } + ? $self->_fetch($name) + : $self->{ TOLERANT } + ? (undef, Template::Constants::STATUS_DECLINED) + : ("$name: relative paths are not allowed (set RELATIVE option)", + Template::Constants::STATUS_ERROR); + } + else { + # otherwise, it's a file name relative to INCLUDE_PATH + ($data, $error) = $self->{ INCLUDE_PATH } + ? $self->_fetch_path($name) + : (undef, Template::Constants::STATUS_DECLINED); + } + +# $self->_dump_cache() +# if $DEBUG > 1; + + return ($data, $error); +} + + +#------------------------------------------------------------------------ +# store($name, $data) +# +# Store a compiled template ($data) in the cached as $name. +#------------------------------------------------------------------------ + +sub store { + my ($self, $name, $data) = @_; + $self->_store($name, { + data => $data, + load => 0, + }); +} + + +#------------------------------------------------------------------------ +# load($name) +# +# Load a template without parsing/compiling it, suitable for use with +# the INSERT directive. There's some duplication with fetch() and at +# some point this could be reworked to integrate them a little closer. +#------------------------------------------------------------------------ + +sub load { + my ($self, $name) = @_; + my ($data, $error); + my $path = $name; + + if (File::Spec->file_name_is_absolute($name)) { + # absolute paths (starting '/') allowed if ABSOLUTE set + $error = "$name: absolute paths are not allowed (set ABSOLUTE option)" + unless $self->{ ABSOLUTE }; + } + elsif ($name =~ m[^\.+/]) { + # anything starting "./" is relative to cwd, allowed if RELATIVE set + $error = "$name: relative paths are not allowed (set RELATIVE option)" + unless $self->{ RELATIVE }; + } + else { + INCPATH: { + # otherwise, it's a file name relative to INCLUDE_PATH + my $paths = $self->paths() + || return ($self->error(), Template::Constants::STATUS_ERROR); + + foreach my $dir (@$paths) { + $path = "$dir/$name"; + last INCPATH + if -f $path; + } + undef $path; # not found + } + } + + if (defined $path && ! $error) { + local $/ = undef; # slurp files in one go + local *FH; + if (open(FH, $path)) { + $data = <FH>; + close(FH); + } + else { + $error = "$name: $!"; + } + } + + if ($error) { + return $self->{ TOLERANT } + ? (undef, Template::Constants::STATUS_DECLINED) + : ($error, Template::Constants::STATUS_ERROR); + } + elsif (! defined $path) { + return (undef, Template::Constants::STATUS_DECLINED); + } + else { + return ($data, Template::Constants::STATUS_OK); + } +} + + + +#------------------------------------------------------------------------ +# include_path(\@newpath) +# +# Accessor method for the INCLUDE_PATH setting. If called with an +# argument, this method will replace the existing INCLUDE_PATH with +# the new value. +#------------------------------------------------------------------------ + +sub include_path { + my ($self, $path) = @_; + $self->{ INCLUDE_PATH } = $path if $path; + return $self->{ INCLUDE_PATH }; +} + + +#------------------------------------------------------------------------ +# paths() +# +# Evaluates the INCLUDE_PATH list, ignoring any blank entries, and +# calling and subroutine or object references to return dynamically +# generated path lists. Returns a reference to a new list of paths +# or undef on error. +#------------------------------------------------------------------------ + +sub paths { + my $self = shift; + my @ipaths = @{ $self->{ INCLUDE_PATH } }; + my (@opaths, $dpaths, $dir); + my $count = $MAX_DIRS; + + while (@ipaths && --$count) { + $dir = shift @ipaths || next; + + # $dir can be a sub or object ref which returns a reference + # to a dynamically generated list of search paths. + + if (ref $dir eq 'CODE') { + eval { $dpaths = &$dir() }; + if ($@) { + chomp $@; + return $self->error($@); + } + unshift(@ipaths, @$dpaths); + next; + } + elsif (UNIVERSAL::can($dir, 'paths')) { + $dpaths = $dir->paths() + || return $self->error($dir->error()); + unshift(@ipaths, @$dpaths); + next; + } + else { + push(@opaths, $dir); + } + } + return $self->error("INCLUDE_PATH exceeds $MAX_DIRS directories") + if @ipaths; + + return \@opaths; +} + + +#------------------------------------------------------------------------ +# DESTROY +# +# The provider cache is implemented as a doubly linked list which Perl +# cannot free by itself due to the circular references between NEXT <=> +# PREV items. This cleanup method walks the list deleting all the NEXT/PREV +# references, allowing the proper cleanup to occur and memory to be +# repooled. +#------------------------------------------------------------------------ + +sub DESTROY { + my $self = shift; + my ($slot, $next); + + $slot = $self->{ HEAD }; + while ($slot) { + $next = $slot->[ NEXT ]; + undef $slot->[ PREV ]; + undef $slot->[ NEXT ]; + $slot = $next; + } + undef $self->{ HEAD }; + undef $self->{ TAIL }; +} + + + + +#======================================================================== +# -- PRIVATE METHODS -- +#======================================================================== + +#------------------------------------------------------------------------ +# _init() +# +# Initialise the cache. +#------------------------------------------------------------------------ + +sub _init { + my ($self, $params) = @_; + my $size = $params->{ CACHE_SIZE }; + my $path = $params->{ INCLUDE_PATH } || '.'; + my $cdir = $params->{ COMPILE_DIR } || ''; + my $dlim = $params->{ DELIMITER }; + my $debug; + + # tweak delim to ignore C:/ + unless (defined $dlim) { + $dlim = ($^O eq 'MSWin32') ? ':(?!\\/)' : ':'; + } + + # coerce INCLUDE_PATH to an array ref, if not already so + $path = [ split(/$dlim/, $path) ] + unless ref $path eq 'ARRAY'; + + # don't allow a CACHE_SIZE 1 because it breaks things and the + # additional checking isn't worth it + $size = 2 + if defined $size && ($size == 1 || $size < 0); + + if (defined ($debug = $params->{ DEBUG })) { + $self->{ DEBUG } = $debug & ( Template::Constants::DEBUG_PROVIDER + | Template::Constants::DEBUG_FLAGS ); + } + else { + $self->{ DEBUG } = $DEBUG; + } + + if ($self->{ DEBUG }) { + local $" = ', '; + $self->debug("creating cache of ", + defined $size ? $size : 'unlimited', + " slots for [ @$path ]"); + } + + # create COMPILE_DIR and sub-directories representing each INCLUDE_PATH + # element in which to store compiled files + if ($cdir) { + +# Stas' hack +# # this is a hack to solve the problem with INCLUDE_PATH using +# # relative dirs +# my $segments = 0; +# for (@$path) { +# my $c = 0; +# $c++ while m|\.\.|g; +# $segments = $c if $c > $segments; +# } +# $cdir .= "/".join "/",('hack') x $segments if $segments; +# + + require File::Path; + foreach my $dir (@$path) { + next if ref $dir; + my $wdir = $dir; + $wdir =~ s[:][]g if $^O eq 'MSWin32'; + $wdir =~ /(.*)/; # untaint + &File::Path::mkpath(File::Spec->catfile($cdir, $1)); + } + } + + $self->{ LOOKUP } = { }; + $self->{ SLOTS } = 0; + $self->{ SIZE } = $size; + $self->{ INCLUDE_PATH } = $path; + $self->{ DELIMITER } = $dlim; + $self->{ COMPILE_DIR } = $cdir; + $self->{ COMPILE_EXT } = $params->{ COMPILE_EXT } || ''; + $self->{ ABSOLUTE } = $params->{ ABSOLUTE } || 0; + $self->{ RELATIVE } = $params->{ RELATIVE } || 0; + $self->{ TOLERANT } = $params->{ TOLERANT } || 0; + $self->{ DOCUMENT } = $params->{ DOCUMENT } || $DOCUMENT; + $self->{ PARSER } = $params->{ PARSER }; + $self->{ DEFAULT } = $params->{ DEFAULT }; +# $self->{ PREFIX } = $params->{ PREFIX }; + $self->{ PARAMS } = $params; + + return $self; +} + + +#------------------------------------------------------------------------ +# _fetch($name) +# +# Fetch a file from cache or disk by specification of an absolute or +# relative filename. No search of the INCLUDE_PATH is made. If the +# file is found and loaded, it is compiled and cached. +#------------------------------------------------------------------------ + +sub _fetch { + my ($self, $name) = @_; + my $size = $self->{ SIZE }; + my ($slot, $data, $error); + + $self->debug("_fetch($name)") if $self->{ DEBUG }; + + my $compiled = $self->_compiled_filename($name); + + if (defined $size && ! $size) { + # caching disabled so load and compile but don't cache + if ($compiled && -f $compiled + && (stat($name))[9] <= (stat($compiled))[9]) { + $data = $self->_load_compiled($compiled); + $error = $self->error() unless $data; + } + else { + ($data, $error) = $self->_load($name); + ($data, $error) = $self->_compile($data, $compiled) + unless $error; + $data = $data->{ data } + unless $error; + } + } + elsif ($slot = $self->{ LOOKUP }->{ $name }) { + # cached entry exists, so refresh slot and extract data + ($data, $error) = $self->_refresh($slot); + $data = $slot->[ DATA ] + unless $error; + } + else { + # nothing in cache so try to load, compile and cache + if ($compiled && -f $compiled + && (stat($name))[9] <= (stat($compiled))[9]) { + $data = $self->_load_compiled($compiled); + $error = $self->error() unless $data; + $self->store($name, $data) unless $error; + } + else { + ($data, $error) = $self->_load($name); + ($data, $error) = $self->_compile($data, $compiled) + unless $error; + $data = $self->_store($name, $data) + unless $error; + } + } + + return ($data, $error); +} + + +#------------------------------------------------------------------------ +# _fetch_path($name) +# +# Fetch a file from cache or disk by specification of an absolute cache +# name (e.g. 'header') or filename relative to one of the INCLUDE_PATH +# directories. If the file isn't already cached and can be found and +# loaded, it is compiled and cached under the full filename. +#------------------------------------------------------------------------ + +sub _fetch_path { + my ($self, $name) = @_; + my ($size, $compext, $compdir) = + @$self{ qw( SIZE COMPILE_EXT COMPILE_DIR ) }; + my ($dir, $paths, $path, $compiled, $slot, $data, $error); + local *FH; + + $self->debug("_fetch_path($name)") if $self->{ DEBUG }; + + # caching is enabled if $size is defined and non-zero or undefined + my $caching = (! defined $size || $size); + + INCLUDE: { + + # the template may have been stored using a non-filename name + if ($caching && ($slot = $self->{ LOOKUP }->{ $name })) { + # cached entry exists, so refresh slot and extract data + ($data, $error) = $self->_refresh($slot); + $data = $slot->[ DATA ] + unless $error; + last INCLUDE; + } + + $paths = $self->paths() || do { + $error = Template::Constants::STATUS_ERROR; + $data = $self->error(); + last INCLUDE; + }; + + # search the INCLUDE_PATH for the file, in cache or on disk + foreach $dir (@$paths) { + $path = File::Spec->catfile($dir, $name); + + $self->debug("searching path: $path\n") if $self->{ DEBUG }; + + if ($caching && ($slot = $self->{ LOOKUP }->{ $path })) { + # cached entry exists, so refresh slot and extract data + ($data, $error) = $self->_refresh($slot); + $data = $slot->[ DATA ] + unless $error; + last INCLUDE; + } + elsif (-f $path) { + $compiled = $self->_compiled_filename($path) + if $compext || $compdir; + + if ($compiled && -f $compiled + && (stat($path))[9] <= (stat($compiled))[9]) { + if ($data = $self->_load_compiled($compiled)) { + # store in cache + $data = $self->store($path, $data); + $error = Template::Constants::STATUS_OK; + last INCLUDE; + } + else { + warn($self->error(), "\n"); + } + } + # $compiled is set if an attempt to write the compiled + # template to disk should be made + + ($data, $error) = $self->_load($path, $name); + ($data, $error) = $self->_compile($data, $compiled) + unless $error; + $data = $self->_store($path, $data) + unless $error || ! $caching; + $data = $data->{ data } if ! $caching; + # all done if $error is OK or ERROR + last INCLUDE if ! $error + || $error == Template::Constants::STATUS_ERROR; + } + } + # template not found, so look for a DEFAULT template + my $default; + if (defined ($default = $self->{ DEFAULT }) && $name ne $default) { + $name = $default; + redo INCLUDE; + } + ($data, $error) = (undef, Template::Constants::STATUS_DECLINED); + } # INCLUDE + + return ($data, $error); +} + + + +sub _compiled_filename { + my ($self, $file) = @_; + my ($compext, $compdir) = @$self{ qw( COMPILE_EXT COMPILE_DIR ) }; + my ($path, $compiled); + + return undef + unless $compext || $compdir; + + $path = $file; + $path =~ /^(.+)$/s or die "invalid filename: $path"; + $path =~ s[:][]g if $^O eq 'MSWin32'; + + $compiled = "$path$compext"; + $compiled = File::Spec->catfile($compdir, $compiled) if length $compdir; + + return $compiled; +} + + +sub _load_compiled { + my ($self, $file) = @_; + my $compiled; + + # load compiled template via require(); we zap any + # %INC entry to ensure it is reloaded (we don't + # want 1 returned by require() to say it's in memory) + delete $INC{ $file }; + eval { $compiled = require $file; }; + return $@ + ? $self->error("compiled template $compiled: $@") + : $compiled; +} + + + +#------------------------------------------------------------------------ +# _load($name, $alias) +# +# Load template text from a string ($name = scalar ref), GLOB or file +# handle ($name = ref), or from an absolute filename ($name = scalar). +# Returns a hash array containing the following items: +# name filename or $alias, if provided, or 'input text', etc. +# text template text +# time modification time of file, or current time for handles/strings +# load time file was loaded (now!) +# +# On error, returns ($error, STATUS_ERROR), or (undef, STATUS_DECLINED) +# if TOLERANT is set. +#------------------------------------------------------------------------ + +sub _load { + my ($self, $name, $alias) = @_; + my ($data, $error); + my $tolerant = $self->{ TOLERANT }; + my $now = time; + local $/ = undef; # slurp files in one go + local *FH; + + $alias = $name unless defined $alias or ref $name; + + $self->debug("_load($name, ", defined $alias ? $alias : '<no alias>', + ')') if $self->{ DEBUG }; + + LOAD: { + if (ref $name eq 'SCALAR') { + # $name can be a SCALAR reference to the input text... + $data = { + name => defined $alias ? $alias : 'input text', + text => $$name, + time => $now, + load => 0, + }; + } + elsif (ref $name) { + # ...or a GLOB or file handle... + my $text = <$name>; + $data = { + name => defined $alias ? $alias : 'input file handle', + text => $text, + time => $now, + load => 0, + }; + } + elsif (-f $name) { + if (open(FH, $name)) { + my $text = <FH>; + $data = { + name => $alias, + path => $name, + text => $text, + time => (stat $name)[9], + load => $now, + }; + } + elsif ($tolerant) { + ($data, $error) = (undef, Template::Constants::STATUS_DECLINED); + } + else { + $data = "$alias: $!"; + $error = Template::Constants::STATUS_ERROR; + } + } + else { + ($data, $error) = (undef, Template::Constants::STATUS_DECLINED); + } + } + + $data->{ path } = $data->{ name } + if $data and ! defined $data->{ path }; + + return ($data, $error); +} + + +#------------------------------------------------------------------------ +# _refresh(\@slot) +# +# Private method called to mark a cache slot as most recently used. +# A reference to the slot array should be passed by parameter. The +# slot is relocated to the head of the linked list. If the file from +# which the data was loaded has been upated since it was compiled, then +# it is re-loaded from disk and re-compiled. +#------------------------------------------------------------------------ + +sub _refresh { + my ($self, $slot) = @_; + my ($head, $file, $data, $error); + + + $self->debug("_refresh([ ", + join(', ', map { defined $_ ? $_ : '<undef>' } @$slot), + '])') if $self->{ DEBUG }; + + # if it's more than $STAT_TTL seconds since we last performed a + # stat() on the file then we need to do it again and see if the file + # time has changed + if ( (time - $slot->[ STAT ]) > $STAT_TTL && stat $slot->[ NAME ] ) { + $slot->[ STAT ] = time; + + if ( (stat(_))[9] != $slot->[ LOAD ]) { + + $self->debug("refreshing cache file ", $slot->[ NAME ]) + if $self->{ DEBUG }; + + ($data, $error) = $self->_load($slot->[ NAME ], + $slot->[ DATA ]->{ name }); + ($data, $error) = $self->_compile($data) + unless $error; + + unless ($error) { + $slot->[ DATA ] = $data->{ data }; + $slot->[ LOAD ] = $data->{ time }; + } + } + } + + unless( $self->{ HEAD } == $slot ) { + # remove existing slot from usage chain... + if ($slot->[ PREV ]) { + $slot->[ PREV ]->[ NEXT ] = $slot->[ NEXT ]; + } + else { + $self->{ HEAD } = $slot->[ NEXT ]; + } + if ($slot->[ NEXT ]) { + $slot->[ NEXT ]->[ PREV ] = $slot->[ PREV ]; + } + else { + $self->{ TAIL } = $slot->[ PREV ]; + } + + # ..and add to start of list + $head = $self->{ HEAD }; + $head->[ PREV ] = $slot if $head; + $slot->[ PREV ] = undef; + $slot->[ NEXT ] = $head; + $self->{ HEAD } = $slot; + } + + return ($data, $error); +} + + +#------------------------------------------------------------------------ +# _store($name, $data) +# +# Private method called to add a data item to the cache. If the cache +# size limit has been reached then the oldest entry at the tail of the +# list is removed and its slot relocated to the head of the list and +# reused for the new data item. If the cache is under the size limit, +# or if no size limit is defined, then the item is added to the head +# of the list. +#------------------------------------------------------------------------ + +sub _store { + my ($self, $name, $data, $compfile) = @_; + my $size = $self->{ SIZE }; + my ($slot, $head); + + # extract the load time and compiled template from the data +# my $load = $data->{ load }; + my $load = (stat($name))[9]; + $data = $data->{ data }; + + $self->debug("_store($name, $data)") if $self->{ DEBUG }; + + if (defined $size && $self->{ SLOTS } >= $size) { + # cache has reached size limit, so reuse oldest entry + + $self->debug("reusing oldest cache entry (size limit reached: $size)\nslots: $self->{ SLOTS }") if $self->{ DEBUG }; + + # remove entry from tail of list + $slot = $self->{ TAIL }; + $slot->[ PREV ]->[ NEXT ] = undef; + $self->{ TAIL } = $slot->[ PREV ]; + + # remove name lookup for old node + delete $self->{ LOOKUP }->{ $slot->[ NAME ] }; + + # add modified node to head of list + $head = $self->{ HEAD }; + $head->[ PREV ] = $slot if $head; + @$slot = ( undef, $name, $data, $load, $head, time ); + $self->{ HEAD } = $slot; + + # add name lookup for new node + $self->{ LOOKUP }->{ $name } = $slot; + } + else { + # cache is under size limit, or none is defined + + $self->debug("adding new cache entry") if $self->{ DEBUG }; + + # add new node to head of list + $head = $self->{ HEAD }; + $slot = [ undef, $name, $data, $load, $head, time ]; + $head->[ PREV ] = $slot if $head; + $self->{ HEAD } = $slot; + $self->{ TAIL } = $slot unless $self->{ TAIL }; + + # add lookup from name to slot and increment nslots + $self->{ LOOKUP }->{ $name } = $slot; + $self->{ SLOTS }++; + } + + return $data; +} + + +#------------------------------------------------------------------------ +# _compile($data) +# +# Private method called to parse the template text and compile it into +# a runtime form. Creates and delegates a Template::Parser object to +# handle the compilation, or uses a reference passed in PARSER. On +# success, the compiled template is stored in the 'data' item of the +# $data hash and returned. On error, ($error, STATUS_ERROR) is returned, +# or (undef, STATUS_DECLINED) if the TOLERANT flag is set. +# The optional $compiled parameter may be passed to specify +# the name of a compiled template file to which the generated Perl +# code should be written. Errors are (for now...) silently +# ignored, assuming that failures to open a file for writing are +# intentional (e.g directory write permission). +#------------------------------------------------------------------------ + +sub _compile { + my ($self, $data, $compfile) = @_; + my $text = $data->{ text }; + my ($parsedoc, $error); + + $self->debug("_compile($data, ", + defined $compfile ? $compfile : '<no compfile>', ')') + if $self->{ DEBUG }; + + my $parser = $self->{ PARSER } + ||= Template::Config->parser($self->{ PARAMS }) + || return (Template::Config->error(), Template::Constants::STATUS_ERROR); + + # discard the template text - we don't need it any more + delete $data->{ text }; + + # call parser to compile template into Perl code + if ($parsedoc = $parser->parse($text, $data)) { + + $parsedoc->{ METADATA } = { + 'name' => $data->{ name }, + 'modtime' => $data->{ time }, + %{ $parsedoc->{ METADATA } }, + }; + + # write the Perl code to the file $compfile, if defined + if ($compfile) { + my $basedir = &File::Basename::dirname($compfile); + $basedir =~ /(.*)/; + $basedir = $1; + &File::Path::mkpath($basedir) unless -d $basedir; + + my $docclass = $self->{ DOCUMENT }; + $error = 'cache failed to write ' + . &File::Basename::basename($compfile) + . ': ' . $docclass->error() + unless $docclass->write_perl_file($compfile, $parsedoc); + + # set atime and mtime of newly compiled file, don't bother + # if time is undef + if (!defined($error) && defined $data->{ time }) { + my ($cfile) = $compfile =~ /^(.+)$/s or do { + return("invalid filename: $compfile", + Template::Constants::STATUS_ERROR); + }; + + my ($ctime) = $data->{ time } =~ /^(\d+)$/; + unless ($ctime || $ctime eq 0) { + return("invalid time: $ctime", + Template::Constants::STATUS_ERROR); + } + utime($ctime, $ctime, $cfile); + } + } + + unless ($error) { + return $data ## RETURN ## + if $data->{ data } = $DOCUMENT->new($parsedoc); + $error = $Template::Document::ERROR; + } + } + else { + $error = Template::Exception->new( 'parse', "$data->{ name } " . + $parser->error() ); + } + + # return STATUS_ERROR, or STATUS_DECLINED if we're being tolerant + return $self->{ TOLERANT } + ? (undef, Template::Constants::STATUS_DECLINED) + : ($error, Template::Constants::STATUS_ERROR) +} + + +#------------------------------------------------------------------------ +# _dump() +# +# Debug method which returns a string representing the internal object +# state. +#------------------------------------------------------------------------ + +sub _dump { + my $self = shift; + my $size = $self->{ SIZE }; + my $parser = $self->{ PARSER }; + $parser = $parser ? $parser->_dump() : '<no parser>'; + $parser =~ s/\n/\n /gm; + $size = 'unlimited' unless defined $size; + + my $output = "[Template::Provider] {\n"; + my $format = " %-16s => %s\n"; + my $key; + + $output .= sprintf($format, 'INCLUDE_PATH', + '[ ' . join(', ', @{ $self->{ INCLUDE_PATH } }) . ' ]'); + $output .= sprintf($format, 'CACHE_SIZE', $size); + + foreach $key (qw( ABSOLUTE RELATIVE TOLERANT DELIMITER + COMPILE_EXT COMPILE_DIR )) { + $output .= sprintf($format, $key, $self->{ $key }); + } + $output .= sprintf($format, 'PARSER', $parser); + + + local $" = ', '; + my $lookup = $self->{ LOOKUP }; + $lookup = join('', map { + sprintf(" $format", $_, defined $lookup->{ $_ } + ? ('[ ' . join(', ', map { defined $_ ? $_ : '<undef>' } + @{ $lookup->{ $_ } }) . ' ]') : '<undef>'); + } sort keys %$lookup); + $lookup = "{\n$lookup }"; + + $output .= sprintf($format, LOOKUP => $lookup); + + $output .= '}'; + return $output; +} + + +#------------------------------------------------------------------------ +# _dump_cache() +# +# Debug method which prints the current state of the cache to STDERR. +#------------------------------------------------------------------------ + +sub _dump_cache { + my $self = shift; + my ($node, $lut, $count); + + $count = 0; + if ($node = $self->{ HEAD }) { + while ($node) { + $lut->{ $node } = $count++; + $node = $node->[ NEXT ]; + } + $node = $self->{ HEAD }; + print STDERR "CACHE STATE:\n"; + print STDERR " HEAD: ", $self->{ HEAD }->[ NAME ], "\n"; + print STDERR " TAIL: ", $self->{ TAIL }->[ NAME ], "\n"; + while ($node) { + my ($prev, $name, $data, $load, $next) = @$node; +# $name = '...' . substr($name, -10) if length $name > 10; + $prev = $prev ? "#$lut->{ $prev }<-": '<undef>'; + $next = $next ? "->#$lut->{ $next }": '<undef>'; + print STDERR " #$lut->{ $node } : [ $prev, $name, $data, $load, $next ]\n"; + $node = $node->[ NEXT ]; + } + } +} + +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::Provider - Provider module for loading/compiling templates + +=head1 SYNOPSIS + + $provider = Template::Provider->new(\%options); + + ($template, $error) = $provider->fetch($name); + +=head1 DESCRIPTION + +The Template::Provider is used to load, parse, compile and cache template +documents. This object may be sub-classed to provide more specific +facilities for loading, or otherwise providing access to templates. + +The Template::Context objects maintain a list of Template::Provider +objects which are polled in turn (via fetch()) to return a requested +template. Each may return a compiled template, raise an error, or +decline to serve the reqest, giving subsequent providers a chance to +do so. + +This is the "Chain of Responsiblity" pattern. See 'Design Patterns' for +further information. + +This documentation needs work. + +=head1 PUBLIC METHODS + +=head2 new(\%options) + +Constructor method which instantiates and returns a new Template::Provider +object. The optional parameter may be a hash reference containing any of +the following items: + +=over 4 + + + + +=item INCLUDE_PATH + +The INCLUDE_PATH is used to specify one or more directories in which +template files are located. When a template is requested that isn't +defined locally as a BLOCK, each of the INCLUDE_PATH directories is +searched in turn to locate the template file. Multiple directories +can be specified as a reference to a list or as a single string where +each directory is delimited by ':'. + + my $provider = Template::Provider->new({ + INCLUDE_PATH => '/usr/local/templates', + }); + + my $provider = Template::Provider->new({ + INCLUDE_PATH => '/usr/local/templates:/tmp/my/templates', + }); + + my $provider = Template::Provider->new({ + INCLUDE_PATH => [ '/usr/local/templates', + '/tmp/my/templates' ], + }); + +On Win32 systems, a little extra magic is invoked, ignoring delimiters +that have ':' followed by a '/' or '\'. This avoids confusion when using +directory names like 'C:\Blah Blah'. + +When specified as a list, the INCLUDE_PATH path can contain elements +which dynamically generate a list of INCLUDE_PATH directories. These +generator elements can be specified as a reference to a subroutine or +an object which implements a paths() method. + + my $provider = Template::Provider->new({ + INCLUDE_PATH => [ '/usr/local/templates', + \&incpath_generator, + My::IncPath::Generator->new( ... ) ], + }); + +Each time a template is requested and the INCLUDE_PATH examined, the +subroutine or object method will be called. A reference to a list of +directories should be returned. Generator subroutines should report +errors using die(). Generator objects should return undef and make an +error available via its error() method. + +For example: + + sub incpath_generator { + + # ...some code... + + if ($all_is_well) { + return \@list_of_directories; + } + else { + die "cannot generate INCLUDE_PATH...\n"; + } + } + +or: + + package My::IncPath::Generator; + + # Template::Base (or Class::Base) provides error() method + use Template::Base; + use base qw( Template::Base ); + + sub paths { + my $self = shift; + + # ...some code... + + if ($all_is_well) { + return \@list_of_directories; + } + else { + return $self->error("cannot generate INCLUDE_PATH...\n"); + } + } + + 1; + + + + + +=item DELIMITER + +Used to provide an alternative delimiter character sequence for +separating paths specified in the INCLUDE_PATH. The default +value for DELIMITER is ':'. + + # tolerate Silly Billy's file system conventions + my $provider = Template::Provider->new({ + DELIMITER => '; ', + INCLUDE_PATH => 'C:/HERE/NOW; D:/THERE/THEN', + }); + + # better solution: install Linux! :-) + +On Win32 systems, the default delimiter is a little more intelligent, +splitting paths only on ':' characters that aren't followed by a '/'. +This means that the following should work as planned, splitting the +INCLUDE_PATH into 2 separate directories, C:/foo and C:/bar. + + # on Win32 only + my $provider = Template::Provider->new({ + INCLUDE_PATH => 'C:/Foo:C:/Bar' + }); + +However, if you're using Win32 then it's recommended that you +explicitly set the DELIMITER character to something else (e.g. ';') +rather than rely on this subtle magic. + + + + +=item ABSOLUTE + +The ABSOLUTE flag is used to indicate if templates specified with +absolute filenames (e.g. '/foo/bar') should be processed. It is +disabled by default and any attempt to load a template by such a +name will cause a 'file' exception to be raised. + + my $provider = Template::Provider->new({ + ABSOLUTE => 1, + }); + + # this is why it's disabled by default + [% INSERT /etc/passwd %] + +On Win32 systems, the regular expression for matching absolute +pathnames is tweaked slightly to also detect filenames that start +with a driver letter and colon, such as: + + C:/Foo/Bar + + + + + + +=item RELATIVE + +The RELATIVE flag is used to indicate if templates specified with +filenames relative to the current directory (e.g. './foo/bar' or +'../../some/where/else') should be loaded. It is also disabled by +default, and will raise a 'file' error if such template names are +encountered. + + my $provider = Template::Provider->new({ + RELATIVE => 1, + }); + + [% INCLUDE ../logs/error.log %] + + + + + +=item DEFAULT + +The DEFAULT option can be used to specify a default template which should +be used whenever a specified template can't be found in the INCLUDE_PATH. + + my $provider = Template::Provider->new({ + DEFAULT => 'notfound.html', + }); + +If a non-existant template is requested through the Template process() +method, or by an INCLUDE, PROCESS or WRAPPER directive, then the +DEFAULT template will instead be processed, if defined. Note that the +DEFAULT template is not used when templates are specified with +absolute or relative filenames, or as a reference to a input file +handle or text string. + + + + + +=item CACHE_SIZE + +The Template::Provider module caches compiled templates to avoid the need +to re-parse template files or blocks each time they are used. The CACHE_SIZE +option is used to limit the number of compiled templates that the module +should cache. + +By default, the CACHE_SIZE is undefined and all compiled templates are +cached. When set to any positive value, the cache will be limited to +storing no more than that number of compiled templates. When a new +template is loaded and compiled and the cache is full (i.e. the number +of entries == CACHE_SIZE), the least recently used compiled template +is discarded to make room for the new one. + +The CACHE_SIZE can be set to 0 to disable caching altogether. + + my $provider = Template::Provider->new({ + CACHE_SIZE => 64, # only cache 64 compiled templates + }); + + my $provider = Template::Provider->new({ + CACHE_SIZE => 0, # don't cache any compiled templates + }); + + + + + + +=item COMPILE_EXT + +From version 2 onwards, the Template Toolkit has the ability to +compile templates to Perl code and save them to disk for subsequent +use (i.e. cache persistence). The COMPILE_EXT option may be +provided to specify a filename extension for compiled template files. +It is undefined by default and no attempt will be made to read or write +any compiled template files. + + my $provider = Template::Provider->new({ + COMPILE_EXT => '.ttc', + }); + +If COMPILE_EXT is defined (and COMPILE_DIR isn't, see below) then compiled +template files with the COMPILE_EXT extension will be written to the same +directory from which the source template files were loaded. + +Compiling and subsequent reuse of templates happens automatically +whenever the COMPILE_EXT or COMPILE_DIR options are set. The Template +Toolkit will automatically reload and reuse compiled files when it +finds them on disk. If the corresponding source file has been modified +since the compiled version as written, then it will load and re-compile +the source and write a new compiled version to disk. + +This form of cache persistence offers significant benefits in terms of +time and resources required to reload templates. Compiled templates can +be reloaded by a simple call to Perl's require(), leaving Perl to handle +all the parsing and compilation. This is a Good Thing. + +=item COMPILE_DIR + +The COMPILE_DIR option is used to specify an alternate directory root +under which compiled template files should be saved. + + my $provider = Template::Provider->new({ + COMPILE_DIR => '/tmp/ttc', + }); + +The COMPILE_EXT option may also be specified to have a consistent file +extension added to these files. + + my $provider1 = Template::Provider->new({ + COMPILE_DIR => '/tmp/ttc', + COMPILE_EXT => '.ttc1', + }); + + my $provider2 = Template::Provider->new({ + COMPILE_DIR => '/tmp/ttc', + COMPILE_EXT => '.ttc2', + }); + + +When COMPILE_EXT is undefined, the compiled template files have the +same name as the original template files, but reside in a different +directory tree. + +Each directory in the INCLUDE_PATH is replicated in full beneath the +COMPILE_DIR directory. This example: + + my $provider = Template::Provider->new({ + COMPILE_DIR => '/tmp/ttc', + INCLUDE_PATH => '/home/abw/templates:/usr/share/templates', + }); + +would create the following directory structure: + + /tmp/ttc/home/abw/templates/ + /tmp/ttc/usr/share/templates/ + +Files loaded from different INCLUDE_PATH directories will have their +compiled forms save in the relevant COMPILE_DIR directory. + +On Win32 platforms a filename may by prefixed by a drive letter and +colon. e.g. + + C:/My Templates/header + +The colon will be silently stripped from the filename when it is added +to the COMPILE_DIR value(s) to prevent illegal filename being generated. +Any colon in COMPILE_DIR elements will be left intact. For example: + + # Win32 only + my $provider = Template::Provider->new({ + DELIMITER => ';', + COMPILE_DIR => 'C:/TT2/Cache', + INCLUDE_PATH => 'C:/TT2/Templates;D:/My Templates', + }); + +This would create the following cache directories: + + C:/TT2/Cache/C/TT2/Templates + C:/TT2/Cache/D/My Templates + + + + +=item TOLERANT + +The TOLERANT flag is used by the various Template Toolkit provider +modules (Template::Provider, Template::Plugins, Template::Filters) to +control their behaviour when errors are encountered. By default, any +errors are reported as such, with the request for the particular +resource (template, plugin, filter) being denied and an exception +raised. When the TOLERANT flag is set to any true values, errors will +be silently ignored and the provider will instead return +STATUS_DECLINED. This allows a subsequent provider to take +responsibility for providing the resource, rather than failing the +request outright. If all providers decline to service the request, +either through tolerated failure or a genuine disinclination to +comply, then a 'E<lt>resourceE<gt> not found' exception is raised. + + + + + + +=item PARSER + +The Template::Parser module implements a parser object for compiling +templates into Perl code which can then be executed. A default object +of this class is created automatically and then used by the +Template::Provider whenever a template is loaded and requires +compilation. The PARSER option can be used to provide a reference to +an alternate parser object. + + my $provider = Template::Provider->new({ + PARSER => MyOrg::Template::Parser->new({ ... }), + }); + + + +=item DEBUG + +The DEBUG option can be used to enable debugging messages from the +Template::Provider module by setting it to include the DEBUG_PROVIDER +value. + + use Template::Constants qw( :debug ); + + my $template = Template->new({ + DEBUG => DEBUG_PROVIDER, + }); + + + +=back + +=head2 fetch($name) + +Returns a compiled template for the name specified. If the template +cannot be found then (undef, STATUS_DECLINED) is returned. If an error +occurs (e.g. read error, parse error) then ($error, STATUS_ERROR) is +returned, where $error is the error message generated. If the TOLERANT +flag is set the the method returns (undef, STATUS_DECLINED) instead of +returning an error. + +=head2 store($name, $template) + +Stores the compiled template, $template, in the cache under the name, +$name. Susbequent calls to fetch($name) will return this template in +preference to any disk-based file. + +=head2 include_path(\@newpath)) + +Accessor method for the INCLUDE_PATH setting. If called with an +argument, this method will replace the existing INCLUDE_PATH with +the new value. + +=head2 paths() + +This method generates a copy of the INCLUDE_PATH list. Any elements in the +list which are dynamic generators (e.g. references to subroutines or objects +implementing a paths() method) will be called and the list of directories +returned merged into the output list. + +It is possible to provide a generator which returns itself, thus sending +this method into an infinite loop. To detect and prevent this from happening, +the C<$MAX_DIRS> package variable, set to 64 by default, limits the maximum +number of paths that can be added to, or generated for the output list. If +this number is exceeded then the method will immediately return an error +reporting as much. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.79, 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|Template>, L<Template::Parser|Template::Parser>, L<Template::Context|Template::Context> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Service.pm b/lib/Template/Service.pm new file mode 100644 index 0000000..fb5590a --- /dev/null +++ b/lib/Template/Service.pm @@ -0,0 +1,775 @@ +#============================================================= -*-Perl-*- +# +# Template::Service +# +# DESCRIPTION +# Module implementing a template processing service which wraps a +# template within PRE_PROCESS and POST_PROCESS templates and offers +# ERROR recovery. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Service.pm,v 2.75 2004/01/13 16:19:16 abw Exp $ +# +#============================================================================ + +package Template::Service; + +require 5.004; + +use strict; +use vars qw( $VERSION $DEBUG $ERROR ); +use base qw( Template::Base ); +use Template::Base; +use Template::Config; +use Template::Exception; +use Template::Constants; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.75 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0 unless defined $DEBUG; + + +#======================================================================== +# ----- PUBLIC METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# process($template, \%params) +# +# Process a template within a service framework. A service may encompass +# PRE_PROCESS and POST_PROCESS templates and an ERROR hash which names +# templates to be substituted for the main template document in case of +# error. Each service invocation begins by resetting the state of the +# context object via a call to reset(). The AUTO_RESET option may be set +# to 0 (default: 1) to bypass this step. +#------------------------------------------------------------------------ + +sub process { + my ($self, $template, $params) = @_; + my $context = $self->{ CONTEXT }; + my ($name, $output, $procout, $error); + $output = ''; + + $self->debug("process($template, ", + defined $params ? $params : '<no params>', + ')') if $self->{ DEBUG }; + + $context->reset() + if $self->{ AUTO_RESET }; + + # pre-request compiled template from context so that we can alias it + # in the stash for pre-processed templates to reference + eval { $template = $context->template($template) }; + return $self->error($@) + if $@; + + # localise the variable stash with any parameters passed + # and set the 'template' variable + $params ||= { }; + $params->{ template } = $template + unless ref $template eq 'CODE'; + $context->localise($params); + + SERVICE: { + # PRE_PROCESS + eval { + foreach $name (@{ $self->{ PRE_PROCESS } }) { + $self->debug("PRE_PROCESS: $name") if $self->{ DEBUG }; + $output .= $context->process($name); + } + }; + last SERVICE if ($error = $@); + + # PROCESS + eval { + foreach $name (@{ $self->{ PROCESS } || [ $template ] }) { + $self->debug("PROCESS: $name") if $self->{ DEBUG }; + $procout .= $context->process($name); + } + }; + if ($error = $@) { + last SERVICE + unless defined ($procout = $self->_recover(\$error)); + } + + if (defined $procout) { + # WRAPPER + eval { + foreach $name (reverse @{ $self->{ WRAPPER } }) { + $self->debug("WRAPPER: $name") if $self->{ DEBUG }; + $procout = $context->process($name, { content => $procout }); + } + }; + last SERVICE if ($error = $@); + $output .= $procout; + } + + # POST_PROCESS + eval { + foreach $name (@{ $self->{ POST_PROCESS } }) { + $self->debug("POST_PROCESS: $name") if $self->{ DEBUG }; + $output .= $context->process($name); + } + }; + last SERVICE if ($error = $@); + } + + $context->delocalise(); + delete $params->{ template }; + + if ($error) { +# $error = $error->as_string if ref $error; + return $self->error($error); + } + + return $output; +} + + +#------------------------------------------------------------------------ +# context() +# +# Returns the internal CONTEXT reference. +#------------------------------------------------------------------------ + +sub context { + return $_[0]->{ CONTEXT }; +} + + +#======================================================================== +# -- PRIVATE METHODS -- +#======================================================================== + +sub _init { + my ($self, $config) = @_; + my ($item, $data, $context, $block, $blocks); + my $delim = $config->{ DELIMITER }; + $delim = ':' unless defined $delim; + + # coerce PRE_PROCESS, PROCESS and POST_PROCESS to arrays if necessary, + # by splitting on non-word characters + foreach $item (qw( PRE_PROCESS PROCESS POST_PROCESS WRAPPER )) { + $data = $config->{ $item }; + $self->{ $item } = [ ], next unless (defined $data); + $data = [ split($delim, $data || '') ] + unless ref $data eq 'ARRAY'; + $self->{ $item } = $data; + } + # unset PROCESS option unless explicitly specified in config + $self->{ PROCESS } = undef + unless defined $config->{ PROCESS }; + + $self->{ ERROR } = $config->{ ERROR } || $config->{ ERRORS }; + $self->{ AUTO_RESET } = defined $config->{ AUTO_RESET } + ? $config->{ AUTO_RESET } : 1; + $self->{ DEBUG } = ( $config->{ DEBUG } || 0 ) + & Template::Constants::DEBUG_SERVICE; + + $context = $self->{ CONTEXT } = $config->{ CONTEXT } + || Template::Config->context($config) + || return $self->error(Template::Config->error); + + return $self; +} + + +#------------------------------------------------------------------------ +# _recover(\$exception) +# +# Examines the internal ERROR hash array to find a handler suitable +# for the exception object passed by reference. Selecting the handler +# is done by delegation to the exception's select_handler() method, +# passing the set of handler keys as arguments. A 'default' handler +# may also be provided. The handler value represents the name of a +# template which should be processed. +#------------------------------------------------------------------------ + +sub _recover { + my ($self, $error) = @_; + my $context = $self->{ CONTEXT }; + my ($hkey, $handler, $output); + + # there shouldn't ever be a non-exception object received at this + # point... unless a module like CGI::Carp messes around with the + # DIE handler. + return undef + unless (ref $$error); + + # a 'stop' exception is thrown by [% STOP %] - we return the output + # buffer stored in the exception object + return $$error->text() + if $$error->type() eq 'stop'; + + my $handlers = $self->{ ERROR } + || return undef; ## RETURN + + if (ref $handlers eq 'HASH') { + if ($hkey = $$error->select_handler(keys %$handlers)) { + $handler = $handlers->{ $hkey }; + $self->debug("using error handler for $hkey") if $self->{ DEBUG }; + } + elsif ($handler = $handlers->{ default }) { + # use default handler + $self->debug("using default error handler") if $self->{ DEBUG }; + } + else { + return undef; ## RETURN + } + } + else { + $handler = $handlers; + $self->debug("using default error handler") if $self->{ DEBUG }; + } + + eval { $handler = $context->template($handler) }; + if ($@) { + $$error = $@; + return undef; ## RETURN + }; + + $context->stash->set('error', $$error); + eval { + $output .= $context->process($handler); + }; + if ($@) { + $$error = $@; + return undef; ## RETURN + } + + return $output; +} + + + +#------------------------------------------------------------------------ +# _dump() +# +# Debug method which return a string representing the internal object +# state. +#------------------------------------------------------------------------ + +sub _dump { + my $self = shift; + my $context = $self->{ CONTEXT }->_dump(); + $context =~ s/\n/\n /gm; + + my $error = $self->{ ERROR }; + $error = join('', + "{\n", + (map { " $_ => $error->{ $_ }\n" } + keys %$error), + "}\n") + if ref $error; + + local $" = ', '; + return <<EOF; +$self +PRE_PROCESS => [ @{ $self->{ PRE_PROCESS } } ] +POST_PROCESS => [ @{ $self->{ POST_PROCESS } } ] +ERROR => $error +CONTEXT => $context +EOF +} + + +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::Service - General purpose template processing service + +=head1 SYNOPSIS + + use Template::Service; + + my $service = Template::Service->new({ + PRE_PROCESS => [ 'config', 'header' ], + POST_PROCESS => 'footer', + ERROR => { + user => 'user/index.html', + dbi => 'error/database', + default => 'error/default', + }, + }); + + my $output = $service->process($template_name, \%replace) + || die $service->error(), "\n"; + +=head1 DESCRIPTION + +The Template::Service module implements an object class for providing +a consistent template processing service. + +Standard header (PRE_PROCESS) and footer (POST_PROCESS) templates may +be specified which are prepended and appended to all templates +processed by the service (but not any other templates or blocks +INCLUDEd or PROCESSed from within). An ERROR hash may be specified +which redirects the service to an alternate template file in the case +of uncaught exceptions being thrown. This allows errors to be +automatically handled by the service and a guaranteed valid response +to be generated regardless of any processing problems encountered. + +A default Template::Service object is created by the Template module. +Any Template::Service options may be passed to the Template new() +constructor method and will be forwarded to the Template::Service +constructor. + + use Template; + + my $template = Template->new({ + PRE_PROCESS => 'header', + POST_PROCESS => 'footer', + }); + +Similarly, the Template::Service constructor will forward all configuration +parameters onto other default objects (e.g. Template::Context) that it may +need to instantiate. + +A Template::Service object (or subclass/derivative) can be explicitly +instantiated and passed to the Template new() constructor method as +the SERVICE item. + + use Template; + use Template::Service; + + my $service = Template::Service->new({ + PRE_PROCESS => 'header', + POST_PROCESS => 'footer', + }); + + my $template = Template->new({ + SERVICE => $service, + }); + +The Template::Service module can be sub-classed to create custom service +handlers. + + use Template; + use MyOrg::Template::Service; + + my $service = MyOrg::Template::Service->new({ + PRE_PROCESS => 'header', + POST_PROCESS => 'footer', + COOL_OPTION => 'enabled in spades', + }); + + my $template = Template->new({ + SERVICE => $service, + }); + +The Template module uses the Template::Config service() factory method +to create a default service object when required. The +$Template::Config::SERVICE package variable may be set to specify an +alternate service module. This will be loaded automatically and its +new() constructor method called by the service() factory method when +a default service object is required. Thus the previous example could +be written as: + + use Template; + + $Template::Config::SERVICE = 'MyOrg::Template::Service'; + + my $template = Template->new({ + PRE_PROCESS => 'header', + POST_PROCESS => 'footer', + COOL_OPTION => 'enabled in spades', + }); + +=head1 METHODS + +=head2 new(\%config) + +The new() constructor method is called to instantiate a Template::Service +object. Configuration parameters may be specified as a HASH reference or +as a list of (name =E<gt> value) pairs. + + my $service1 = Template::Service->new({ + PRE_PROCESS => 'header', + POST_PROCESS => 'footer', + }); + + my $service2 = Template::Service->new( ERROR => 'error.html' ); + +The new() method returns a Template::Service object (or sub-class) or +undef on error. In the latter case, a relevant error message can be +retrieved by the error() class method or directly from the +$Template::Service::ERROR package variable. + + my $service = Template::Service->new(\%config) + || die Template::Service->error(); + + my $service = Template::Service->new(\%config) + || die $Template::Service::ERROR; + +The following configuration items may be specified: + +=over 4 + + + + +=item PRE_PROCESS, POST_PROCESS + +These values may be set to contain the name(s) of template files +(relative to INCLUDE_PATH) which should be processed immediately +before and/or after each template. These do not get added to +templates processed into a document via directives such as INCLUDE, +PROCESS, WRAPPER etc. + + my $service = Template::Service->new({ + PRE_PROCESS => 'header', + POST_PROCESS => 'footer', + }; + +Multiple templates may be specified as a reference to a list. Each is +processed in the order defined. + + my $service = Template::Service->new({ + PRE_PROCESS => [ 'config', 'header' ], + POST_PROCESS => 'footer', + }; + +Alternately, multiple template may be specified as a single string, +delimited by ':'. This delimiter string can be changed via the +DELIMITER option. + + my $service = Template::Service->new({ + PRE_PROCESS => 'config:header', + POST_PROCESS => 'footer', + }; + +The PRE_PROCESS and POST_PROCESS templates are evaluated in the same +variable context as the main document and may define or update +variables for subsequent use. + +config: + + [% # set some site-wide variables + bgcolor = '#ffffff' + version = 2.718 + %] + +header: + + [% DEFAULT title = 'My Funky Web Site' %] + <html> + <head> + <title>[% title %]</title> + </head> + <body bgcolor="[% bgcolor %]"> + +footer: + + <hr> + Version [% version %] + </body> + </html> + +The Template::Document object representing the main template being processed +is available within PRE_PROCESS and POST_PROCESS templates as the 'template' +variable. Metadata items defined via the META directive may be accessed +accordingly. + + $service->process('mydoc.html', $vars); + +mydoc.html: + + [% META title = 'My Document Title' %] + blah blah blah + ... + +header: + + <html> + <head> + <title>[% template.title %]</title></head> + <body bgcolor="[% bgcolor %]"> + + + + + + + + + + + + + + +=item PROCESS + +The PROCESS option may be set to contain the name(s) of template files +(relative to INCLUDE_PATH) which should be processed instead of the +main template passed to the Template::Service process() method. This can +be used to apply consistent wrappers around all templates, similar to +the use of PRE_PROCESS and POST_PROCESS templates. + + my $service = Template::Service->new({ + PROCESS => 'content', + }; + + # processes 'content' instead of 'foo.html' + $service->process('foo.html'); + +A reference to the original template is available in the 'template' +variable. Metadata items can be inspected and the template can be +processed by specifying it as a variable reference (i.e. prefixed by +'$') to an INCLUDE, PROCESS or WRAPPER directive. + +content: + + <html> + <head> + <title>[% template.title %]</title> + </head> + + <body> + [% PROCESS $template %] + <hr> + © Copyright [% template.copyright %] + </body> + </html> + +foo.html: + + [% META + title = 'The Foo Page' + author = 'Fred Foo' + copyright = '2000 Fred Foo' + %] + <h1>[% template.title %]</h1> + Welcome to the Foo Page, blah blah blah + +output: + + <html> + <head> + <title>The Foo Page</title> + </head> + + <body> + <h1>The Foo Page</h1> + Welcome to the Foo Page, blah blah blah + <hr> + © Copyright 2000 Fred Foo + </body> + </html> + + + + + + + +=item ERROR + +The ERROR (or ERRORS if you prefer) configuration item can be used to +name a single template or specify a hash array mapping exception types +to templates which should be used for error handling. If an uncaught +exception is raised from within a template then the appropriate error +template will instead be processed. + +If specified as a single value then that template will be processed +for all uncaught exceptions. + + my $service = Template::Service->new({ + ERROR => 'error.html' + }); + +If the ERROR item is a hash reference the keys are assumed to be +exception types and the relevant template for a given exception will +be selected. A 'default' template may be provided for the general +case. Note that 'ERROR' can be pluralised to 'ERRORS' if you find +it more appropriate in this case. + + my $service = Template::Service->new({ + ERRORS => { + user => 'user/index.html', + dbi => 'error/database', + default => 'error/default', + }, + }); + +In this example, any 'user' exceptions thrown will cause the +'user/index.html' template to be processed, 'dbi' errors are handled +by 'error/database' and all others by the 'error/default' template. +Any PRE_PROCESS and/or POST_PROCESS templates will also be applied +to these error templates. + +Note that exception types are hierarchical and a 'foo' handler will +catch all 'foo.*' errors (e.g. foo.bar, foo.bar.baz) if a more +specific handler isn't defined. Be sure to quote any exception types +that contain periods to prevent Perl concatenating them into a single +string (i.e. C<user.passwd> is parsed as 'user'.'passwd'). + + my $service = Template::Service->new({ + ERROR => { + 'user.login' => 'user/login.html', + 'user.passwd' => 'user/badpasswd.html', + 'user' => 'user/index.html', + 'default' => 'error/default', + }, + }); + +In this example, any template processed by the $service object, or +other templates or code called from within, can raise a 'user.login' +exception and have the service redirect to the 'user/login.html' +template. Similarly, a 'user.passwd' exception has a specific +handling template, 'user/badpasswd.html', while all other 'user' or +'user.*' exceptions cause a redirection to the 'user/index.html' page. +All other exception types are handled by 'error/default'. + + +Exceptions can be raised in a template using the THROW directive, + + [% THROW user.login 'no user id: please login' %] + +or by calling the throw() method on the current Template::Context object, + + $context->throw('user.passwd', 'Incorrect Password'); + $context->throw('Incorrect Password'); # type 'undef' + +or from Perl code by calling die() with a Template::Exception object, + + die (Template::Exception->new('user.denied', 'Invalid User ID')); + +or by simply calling die() with an error string. This is +automagically caught and converted to an exception of 'undef' +type which can then be handled in the usual way. + + die "I'm sorry Dave, I can't do that"; + + + + + + + +=item AUTO_RESET + +The AUTO_RESET option is set by default and causes the local BLOCKS +cache for the Template::Context object to be reset on each call to the +Template process() method. This ensures that any BLOCKs defined +within a template will only persist until that template is finished +processing. This prevents BLOCKs defined in one processing request +from interfering with other independent requests subsequently +processed by the same context object. + +The BLOCKS item may be used to specify a default set of block definitions +for the Template::Context object. Subsequent BLOCK definitions in templates +will over-ride these but they will be reinstated on each reset if AUTO_RESET +is enabled (default), or if the Template::Context reset() method is called. + + + + + + + +=item DEBUG + +The DEBUG option can be used to enable debugging messages from the +Template::Service module by setting it to include the DEBUG_SERVICE +value. + + use Template::Constants qw( :debug ); + + my $template = Template->new({ + DEBUG => DEBUG_SERVICE, + }); + + + + +=back + +=head2 process($input, \%replace) + +The process() method is called to process a template specified as the first +parameter, $input. This may be a file name, file handle (e.g. GLOB or IO::Handle) +or a reference to a text string containing the template text. An additional +hash reference may be passed containing template variable definitions. + +The method processes the template, adding any PRE_PROCESS or POST_PROCESS +templates defined, and returns the output text. An uncaught exception thrown +by the template will be handled by a relevant ERROR handler if defined. +Errors that occur in the PRE_PROCESS or POST_PROCESS templates, or those that +occur in the main input template and aren't handled, cause the method to +return undef to indicate failure. The appropriate error message can be +retrieved via the error() method. + + $service->process('myfile.html', { title => 'My Test File' }) + || die $service->error(); + + +=head2 context() + +Returns a reference to the internal context object which is, by default, an +instance of the Template::Context class. + +=head2 error() + +Returns the most recent error message. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.79, 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|Template>, L<Template::Context|Template::Context> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Stash.pm b/lib/Template/Stash.pm new file mode 100644 index 0000000..64553fe --- /dev/null +++ b/lib/Template/Stash.pm @@ -0,0 +1,1040 @@ +#============================================================= -*-Perl-*- +# +# Template::Stash +# +# DESCRIPTION +# Definition of an object class which stores and manages access to +# variables for the Template Toolkit. +# +# AUTHOR +# Andy Wardley <abw@wardley.org> +# +# COPYRIGHT +# Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Stash.pm,v 2.85 2004/01/30 17:51:01 abw Exp $ +# +#============================================================================ + +package Template::Stash; + +require 5.004; + +use strict; +use vars qw( $VERSION $DEBUG $ROOT_OPS $SCALAR_OPS $HASH_OPS $LIST_OPS ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.85 $ =~ /(\d+)\.(\d+)/); + + +#======================================================================== +# -- PACKAGE VARIABLES AND SUBS -- +#======================================================================== + +#------------------------------------------------------------------------ +# Definitions of various pseudo-methods. ROOT_OPS are merged into all +# new Template::Stash objects, and are thus default global functions. +# SCALAR_OPS are methods that can be called on a scalar, and ditto +# respectively for LIST_OPS and HASH_OPS +#------------------------------------------------------------------------ + +$ROOT_OPS = { + 'inc' => sub { local $^W = 0; my $item = shift; ++$item }, + 'dec' => sub { local $^W = 0; my $item = shift; --$item }, +# import => \&hash_import, + defined $ROOT_OPS ? %$ROOT_OPS : (), +}; + +$SCALAR_OPS = { + 'item' => sub { $_[0] }, + 'list' => sub { [ $_[0] ] }, + 'hash' => sub { { value => $_[0] } }, + 'length' => sub { length $_[0] }, + 'size' => sub { return 1 }, + 'defined' => sub { return 1 }, + 'repeat' => sub { + my ($str, $count) = @_; + $str = '' unless defined $str; + return '' unless $count; + $count ||= 1; + return $str x $count; + }, + 'search' => sub { + my ($str, $pattern) = @_; + return $str unless defined $str and defined $pattern; + return $str =~ /$pattern/; + }, + 'replace' => sub { + my ($str, $search, $replace) = @_; + $replace = '' unless defined $replace; + return $str unless defined $str and defined $search; + $str =~ s/$search/$replace/g; +# print STDERR "s [ $search ] [ $replace ] g\n"; +# eval "\$str =~ s$search$replaceg"; + return $str; + }, + 'match' => sub { + my ($str, $search) = @_; + return $str unless defined $str and defined $search; + my @matches = ($str =~ /$search/); + return @matches ? \@matches : ''; + }, + 'split' => sub { + my ($str, $split, @args) = @_; + $str = '' unless defined $str; + return [ defined $split ? split($split, $str, @args) + : split(' ', $str, @args) ]; + }, + 'chunk' => sub { + my ($string, $size) = @_; + my @list; + $size ||= 1; + if ($size < 0) { + # sexeger! It's faster to reverse the string, search + # it from the front and then reverse the output than to + # search it from the end, believe it nor not! + $string = reverse $string; + $size = -$size; + unshift(@list, scalar reverse $1) + while ($string =~ /((.{$size})|(.+))/g); + } + else { + push(@list, $1) while ($string =~ /((.{$size})|(.+))/g); + } + return \@list; + }, + + + defined $SCALAR_OPS ? %$SCALAR_OPS : (), +}; + +$HASH_OPS = { + 'item' => sub { + my ($hash, $item) = @_; + $item = '' unless defined $item; + return if $item =~ /^[_.]/; + $hash->{ $item }; + }, + 'hash' => sub { $_[0] }, + 'size' => sub { scalar keys %{$_[0]} }, + 'keys' => sub { [ keys %{ $_[0] } ] }, + 'values' => sub { [ values %{ $_[0] } ] }, + 'each' => sub { [ %{ $_[0] } ] }, + 'list' => sub { + my ($hash, $what) = @_; $what ||= ''; + return ($what eq 'keys') ? [ keys %$hash ] + : ($what eq 'values') ? [ values %$hash ] + : ($what eq 'each') ? [ %$hash ] + : [ map { { key => $_ , value => $hash->{ $_ } } } + keys %$hash ]; + }, + 'exists' => sub { exists $_[0]->{ $_[1] } }, + 'defined' => sub { defined $_[0]->{ $_[1] } }, + 'import' => \&hash_import, + 'sort' => sub { + my ($hash) = @_; + [ sort { lc $hash->{$a} cmp lc $hash->{$b} } (keys %$hash) ]; + }, + 'nsort' => sub { + my ($hash) = @_; + [ sort { $hash->{$a} <=> $hash->{$b} } (keys %$hash) ]; + }, + defined $HASH_OPS ? %$HASH_OPS : (), +}; + +$LIST_OPS = { + 'item' => sub { $_[0]->[ $_[1] || 0 ] }, + 'list' => sub { $_[0] }, + 'hash' => sub { my $list = shift; my $n = 0; + return { map { ($n++, $_) } @$list }; }, + 'push' => sub { my $list = shift; push(@$list, shift); return '' }, + 'pop' => sub { my $list = shift; pop(@$list) }, + 'unshift' => sub { my $list = shift; unshift(@$list, shift); return '' }, + 'shift' => sub { my $list = shift; shift(@$list) }, + 'max' => sub { local $^W = 0; my $list = shift; $#$list; }, + 'size' => sub { local $^W = 0; my $list = shift; $#$list + 1; }, + 'first' => sub { + my $list = shift; + return $list->[0] unless @_; + return [ @$list[0..$_[0]-1] ]; + }, + 'last' => sub { + my $list = shift; + return $list->[-1] unless @_; + return [ @$list[-$_[0]..-1] ]; + }, + 'reverse' => sub { my $list = shift; [ reverse @$list ] }, + 'grep' => sub { + my ($list, $pattern) = @_; + $pattern ||= ''; + return [ grep /$pattern/, @$list ]; + }, + 'join' => sub { + my ($list, $joint) = @_; + join(defined $joint ? $joint : ' ', + map { defined $_ ? $_ : '' } @$list) + }, + 'sort' => sub { + $^W = 0; + my ($list, $field) = @_; + return $list unless @$list > 1; # no need to sort 1 item lists + return $field # Schwartzian Transform + ? map { $_->[0] } # for case insensitivity + sort { $a->[1] cmp $b->[1] } + map { [ $_, lc(ref($_) eq 'HASH' + ? $_->{ $field } : + UNIVERSAL::can($_, $field) + ? $_->$field() : $_) ] } + @$list + : map { $_->[0] } + sort { $a->[1] cmp $b->[1] } + map { [ $_, lc $_ ] } + @$list + }, + 'nsort' => sub { + my ($list, $field) = @_; + return $list unless $#$list; # no need to sort 1 item lists + return $field # Schwartzian Transform + ? map { $_->[0] } # for case insensitivity + sort { $a->[1] <=> $b->[1] } + map { [ $_, lc(ref($_) eq 'HASH' + ? $_->{ $field } : + UNIVERSAL::can($_, $field) + ? $_->$field() : $_) ] } + @$list + : map { $_->[0] } + sort { $a->[1] <=> $b->[1] } + map { [ $_, lc $_ ] } + @$list + }, + 'unique' => sub { my %u; [ grep { ++$u{$_} == 1 } @{$_[0]} ] }, + 'merge' => sub { + my $list = shift; + return [ @$list, grep defined, map ref eq 'ARRAY' ? @$_ : undef, @_ ]; + }, + 'slice' => sub { + my ($list, $from, $to) = @_; + $from ||= 0; + $to = $#$list unless defined $to; + return [ @$list[$from..$to] ]; + }, + 'splice' => sub { + my ($list, $offset, $length, @replace) = @_; + if (@replace) { + # @replace can contain a list of multiple replace items, or + # be a single reference to a list + @replace = @{ $replace[0] } + if @replace == 1 && ref $replace[0] eq 'ARRAY'; + return [ splice @$list, $offset, $length, @replace ]; + } + elsif (defined $length) { + return [ splice @$list, $offset, $length ]; + } + elsif (defined $offset) { + return [ splice @$list, $offset ]; + } + else { + return [ splice(@$list) ]; + } + }, + + defined $LIST_OPS ? %$LIST_OPS : (), +}; + +sub hash_import { + my ($hash, $imp) = @_; + $imp = {} unless ref $imp eq 'HASH'; + @$hash{ keys %$imp } = values %$imp; + return ''; +} + + +#------------------------------------------------------------------------ +# define_vmethod($type, $name, \&sub) +# +# Defines a virtual method of type $type (SCALAR, HASH, or LIST), with +# name $name, that invokes &sub when called. It is expected that &sub +# be able to handle the type that it will be called upon. +#------------------------------------------------------------------------ + +sub define_vmethod { + my ($class, $type, $name, $sub) = @_; + my $op; + $type = lc $type; + + if ($type =~ /^scalar|item$/) { + $op = $SCALAR_OPS; + } + elsif ($type eq 'hash') { + $op = $HASH_OPS; + } + elsif ($type =~ /^list|array$/) { + $op = $LIST_OPS; + } + else { + die "invalid vmethod type: $type\n"; + } + + $op->{ $name } = $sub; + + return 1; +} + + +#======================================================================== +# ----- CLASS METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# new(\%params) +# +# Constructor method which creates a new Template::Stash object. +# An optional hash reference may be passed containing variable +# definitions that will be used to initialise the stash. +# +# Returns a reference to a newly created Template::Stash. +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $params = ref $_[0] eq 'HASH' ? shift(@_) : { @_ }; + + my $self = { + global => { }, + %$params, + %$ROOT_OPS, + '_PARENT' => undef, + }; + + bless $self, $class; +} + + +#======================================================================== +# ----- PUBLIC OBJECT METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# clone(\%params) +# +# Creates a copy of the current stash object to effect localisation +# of variables. The new stash is blessed into the same class as the +# parent (which may be a derived class) and has a '_PARENT' member added +# which contains a reference to the parent stash that created it +# ($self). This member is used in a successive declone() method call to +# return the reference to the parent. +# +# A parameter may be provided which should reference a hash of +# variable/values which should be defined in the new stash. The +# update() method is called to define these new variables in the cloned +# stash. +# +# Returns a reference to a cloned Template::Stash. +#------------------------------------------------------------------------ + +sub clone { + my ($self, $params) = @_; + $params ||= { }; + + # look out for magical 'import' argument which imports another hash + my $import = $params->{ import }; + if (defined $import && UNIVERSAL::isa($import, 'HASH')) { + delete $params->{ import }; + } + else { + undef $import; + } + + my $clone = bless { + %$self, # copy all parent members + %$params, # copy all new data + '_PARENT' => $self, # link to parent + }, ref $self; + + # perform hash import if defined + &{ $HASH_OPS->{ import }}($clone, $import) + if defined $import; + + return $clone; +} + + +#------------------------------------------------------------------------ +# declone($export) +# +# Returns a reference to the PARENT stash. When called in the following +# manner: +# $stash = $stash->declone(); +# the reference count on the current stash will drop to 0 and be "freed" +# and the caller will be left with a reference to the parent. This +# contains the state of the stash before it was cloned. +#------------------------------------------------------------------------ + +sub declone { + my $self = shift; + $self->{ _PARENT } || $self; +} + + +#------------------------------------------------------------------------ +# get($ident) +# +# Returns the value for an variable stored in the stash. The variable +# may be specified as a simple string, e.g. 'foo', or as an array +# reference representing compound variables. In the latter case, each +# pair of successive elements in the list represent a node in the +# compound variable. The first is the variable name, the second a +# list reference of arguments or 0 if undefined. So, the compound +# variable [% foo.bar('foo').baz %] would be represented as the list +# [ 'foo', 0, 'bar', ['foo'], 'baz', 0 ]. Returns the value of the +# identifier or an empty string if undefined. Errors are thrown via +# die(). +#------------------------------------------------------------------------ + +sub get { + my ($self, $ident, $args) = @_; + my ($root, $result); + $root = $self; + + if (ref $ident eq 'ARRAY' + || ($ident =~ /\./) + && ($ident = [ map { s/\(.*$//; ($_, 0) } split(/\./, $ident) ])) { + my $size = $#$ident; + + # if $ident is a list reference, then we evaluate each item in the + # identifier against the previous result, using the root stash + # ($self) as the first implicit 'result'... + + foreach (my $i = 0; $i <= $size; $i += 2) { + $result = $self->_dotop($root, @$ident[$i, $i+1]); + last unless defined $result; + $root = $result; + } + } + else { + $result = $self->_dotop($root, $ident, $args); + } + + return defined $result ? $result : $self->undefined($ident, $args); +} + + +#------------------------------------------------------------------------ +# set($ident, $value, $default) +# +# Updates the value for a variable in the stash. The first parameter +# should be the variable name or array, as per get(). The second +# parameter should be the intended value for the variable. The third, +# optional parameter is a flag which may be set to indicate 'default' +# mode. When set true, the variable will only be updated if it is +# currently undefined or has a false value. The magical 'IMPORT' +# variable identifier may be used to indicate that $value is a hash +# reference whose values should be imported. Returns the value set, +# or an empty string if not set (e.g. default mode). In the case of +# IMPORT, returns the number of items imported from the hash. +#------------------------------------------------------------------------ + +sub set { + my ($self, $ident, $value, $default) = @_; + my ($root, $result, $error); + + $root = $self; + + ELEMENT: { + if (ref $ident eq 'ARRAY' + || ($ident =~ /\./) + && ($ident = [ map { s/\(.*$//; ($_, 0) } + split(/\./, $ident) ])) { + + # a compound identifier may contain multiple elements (e.g. + # foo.bar.baz) and we must first resolve all but the last, + # using _dotop() with the $lvalue flag set which will create + # intermediate hashes if necessary... + my $size = $#$ident; + foreach (my $i = 0; $i < $size - 2; $i += 2) { + $result = $self->_dotop($root, @$ident[$i, $i+1], 1); + last ELEMENT unless defined $result; + $root = $result; + } + + # then we call _assign() to assign the value to the last element + $result = $self->_assign($root, @$ident[$size-1, $size], + $value, $default); + } + else { + $result = $self->_assign($root, $ident, 0, $value, $default); + } + } + + return defined $result ? $result : ''; +} + + +#------------------------------------------------------------------------ +# getref($ident) +# +# Returns a "reference" to a particular item. This is represented as a +# closure which will return the actual stash item when called. +# WARNING: still experimental! +#------------------------------------------------------------------------ + +sub getref { + my ($self, $ident, $args) = @_; + my ($root, $item, $result); + $root = $self; + + if (ref $ident eq 'ARRAY') { + my $size = $#$ident; + + foreach (my $i = 0; $i <= $size; $i += 2) { + ($item, $args) = @$ident[$i, $i + 1]; + last if $i >= $size - 2; # don't evaluate last node + last unless defined + ($root = $self->_dotop($root, $item, $args)); + } + } + else { + $item = $ident; + } + + if (defined $root) { + return sub { my @args = (@{$args||[]}, @_); + $self->_dotop($root, $item, \@args); + } + } + else { + return sub { '' }; + } +} + + + + +#------------------------------------------------------------------------ +# update(\%params) +# +# Update multiple variables en masse. No magic is performed. Simple +# variable names only. +#------------------------------------------------------------------------ + +sub update { + my ($self, $params) = @_; + + # look out for magical 'import' argument to import another hash + my $import = $params->{ import }; + if (defined $import && UNIVERSAL::isa($import, 'HASH')) { + @$self{ keys %$import } = values %$import; + delete $params->{ import }; + } + + @$self{ keys %$params } = values %$params; +} + + +#------------------------------------------------------------------------ +# undefined($ident, $args) +# +# Method called when a get() returns an undefined value. Can be redefined +# in a subclass to implement alternate handling. +#------------------------------------------------------------------------ + +sub undefined { + my ($self, $ident, $args); + return ''; +} + + +#======================================================================== +# ----- PRIVATE OBJECT METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# _dotop($root, $item, \@args, $lvalue) +# +# This is the core 'dot' operation method which evaluates elements of +# variables against their root. All variables have an implicit root +# which is the stash object itself (a hash). Thus, a non-compound +# variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is +# '(stash.)foo.bar'. The first parameter is a reference to the current +# root, initially the stash itself. The second parameter contains the +# name of the variable element, e.g. 'foo'. The third optional +# parameter is a reference to a list of any parenthesised arguments +# specified for the variable, which are passed to sub-routines, object +# methods, etc. The final parameter is an optional flag to indicate +# if this variable is being evaluated on the left side of an assignment +# (e.g. foo.bar.baz = 10). When set true, intermediated hashes will +# be created (e.g. bar) if necessary. +# +# Returns the result of evaluating the item against the root, having +# performed any variable "magic". The value returned can then be used +# as the root of the next _dotop() in a compound sequence. Returns +# undef if the variable is undefined. +#------------------------------------------------------------------------ + +sub _dotop { + my ($self, $root, $item, $args, $lvalue) = @_; + my $rootref = ref $root; + my $atroot = ($root eq $self); + my ($value, @result); + + $args ||= [ ]; + $lvalue ||= 0; + +# print STDERR "_dotop(root=$root, item=$item, args=[@$args])\n" +# if $DEBUG; + + # return undef without an error if either side of the dot is unviable + # or if an attempt is made to access a private member, starting _ or . + return undef + unless defined($root) and defined($item) and $item !~ /^[\._]/; + + if ($atroot || $rootref eq 'HASH') { + # if $root is a regular HASH or a Template::Stash kinda HASH (the + # *real* root of everything). We first lookup the named key + # in the hash, or create an empty hash in its place if undefined + # and the $lvalue flag is set. Otherwise, we check the HASH_OPS + # pseudo-methods table, calling the code if found, or return undef. + + if (defined($value = $root->{ $item })) { + return $value unless ref $value eq 'CODE'; ## RETURN + @result = &$value(@$args); ## @result + } + elsif ($lvalue) { + # we create an intermediate hash if this is an lvalue + return $root->{ $item } = { }; ## RETURN + } + # ugly hack: only allow import vmeth to be called on root stash + elsif (($value = $HASH_OPS->{ $item }) + && ! $atroot || $item eq 'import') { + @result = &$value($root, @$args); ## @result + } + elsif ( ref $item eq 'ARRAY' ) { + # hash slice + return [@$root{@$item}]; ## RETURN + } + } + elsif ($rootref eq 'ARRAY') { + # if root is an ARRAY then we check for a LIST_OPS pseudo-method + # (except for l-values for which it doesn't make any sense) + # or return the numerical index into the array, or undef + + if (($value = $LIST_OPS->{ $item }) && ! $lvalue) { + @result = &$value($root, @$args); ## @result + } + elsif ($item =~ /^-?\d+$/) { + $value = $root->[$item]; + return $value unless ref $value eq 'CODE'; ## RETURN + @result = &$value(@$args); ## @result + } + elsif ( ref $item eq 'ARRAY' ) { + # array slice + return [@$root[@$item]]; ## RETURN + } + } + + # NOTE: we do the can-can because UNIVSERAL::isa($something, 'UNIVERSAL') + # doesn't appear to work with CGI, returning true for the first call + # and false for all subsequent calls. + + elsif (ref($root) && UNIVERSAL::can($root, 'can')) { + + # if $root is a blessed reference (i.e. inherits from the + # UNIVERSAL object base class) then we call the item as a method. + # If that fails then we try to fallback on HASH behaviour if + # possible. + eval { @result = $root->$item(@$args); }; + + if ($@) { + # temporary hack - required to propogate errors thrown + # by views; if $@ is a ref (e.g. Template::Exception + # object then we assume it's a real error that needs + # real throwing + + die $@ if ref($@) || ($@ !~ /Can't locate object method/); + + # failed to call object method, so try some fallbacks +# patch from Stephen Howard +# -- remove from here... -- + if (UNIVERSAL::isa($root, 'HASH') + && defined($value = $root->{ $item })) { + return $value unless ref $value eq 'CODE'; ## RETURN + @result = &$value(@$args); + } +# -- and replace with this... -- +# if (UNIVERSAL::isa($root, 'HASH') ) { +# if( defined($value = $root->{ $item })) { +# return $value unless ref $value eq 'CODE'; ## RETURN +# @result = &$value(@$args); +# } +# elsif ($value = $HASH_OPS->{ $item }) { +# @result = &$value($root, @$args); +# } +# } +# -- remove from here... -- + elsif (UNIVERSAL::isa($root, 'ARRAY') + && ($value = $LIST_OPS->{ $item })) { + @result = &$value($root, @$args); + } +# -- and replace with this... -- +# elsif (UNIVERSAL::isa($root, 'ARRAY') ) { +# if( $value = $LIST_OPS->{ $item }) { +# @result = &$value($root, @$args); +# } +# elsif( $item =~ /^-?\d+$/ ) { +# $value = $root->[$item]; +# return $value unless ref $value eq 'CODE'; ## RETURN +# @result = &$value(@$args); ## @result +# } +# elsif ( ref $item eq 'ARRAY' ) { +# # array slice +# return [@$root[@$item]]; ## RETURN +# } +# } +# -- end -- + elsif ($value = $SCALAR_OPS->{ $item }) { + @result = &$value($root, @$args); + } + elsif ($value = $LIST_OPS->{ $item }) { + @result = &$value([$root], @$args); + } + elsif ($self->{ _DEBUG }) { + @result = (undef, $@); + } + } + } + elsif (($value = $SCALAR_OPS->{ $item }) && ! $lvalue) { + # at this point, it doesn't look like we've got a reference to + # anything we know about, so we try the SCALAR_OPS pseudo-methods + # table (but not for l-values) + @result = &$value($root, @$args); ## @result + } + elsif (($value = $LIST_OPS->{ $item }) && ! $lvalue) { + # last-ditch: can we promote a scalar to a one-element + # list and apply a LIST_OPS virtual method? + @result = &$value([$root], @$args); + } + elsif ($self->{ _DEBUG }) { + die "don't know how to access [ $root ].$item\n"; ## DIE + } + else { + @result = (); + } + + # fold multiple return items into a list unless first item is undef + if (defined $result[0]) { + return ## RETURN + scalar @result > 1 ? [ @result ] : $result[0]; + } + elsif (defined $result[1]) { + die $result[1]; ## DIE + } + elsif ($self->{ _DEBUG }) { + die "$item is undefined\n"; ## DIE + } + + return undef; +} + + +#------------------------------------------------------------------------ +# _assign($root, $item, \@args, $value, $default) +# +# Similar to _dotop() above, but assigns a value to the given variable +# instead of simply returning it. The first three parameters are the +# root item, the item and arguments, as per _dotop(), followed by the +# value to which the variable should be set and an optional $default +# flag. If set true, the variable will only be set if currently false +# (undefined/zero) +#------------------------------------------------------------------------ + +sub _assign { + my ($self, $root, $item, $args, $value, $default) = @_; + my $rootref = ref $root; + my $atroot = ($root eq $self); + my $result; + $args ||= [ ]; + $default ||= 0; + +# print(STDERR "_assign(root=$root, item=$item, args=[@$args], \n", +# "value=$value, default=$default)\n") +# if $DEBUG; + + # return undef without an error if either side of the dot is unviable + # or if an attempt is made to update a private member, starting _ or . + return undef ## RETURN + unless $root and defined $item and $item !~ /^[\._]/; + + if ($rootref eq 'HASH' || $atroot) { +# if ($item eq 'IMPORT' && UNIVERSAL::isa($value, 'HASH')) { +# # import hash entries into root hash +# @$root{ keys %$value } = values %$value; +# return ''; ## RETURN +# } + # if the root is a hash we set the named key + return ($root->{ $item } = $value) ## RETURN + unless $default && $root->{ $item }; + } + elsif ($rootref eq 'ARRAY' && $item =~ /^-?\d+$/) { + # or set a list item by index number + return ($root->[$item] = $value) ## RETURN + unless $default && $root->{ $item }; + } + elsif (UNIVERSAL::isa($root, 'UNIVERSAL')) { + # try to call the item as a method of an object + + return $root->$item(@$args, $value) ## RETURN + unless $default && $root->$item(); + +# 2 issues: +# - method call should be wrapped in eval { } +# - fallback on hash methods if object method not found +# +# eval { $result = $root->$item(@$args, $value); }; +# +# if ($@) { +# die $@ if ref($@) || ($@ !~ /Can't locate object method/); +# +# # failed to call object method, so try some fallbacks +# if (UNIVERSAL::isa($root, 'HASH') && exists $root->{ $item }) { +# $result = ($root->{ $item } = $value) +# unless $default && $root->{ $item }; +# } +# } +# return $result; ## RETURN + + } + else { + die "don't know how to assign to [$root].[$item]\n"; ## DIE + } + + return undef; +} + + +#------------------------------------------------------------------------ +# _dump() +# +# Debug method which returns a string representing the internal state +# of the object. The method calls itself recursively to dump sub-hashes. +#------------------------------------------------------------------------ + +sub _dump { + my $self = shift; + return "[Template::Stash] " . $self->_dump_frame(2); +} + +sub _dump_frame { + my ($self, $indent) = @_; + $indent ||= 1; + my $buffer = ' '; + my $pad = $buffer x $indent; + my $text = "{\n"; + local $" = ', '; + + my ($key, $value); + + return $text . "...excessive recursion, terminating\n" + if $indent > 32; + + foreach $key (keys %$self) { + $value = $self->{ $key }; + $value = '<undef>' unless defined $value; + next if $key =~ /^\./; + if (ref($value) eq 'ARRAY') { + $value = '[ ' . join(', ', map { defined $_ ? $_ : '<undef>' } + @$value) . ' ]'; + } + elsif (ref $value eq 'HASH') { + $value = _dump_frame($value, $indent + 1); + } + + $text .= sprintf("$pad%-16s => $value\n", $key); + } + $text .= $buffer x ($indent - 1) . '}'; + return $text; +} + + +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::Stash - Magical storage for template variables + +=head1 SYNOPSIS + + use Template::Stash; + + my $stash = Template::Stash->new(\%vars); + + # get variable values + $value = $stash->get($variable); + $value = $stash->get(\@compound); + + # set variable value + $stash->set($variable, $value); + $stash->set(\@compound, $value); + + # default variable value + $stash->set($variable, $value, 1); + $stash->set(\@compound, $value, 1); + + # set variable values en masse + $stash->update(\%new_vars) + + # methods for (de-)localising variables + $stash = $stash->clone(\%new_vars); + $stash = $stash->declone(); + +=head1 DESCRIPTION + +The Template::Stash module defines an object class which is used to store +variable values for the runtime use of the template processor. Variable +values are stored internally in a hash reference (which itself is blessed +to create the object) and are accessible via the get() and set() methods. + +Variables may reference hash arrays, lists, subroutines and objects +as well as simple values. The stash automatically performs the right +magic when dealing with variables, calling code or object methods, +indexing into lists, hashes, etc. + +The stash has clone() and declone() methods which are used by the +template processor to make temporary copies of the stash for +localising changes made to variables. + +=head1 PUBLIC METHODS + +=head2 new(\%params) + +The new() constructor method creates and returns a reference to a new +Template::Stash object. + + my $stash = Template::Stash->new(); + +A hash reference may be passed to provide variables and values which +should be used to initialise the stash. + + my $stash = Template::Stash->new({ var1 => 'value1', + var2 => 'value2' }); + +=head2 get($variable) + +The get() method retrieves the variable named by the first parameter. + + $value = $stash->get('var1'); + +Dotted compound variables can be retrieved by specifying the variable +elements by reference to a list. Each node in the variable occupies +two entries in the list. The first gives the name of the variable +element, the second is a reference to a list of arguments for that +element, or 0 if none. + + [% foo.bar(10).baz(20) %] + + $stash->get([ 'foo', 0, 'bar', [ 10 ], 'baz', [ 20 ] ]); + +=head2 set($variable, $value, $default) + +The set() method sets the variable name in the first parameter to the +value specified in the second. + + $stash->set('var1', 'value1'); + +If the third parameter evaluates to a true value, the variable is +set only if it did not have a true value before. + + $stash->set('var2', 'default_value', 1); + +Dotted compound variables may be specified as per get() above. + + [% foo.bar = 30 %] + + $stash->set([ 'foo', 0, 'bar', 0 ], 30); + +The magical variable 'IMPORT' can be specified whose corresponding +value should be a hash reference. The contents of the hash array are +copied (i.e. imported) into the current namespace. + + # foo.bar = baz, foo.wiz = waz + $stash->set('foo', { 'bar' => 'baz', 'wiz' => 'waz' }); + + # import 'foo' into main namespace: foo = baz, wiz = waz + $stash->set('IMPORT', $stash->get('foo')); + +=head2 clone(\%params) + +The clone() method creates and returns a new Template::Stash object which +represents a localised copy of the parent stash. Variables can be +freely updated in the cloned stash and when declone() is called, the +original stash is returned with all its members intact and in the +same state as they were before clone() was called. + +For convenience, a hash of parameters may be passed into clone() which +is used to update any simple variable (i.e. those that don't contain any +namespace elements like 'foo' and 'bar' but not 'foo.bar') variables while +cloning the stash. For adding and updating complex variables, the set() +method should be used after calling clone(). This will correctly resolve +and/or create any necessary namespace hashes. + +A cloned stash maintains a reference to the stash that it was copied +from in its '_PARENT' member. + +=head2 declone() + +The declone() method returns the '_PARENT' reference and can be used to +restore the state of a stash as described above. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.85, 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|Template>, L<Template::Context|Template::Context> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Stash/Context.pm b/lib/Template/Stash/Context.pm new file mode 100644 index 0000000..975dc79 --- /dev/null +++ b/lib/Template/Stash/Context.pm @@ -0,0 +1,791 @@ +#============================================================= -*-Perl-*- +# +# Template::Stash::Context +# +# DESCRIPTION +# This is an alternate stash object which includes a patch from +# Craig Barratt to implement various new virtual methods to allow +# dotted template variable to denote if object methods and subroutines +# should be called in scalar or list context. It adds a little overhead +# to each stash call and I'm a little wary of doing that. So for now, +# it's implemented as a separate stash module which will allow us to +# test it out, benchmark it and switch it in or out as we require. +# +# This is what Craig has to say about it: +# +# Here's a better set of features for the core. Attached is a new version +# of Stash.pm (based on TT2.02) that: +# +# - supports the special op "scalar" that forces scalar context on +# function calls, eg: +# +# cgi.param("foo").scalar +# +# calls cgi.param("foo") in scalar context (unlike my wimpy +# scalar op from last night). Array context is the default. +# +# With non-function operands, scalar behaves like the perl +# version (eg: no-op for scalar, size for arrays, etc). +# +# - supports the special op "ref" that behaves like the perl ref. +# If applied to a function the function is not called. Eg: +# +# cgi.param("foo").ref +# +# does *not* call cgi.param and evaluates to "CODE". Similarly, +# HASH.ref, ARRAY.ref return what you expect. +# +# - adds a new scalar and list op called "array" that is a no-op for +# arrays and promotes scalars to one-element arrays. +# +# - allows scalar ops to be applied to arrays and hashes in place, +# eg: ARRAY.repeat(3) repeats each element in place. +# +# - allows list ops to be applied to scalars by promoting the scalars +# to one-element arrays (like an implicit "array"). So you can +# do things like SCALAR.size, SCALAR.join and get a useful result. +# +# This also means you can now use x.0 to safely get the first element +# whether x is an array or scalar. +# +# The new Stash.pm passes the TT2.02 test suite. But I haven't tested the +# new features very much. One nagging implementation problem is that the +# "scalar" and "ref" ops have higher precedence than user variable names. +# +# AUTHORS +# Andy Wardley <abw@kfs.org> +# Craig Barratt <craig@arraycomm.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Context.pm,v 1.58 2004/01/13 16:21:52 abw Exp $ +# +#============================================================================ + +package Template::Stash::Context; + +require 5.004; + +use strict; +use Template::Stash; +use vars qw( $VERSION $DEBUG $ROOT_OPS $SCALAR_OPS $HASH_OPS $LIST_OPS ); + +$VERSION = sprintf("%d.%02d", q$Revision: 1.58 $ =~ /(\d+)\.(\d+)/); + + +#======================================================================== +# -- PACKAGE VARIABLES AND SUBS -- +#======================================================================== + +#------------------------------------------------------------------------ +# copy virtual methods from those in the regular Template::Stash +#------------------------------------------------------------------------ + +$ROOT_OPS = { + %$Template::Stash::ROOT_OPS, + defined $ROOT_OPS ? %$ROOT_OPS : (), +}; + +$SCALAR_OPS = { + %$Template::Stash::SCALAR_OPS, + 'array' => sub { return [$_[0]] }, + defined $SCALAR_OPS ? %$SCALAR_OPS : (), +}; + +$LIST_OPS = { + %$Template::Stash::LIST_OPS, + 'array' => sub { return $_[0] }, + defined $LIST_OPS ? %$LIST_OPS : (), +}; + +$HASH_OPS = { + %$Template::Stash::HASH_OPS, + defined $HASH_OPS ? %$HASH_OPS : (), +}; + + + +#======================================================================== +# ----- CLASS METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# new(\%params) +# +# Constructor method which creates a new Template::Stash object. +# An optional hash reference may be passed containing variable +# definitions that will be used to initialise the stash. +# +# Returns a reference to a newly created Template::Stash. +#------------------------------------------------------------------------ + +sub new { + my $class = shift; + my $params = ref $_[0] eq 'HASH' ? shift(@_) : { @_ }; + + my $self = { + global => { }, + %$params, + %$ROOT_OPS, + '_PARENT' => undef, + }; + + bless $self, $class; +} + + +#======================================================================== +# ----- PUBLIC OBJECT METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# clone(\%params) +# +# Creates a copy of the current stash object to effect localisation +# of variables. The new stash is blessed into the same class as the +# parent (which may be a derived class) and has a '_PARENT' member added +# which contains a reference to the parent stash that created it +# ($self). This member is used in a successive declone() method call to +# return the reference to the parent. +# +# A parameter may be provided which should reference a hash of +# variable/values which should be defined in the new stash. The +# update() method is called to define these new variables in the cloned +# stash. +# +# Returns a reference to a cloned Template::Stash. +#------------------------------------------------------------------------ + +sub clone { + my ($self, $params) = @_; + $params ||= { }; + + # look out for magical 'import' argument which imports another hash + my $import = $params->{ import }; + if (defined $import && UNIVERSAL::isa($import, 'HASH')) { + delete $params->{ import }; + } + else { + undef $import; + } + + my $clone = bless { + %$self, # copy all parent members + %$params, # copy all new data + '_PARENT' => $self, # link to parent + }, ref $self; + + # perform hash import if defined + &{ $HASH_OPS->{ import }}($clone, $import) + if defined $import; + + return $clone; +} + + +#------------------------------------------------------------------------ +# declone($export) +# +# Returns a reference to the PARENT stash. When called in the following +# manner: +# $stash = $stash->declone(); +# the reference count on the current stash will drop to 0 and be "freed" +# and the caller will be left with a reference to the parent. This +# contains the state of the stash before it was cloned. +#------------------------------------------------------------------------ + +sub declone { + my $self = shift; + $self->{ _PARENT } || $self; +} + + +#------------------------------------------------------------------------ +# get($ident) +# +# Returns the value for an variable stored in the stash. The variable +# may be specified as a simple string, e.g. 'foo', or as an array +# reference representing compound variables. In the latter case, each +# pair of successive elements in the list represent a node in the +# compound variable. The first is the variable name, the second a +# list reference of arguments or 0 if undefined. So, the compound +# variable [% foo.bar('foo').baz %] would be represented as the list +# [ 'foo', 0, 'bar', ['foo'], 'baz', 0 ]. Returns the value of the +# identifier or an empty string if undefined. Errors are thrown via +# die(). +#------------------------------------------------------------------------ + +sub get { + my ($self, $ident, $args) = @_; + my ($root, $result); + $root = $self; + + if (ref $ident eq 'ARRAY' + || ($ident =~ /\./) + && ($ident = [ map { s/\(.*$//; ($_, 0) } split(/\./, $ident) ])) { + my $size = $#$ident; + + # if $ident is a list reference, then we evaluate each item in the + # identifier against the previous result, using the root stash + # ($self) as the first implicit 'result'... + + foreach (my $i = 0; $i <= $size; $i += 2) { + if ( $i + 2 <= $size && ($ident->[$i+2] eq "scalar" + || $ident->[$i+2] eq "ref") ) { + $result = $self->_dotop($root, @$ident[$i, $i+1], 0, + $ident->[$i+2]); + $i += 2; + } else { + $result = $self->_dotop($root, @$ident[$i, $i+1]); + } + last unless defined $result; + $root = $result; + } + } + else { + $result = $self->_dotop($root, $ident, $args); + } + + return defined $result ? $result : ''; +} + + +#------------------------------------------------------------------------ +# set($ident, $value, $default) +# +# Updates the value for a variable in the stash. The first parameter +# should be the variable name or array, as per get(). The second +# parameter should be the intended value for the variable. The third, +# optional parameter is a flag which may be set to indicate 'default' +# mode. When set true, the variable will only be updated if it is +# currently undefined or has a false value. The magical 'IMPORT' +# variable identifier may be used to indicate that $value is a hash +# reference whose values should be imported. Returns the value set, +# or an empty string if not set (e.g. default mode). In the case of +# IMPORT, returns the number of items imported from the hash. +#------------------------------------------------------------------------ + +sub set { + my ($self, $ident, $value, $default) = @_; + my ($root, $result, $error); + + $root = $self; + + ELEMENT: { + if (ref $ident eq 'ARRAY' + || ($ident =~ /\./) + && ($ident = [ map { s/\(.*$//; ($_, 0) } + split(/\./, $ident) ])) { + + # a compound identifier may contain multiple elements (e.g. + # foo.bar.baz) and we must first resolve all but the last, + # using _dotop() with the $lvalue flag set which will create + # intermediate hashes if necessary... + my $size = $#$ident; + foreach (my $i = 0; $i < $size - 2; $i += 2) { + $result = $self->_dotop($root, @$ident[$i, $i+1], 1); + last ELEMENT unless defined $result; + $root = $result; + } + + # then we call _assign() to assign the value to the last element + $result = $self->_assign($root, @$ident[$size-1, $size], + $value, $default); + } + else { + $result = $self->_assign($root, $ident, 0, $value, $default); + } + } + + return defined $result ? $result : ''; +} + + +#------------------------------------------------------------------------ +# getref($ident) +# +# Returns a "reference" to a particular item. This is represented as a +# closure which will return the actual stash item when called. +# WARNING: still experimental! +#------------------------------------------------------------------------ + +sub getref { + my ($self, $ident, $args) = @_; + my ($root, $item, $result); + $root = $self; + + if (ref $ident eq 'ARRAY') { + my $size = $#$ident; + + foreach (my $i = 0; $i <= $size; $i += 2) { + ($item, $args) = @$ident[$i, $i + 1]; + last if $i >= $size - 2; # don't evaluate last node + last unless defined + ($root = $self->_dotop($root, $item, $args)); + } + } + else { + $item = $ident; + } + + if (defined $root) { + return sub { my @args = (@{$args||[]}, @_); + $self->_dotop($root, $item, \@args); + } + } + else { + return sub { '' }; + } +} + + + + +#------------------------------------------------------------------------ +# update(\%params) +# +# Update multiple variables en masse. No magic is performed. Simple +# variable names only. +#------------------------------------------------------------------------ + +sub update { + my ($self, $params) = @_; + + # look out for magical 'import' argument to import another hash + my $import = $params->{ import }; + if (defined $import && UNIVERSAL::isa($import, 'HASH')) { + @$self{ keys %$import } = values %$import; + delete $params->{ import }; + } + + @$self{ keys %$params } = values %$params; +} + + +#======================================================================== +# ----- PRIVATE OBJECT METHODS ----- +#======================================================================== + +#------------------------------------------------------------------------ +# _dotop($root, $item, \@args, $lvalue, $nextItem) +# +# This is the core 'dot' operation method which evaluates elements of +# variables against their root. All variables have an implicit root +# which is the stash object itself (a hash). Thus, a non-compound +# variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is +# '(stash.)foo.bar'. The first parameter is a reference to the current +# root, initially the stash itself. The second parameter contains the +# name of the variable element, e.g. 'foo'. The third optional +# parameter is a reference to a list of any parenthesised arguments +# specified for the variable, which are passed to sub-routines, object +# methods, etc. The final parameter is an optional flag to indicate +# if this variable is being evaluated on the left side of an assignment +# (e.g. foo.bar.baz = 10). When set true, intermediated hashes will +# be created (e.g. bar) if necessary. +# +# Returns the result of evaluating the item against the root, having +# performed any variable "magic". The value returned can then be used +# as the root of the next _dotop() in a compound sequence. Returns +# undef if the variable is undefined. +#------------------------------------------------------------------------ + +sub _dotop { + my ($self, $root, $item, $args, $lvalue, $nextItem) = @_; + my $rootref = ref $root; + my ($value, @result, $ret, $retVal); + $nextItem ||= ""; + my $scalarContext = 1 if ( $nextItem eq "scalar" ); + my $returnRef = 1 if ( $nextItem eq "ref" ); + + $args ||= [ ]; + $lvalue ||= 0; + +# print STDERR "_dotop(root=$root, item=$item, args=[@$args])\n" +# if $DEBUG; + + # return undef without an error if either side of the dot is unviable + # or if an attempt is made to access a private member, starting _ or . + return undef + unless defined($root) and defined($item) and $item !~ /^[\._]/; + + if (ref(\$root) eq "SCALAR" && !$lvalue && + (($value = $LIST_OPS->{ $item }) || $item =~ /^-?\d+$/) ) { + # + # Promote scalar to one element list, to be processed below. + # + $rootref = 'ARRAY'; + $root = [$root]; + } + if ($rootref eq __PACKAGE__ || $rootref eq 'HASH') { + + # if $root is a regular HASH or a Template::Stash kinda HASH (the + # *real* root of everything). We first lookup the named key + # in the hash, or create an empty hash in its place if undefined + # and the $lvalue flag is set. Otherwise, we check the HASH_OPS + # pseudo-methods table, calling the code if found, or return undef. + + if (defined($value = $root->{ $item })) { + ($ret, $retVal, @result) = _dotop_return($value, $args, $returnRef, + $scalarContext); + return $retVal if ( $ret ); ## RETURN + } + elsif ($lvalue) { + # we create an intermediate hash if this is an lvalue + return $root->{ $item } = { }; ## RETURN + } + elsif ($value = $HASH_OPS->{ $item }) { + @result = &$value($root, @$args); ## @result + } + elsif (ref $item eq 'ARRAY') { + # hash slice + return [@$root{@$item}]; ## RETURN + } + elsif ($value = $SCALAR_OPS->{ $item }) { + # + # Apply scalar ops to every hash element, in place. + # + foreach my $key ( keys %$root ) { + $root->{$key} = &$value($root->{$key}, @$args); + } + } + } + elsif ($rootref eq 'ARRAY') { + + # if root is an ARRAY then we check for a LIST_OPS pseudo-method + # (except for l-values for which it doesn't make any sense) + # or return the numerical index into the array, or undef + + if (($value = $LIST_OPS->{ $item }) && ! $lvalue) { + @result = &$value($root, @$args); ## @result + } + elsif (($value = $SCALAR_OPS->{ $item }) && ! $lvalue) { + # + # Apply scalar ops to every array element, in place. + # + for ( my $i = 0 ; $i < @$root ; $i++ ) { + $root->[$i] = &$value($root->[$i], @$args); ## @result + } + } + elsif ($item =~ /^-?\d+$/) { + $value = $root->[$item]; + ($ret, $retVal, @result) = _dotop_return($value, $args, $returnRef, + $scalarContext); + return $retVal if ( $ret ); ## RETURN + } + elsif (ref $item eq 'ARRAY' ) { + # array slice + return [@$root[@$item]]; ## RETURN + } + } + + # NOTE: we do the can-can because UNIVSERAL::isa($something, 'UNIVERSAL') + # doesn't appear to work with CGI, returning true for the first call + # and false for all subsequent calls. + + elsif (ref($root) && UNIVERSAL::can($root, 'can')) { + + # if $root is a blessed reference (i.e. inherits from the + # UNIVERSAL object base class) then we call the item as a method. + # If that fails then we try to fallback on HASH behaviour if + # possible. + return ref $root->can($item) if ( $returnRef ); ## RETURN + eval { + @result = $scalarContext ? scalar $root->$item(@$args) + : $root->$item(@$args); ## @result + }; + + if ($@) { + # failed to call object method, so try some fallbacks + if (UNIVERSAL::isa($root, 'HASH') + && defined($value = $root->{ $item })) { + ($ret, $retVal, @result) = _dotop_return($value, $args, + $returnRef, $scalarContext); + return $retVal if ( $ret ); ## RETURN + } + elsif (UNIVERSAL::isa($root, 'ARRAY') + && ($value = $LIST_OPS->{ $item })) { + @result = &$value($root, @$args); + } + else { + @result = (undef, $@); + } + } + } + elsif (($value = $SCALAR_OPS->{ $item }) && ! $lvalue) { + + # at this point, it doesn't look like we've got a reference to + # anything we know about, so we try the SCALAR_OPS pseudo-methods + # table (but not for l-values) + + @result = &$value($root, @$args); ## @result + } + elsif ($self->{ _DEBUG }) { + die "don't know how to access [ $root ].$item\n"; ## DIE + } + else { + @result = (); + } + + # fold multiple return items into a list unless first item is undef + if (defined $result[0]) { + return ref(@result > 1 ? [ @result ] : $result[0]) + if ( $returnRef ); ## RETURN + if ( $scalarContext ) { + return scalar @result if ( @result > 1 ); ## RETURN + return scalar(@{$result[0]}) if ( ref $result[0] eq "ARRAY" ); + return scalar(%{$result[0]}) if ( ref $result[0] eq "HASH" ); + return $result[0]; ## RETURN + } else { + return @result > 1 ? [ @result ] : $result[0]; ## RETURN + } + } + elsif (defined $result[1]) { + die $result[1]; ## DIE + } + elsif ($self->{ _DEBUG }) { + die "$item is undefined\n"; ## DIE + } + + return undef; +} + +#------------------------------------------------------------------------ +# ($ret, $retVal, @result) = _dotop_return($value, $args, $returnRef, +# $scalarContext); +# +# Handle the various return processing for _dotop +#------------------------------------------------------------------------ +sub _dotop_return +{ + my($value, $args, $returnRef, $scalarContext) = @_; + my(@result); + + return (1, ref $value) if ( $returnRef ); ## RETURN + if ( $scalarContext ) { + return (1, scalar(@$value)) if ref $value eq 'ARRAY'; ## RETURN + return (1, scalar(%$value)) if ref $value eq 'HASH'; ## RETURN + return (1, scalar($value)) unless ref $value eq 'CODE'; ## RETURN; + @result = scalar &$value(@$args) ## @result; + } else { + return (1, $value) unless ref $value eq 'CODE'; ## RETURN + @result = &$value(@$args); ## @result + } + return (0, undef, @result); +} + + +#------------------------------------------------------------------------ +# _assign($root, $item, \@args, $value, $default) +# +# Similar to _dotop() above, but assigns a value to the given variable +# instead of simply returning it. The first three parameters are the +# root item, the item and arguments, as per _dotop(), followed by the +# value to which the variable should be set and an optional $default +# flag. If set true, the variable will only be set if currently false +# (undefined/zero) +#------------------------------------------------------------------------ + +sub _assign { + my ($self, $root, $item, $args, $value, $default) = @_; + my $rootref = ref $root; + my $result; + $args ||= [ ]; + $default ||= 0; + +# print(STDERR "_assign(root=$root, item=$item, args=[@$args], \n", +# "value=$value, default=$default)\n") +# if $DEBUG; + + # return undef without an error if either side of the dot is unviable + # or if an attempt is made to update a private member, starting _ or . + return undef ## RETURN + unless $root and defined $item and $item !~ /^[\._]/; + + if ($rootref eq 'HASH' || $rootref eq __PACKAGE__) { +# if ($item eq 'IMPORT' && UNIVERSAL::isa($value, 'HASH')) { +# # import hash entries into root hash +# @$root{ keys %$value } = values %$value; +# return ''; ## RETURN +# } + # if the root is a hash we set the named key + return ($root->{ $item } = $value) ## RETURN + unless $default && $root->{ $item }; + } + elsif ($rootref eq 'ARRAY' && $item =~ /^-?\d+$/) { + # or set a list item by index number + return ($root->[$item] = $value) ## RETURN + unless $default && $root->{ $item }; + } + elsif (UNIVERSAL::isa($root, 'UNIVERSAL')) { + # try to call the item as a method of an object + return $root->$item(@$args, $value); ## RETURN + } + else { + die "don't know how to assign to [$root].[$item]\n"; ## DIE + } + + return undef; +} + + +#------------------------------------------------------------------------ +# _dump() +# +# Debug method which returns a string representing the internal state +# of the object. The method calls itself recursively to dump sub-hashes. +#------------------------------------------------------------------------ + +sub _dump { + my $self = shift; + my $indent = shift || 1; + my $buffer = ' '; + my $pad = $buffer x $indent; + my $text = ''; + local $" = ', '; + + my ($key, $value); + + + return $text . "...excessive recursion, terminating\n" + if $indent > 32; + + foreach $key (keys %$self) { + + $value = $self->{ $key }; + $value = '<undef>' unless defined $value; + + if (ref($value) eq 'ARRAY') { + $value = "$value [@$value]"; + } + $text .= sprintf("$pad%-8s => $value\n", $key); + next if $key =~ /^\./; + if (UNIVERSAL::isa($value, 'HASH')) { + $text .= _dump($value, $indent + 1); + } + } + $text; +} + + +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::Stash::Context - Experimetal stash allowing list/scalar context definition + +=head1 SYNOPSIS + + use Template; + use Template::Stash::Context; + + my $stash = Template::Stash::Context->new(\%vars); + my $tt2 = Template->new({ STASH => $stash }); + +=head1 DESCRIPTION + +This is an alternate stash object which includes a patch from +Craig Barratt to implement various new virtual methods to allow +dotted template variable to denote if object methods and subroutines +should be called in scalar or list context. It adds a little overhead +to each stash call and I'm a little wary of applying that to the core +default stash without investigating the effects first. So for now, +it's implemented as a separate stash module which will allow us to +test it out, benchmark it and switch it in or out as we require. + +This is what Craig has to say about it: + +Here's a better set of features for the core. Attached is a new version +of Stash.pm (based on TT2.02) that: + +* supports the special op "scalar" that forces scalar context on +function calls, eg: + + cgi.param("foo").scalar + +calls cgi.param("foo") in scalar context (unlike my wimpy +scalar op from last night). Array context is the default. + +With non-function operands, scalar behaves like the perl +version (eg: no-op for scalar, size for arrays, etc). + +* supports the special op "ref" that behaves like the perl ref. +If applied to a function the function is not called. Eg: + + cgi.param("foo").ref + +does *not* call cgi.param and evaluates to "CODE". Similarly, +HASH.ref, ARRAY.ref return what you expect. + +* adds a new scalar and list op called "array" that is a no-op for +arrays and promotes scalars to one-element arrays. + +* allows scalar ops to be applied to arrays and hashes in place, +eg: ARRAY.repeat(3) repeats each element in place. + +* allows list ops to be applied to scalars by promoting the scalars +to one-element arrays (like an implicit "array"). So you can +do things like SCALAR.size, SCALAR.join and get a useful result. + +This also means you can now use x.0 to safely get the first element +whether x is an array or scalar. + +The new Stash.pm passes the TT2.02 test suite. But I haven't tested the +new features very much. One nagging implementation problem is that the +"scalar" and "ref" ops have higher precedence than user variable names. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +1.58, 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::Stash|Template::Stash> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Stash/XS.pm b/lib/Template/Stash/XS.pm new file mode 100644 index 0000000..2cfb543 --- /dev/null +++ b/lib/Template/Stash/XS.pm @@ -0,0 +1,176 @@ +#============================================================= -*-Perl-*- +# +# Template::Stash::XS +# +# DESCRIPTION +# +# Perl bootstrap for XS module. Inherits methods from +# Template::Stash when not implemented in the XS module. +# +#======================================================================== + +package Template::Stash::XS; + +use Template; +use Template::Stash; + +BEGIN { + require DynaLoader; + @Template::Stash::XS::ISA = qw( DynaLoader Template::Stash ); + + eval { + bootstrap Template::Stash::XS $Template::VERSION; + }; + if ($@) { + die "Couldn't load Template::Stash::XS $Template::VERSION:\n\n$@\n"; + } +} + + +sub DESTROY { + # no op + 1; +} + + +# catch missing method calls here so perl doesn't barf +# trying to load *.al files +sub AUTOLOAD { + my ($self, @args) = @_; + my @c = caller(0); + my $auto = $AUTOLOAD; + + $auto =~ s/.*:://; + $self =~ s/=.*//; + + die "Can't locate object method \"$auto\"" . + " via package \"$self\" at $c[1] line $c[2]\n"; +} + +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::Stash::XS - Experimetal high-speed stash written in XS + +=head1 SYNOPSIS + + use Template; + use Template::Stash::XS; + + my $stash = Template::Stash::XS->new(\%vars); + my $tt2 = Template->new({ STASH => $stash }); + +=head1 DESCRIPTION + +This module loads the XS version of Template::Stash::XS. It should +behave very much like the old one, but run about twice as fast. +See the synopsis above for usage information. + +Only a few methods (such as get and set) have been implemented in XS. +The others are inherited from Template::Stash. + +=head1 NOTE + +To always use the XS version of Stash, modify the Template/Config.pm +module near line 45: + + $STASH = 'Template::Stash::XS'; + +If you make this change, then there is no need to explicitly create +an instance of Template::Stash::XS as seen in the SYNOPSIS above. Just +use Template as normal. + +Alternatively, in your code add this line before creating a Template +object: + + $Template::Config::STASH = 'Template::Stash::XS'; + +To use the original, pure-perl version restore this line in +Template/Config.pm: + + $STASH = 'Template::Stash'; + +Or in your code: + + $Template::Config::STASH = 'Template::Stash'; + +You can elect to have this performed once for you at installation +time by answering 'y' or 'n' to the question that asks if you want +to make the XS Stash the default. + +=head1 BUGS + +Please report bugs to the Template Toolkit mailing list +templates@template-toolkit.org + +As of version 2.05 of the Template Toolkit, use of the XS Stash is +known to have 2 potentially troublesome side effects. The first +problem is that accesses to tied hashes (e.g. Apache::Session) may not +work as expected. This should be fixed in an imminent release. If +you are using tied hashes then it is suggested that you use the +regular Stash by default, or write a thin wrapper around your tied +hashes to enable the XS Stash to access items via regular method +calls. + +The second potential problem is that enabling the XS Stash causes all +the Template Toolkit modules to be installed in an architecture +dependant library, e.g. in + + /usr/lib/perl5/site_perl/5.6.0/i386-linux/Template + +instead of + + /usr/lib/perl5/site_perl/5.6.0/Template + +At the time of writing, we're not sure why this is happening but it's +likely that this is either a bug or intentional feature in the Perl +ExtUtils::MakeMaker module. As far as I know, Perl always checks the +architecture dependant directories before the architecture independant +ones. Therefore, a newer version of the Template Toolkit installed +with the XS Stash enabled should be used by Perl in preference to any +existing version using the regular stash. However, if you install a +future version of the Template Toolkit with the XS Stash disabled, you +may find that Perl continues to use the older version with XS Stash +enabled in preference. + +=head1 AUTHORS + +Andy Wardley E<lt>abw@tt2.orgE<gt> + +Doug Steinwand E<lt>dsteinwand@citysearch.comE<gt> + +=head1 VERSION + +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::Stash|Template::Stash> + diff --git a/lib/Template/Test.pm b/lib/Template/Test.pm new file mode 100644 index 0000000..9413d68 --- /dev/null +++ b/lib/Template/Test.pm @@ -0,0 +1,711 @@ +#============================================================= -*-Perl-*- +# +# Template::Test +# +# DESCRIPTION +# Module defining a test harness which processes template input and +# then compares the output against pre-define expected output. +# Generates test output compatible with Test::Harness. This was +# originally the t/texpect.pl script. +# +# AUTHOR +# Andy Wardley <abw@kfs.org> +# +# COPYRIGHT +# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2000 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. +# +#---------------------------------------------------------------------------- +# +# $Id: Test.pm,v 2.69 2004/01/13 16:19:16 abw Exp $ +# +#============================================================================ + +package Template::Test; + +require 5.004; + +use strict; +use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS + $VERSION $DEBUG $EXTRA $PRESERVE $REASON $NO_FLUSH + $loaded %callsign); +use Template qw( :template ); +use Exporter; + +$VERSION = sprintf("%d.%02d", q$Revision: 2.69 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0; +@ISA = qw( Exporter ); +@EXPORT = qw( ntests ok is match flush skip_all test_expect callsign banner ); +@EXPORT_OK = ( 'assert' ); +%EXPORT_TAGS = ( all => [ @EXPORT_OK, @EXPORT ] ); +$| = 1; + +$REASON = 'not applicable on this platform'; +$NO_FLUSH = 0; +$EXTRA = 0; # any extra tests to come after test_expect() +$PRESERVE = 0 # don't mangle newlines in output/expect + unless defined $PRESERVE; + +# always set binmode on Win32 machines so that any output generated +# is true to what we expect +$Template::BINMODE = ($^O eq 'MSWin32') ? 1 : 0; + +my @results = (); +my ($ntests, $ok_count); +*is = \&match; + +END { + # ensure flush() is called to print any cached results + flush(); +} + + +#------------------------------------------------------------------------ +# ntests($n) +# +# Declare how many (more) tests are expected to come. If ok() is called +# before ntests() then the results are cached instead of being printed +# to STDOUT. When ntests() is called, the total number of tests +# (including any cached) is known and the "1..$ntests" line can be +# printed along with the cached results. After that, calls to ok() +# generated printed output immediately. +#------------------------------------------------------------------------ + +sub ntests { + $ntests = shift; + # add any pre-declared extra tests, or pre-stored test @results, to + # the grand total of tests + $ntests += $EXTRA + scalar @results; + $ok_count = 1; + print $ntests ? "1..$ntests\n" : "1..$ntests # skipped: $REASON\n"; + # flush cached results + foreach my $pre_test (@results) { + ok(@$pre_test); + } +} + + +#------------------------------------------------------------------------ +# ok($truth, $msg) +# +# Tests the value passed for truth and generates an "ok $n" or "not ok $n" +# line accordingly. If ntests() hasn't been called then we cached +# results for later, instead. +#------------------------------------------------------------------------ + +sub ok { + my ($ok, $msg) = @_; + + # cache results if ntests() not yet called + unless ($ok_count) { + push(@results, [ $ok, $msg ]); + return $ok; + } + + $msg = defined $msg ? " - $msg" : ''; + if ($ok) { + print "ok ", $ok_count++, "$msg\n"; + } + else { + print STDERR "FAILED $ok_count: $msg\n" if defined $msg; + print "not ok ", $ok_count++, "$msg\n"; + } +} + + + +#------------------------------------------------------------------------ +# assert($truth, $error) +# +# Test value for truth, die if false. +#------------------------------------------------------------------------ + +sub assert { + my ($ok, $err) = @_; + return ok(1) if $ok; + + # failed + my ($pkg, $file, $line) = caller(); + $err ||= "assert failed"; + $err .= " at $file line $line\n"; + ok(0); + die $err; +} + +#------------------------------------------------------------------------ +# match( $result, $expect ) +#------------------------------------------------------------------------ + +sub match { + my ($result, $expect, $msg) = @_; + my $count = $ok_count ? $ok_count : scalar @results + 1; + + # force stringification of $result to avoid 'no eq method' overload errors + $result = "$result" if ref $result; + + if ($result eq $expect) { + return ok(1, $msg); + } + else { + print STDERR "FAILED $count:\n expect: [$expect]\n result: [$result]\n"; + return ok(0, $msg); + } +} + + +#------------------------------------------------------------------------ +# flush() +# +# Flush any tests results. +#------------------------------------------------------------------------ + +sub flush { + ntests(0) + unless $ok_count || $NO_FLUSH; +} + + +#------------------------------------------------------------------------ +# skip_all($reason) +# +# Skip all tests, setting $REASON to contain any message passed. Calls +# exit(0) which triggers flush() which generates a "1..0 # $REASON" +# string to keep to test harness happy. +#------------------------------------------------------------------------ + +sub skip_all { + $REASON = join('', @_); + exit(0); +} + + +#------------------------------------------------------------------------ +# test_expect($input, $template, \%replace) +# +# This is the main testing sub-routine. The $input parameter should be a +# text string or a filehandle reference (e.g. GLOB or IO::Handle) from +# which the input text can be read. The input should contain a number +# of tests which are split up and processed individually, comparing the +# generated output against the expected output. Tests should be defined +# as follows: +# +# -- test -- +# test input +# -- expect -- +# expected output +# +# -- test -- +# etc... +# +# The number of tests is determined and ntests() is called to generate +# the "0..$n" line compatible with Test::Harness. Each test input is +# then processed by the Template object passed as the second parameter, +# $template. This may also be a hash reference containing configuration +# which are used to instantiate a Template object, or may be left +# undefined in which case a default Template object will be instantiated. +# The third parameter, also optional, may be a reference to a hash array +# defining template variables. This is passed to the template process() +# method. +#------------------------------------------------------------------------ + +sub test_expect { + my ($src, $tproc, $params) = @_; + my ($input, @tests); + my ($output, $expect, $match); + my $count = 0; + my $ttprocs; + + # read input text + eval { + local $/ = undef; + $input = ref $src ? <$src> : $src; + }; + if ($@) { + ntests(1); ok(0); + warn "Cannot read input text from $src\n"; + return undef; + } + + # remove any comment lines + $input =~ s/^#.*?\n//gm; + + # remove anything before '-- start --' and/or after '-- stop --' + $input = $' if $input =~ /\s*--\s*start\s*--\s*/; + $input = $` if $input =~ /\s*--\s*stop\s*--\s*/; + + @tests = split(/^\s*--\s*test\s*--\s*\n/im, $input); + + # if the first line of the file was '--test--' (optional) then the + # first test will be empty and can be discarded + shift(@tests) if $tests[0] =~ /^\s*$/; + + ntests(3 + scalar(@tests) * 2); + + # first test is that Template loaded OK, which it did + ok(1, 'running test_expect()'); + + # optional second param may contain a Template reference or a HASH ref + # of constructor options, or may be undefined + if (ref($tproc) eq 'HASH') { + # create Template object using hash of config items + $tproc = Template->new($tproc) + || die Template->error(), "\n"; + } + elsif (ref($tproc) eq 'ARRAY') { + # list of [ name => $tproc, name => $tproc ], use first $tproc + $ttprocs = { @$tproc }; + $tproc = $tproc->[1]; + } + elsif (! ref $tproc) { + $tproc = Template->new() + || die Template->error(), "\n"; + } + # otherwise, we assume it's a Template reference + + # test: template processor created OK + ok($tproc, 'template processor is engaged'); + + # third test is that the input read ok, which it did + ok(1, 'input read and split into ' . scalar @tests . ' tests'); + + # the remaining tests are defined in @tests... + foreach $input (@tests) { + $count++; + my $name = ''; + + if ($input =~ s/^\s*-- name:? (.*?) --\s*\n//im) { + $name = $1; + } + else { + $name = "template text $count"; + } + + # split input by a line like "-- expect --" + ($input, $expect) = + split(/^\s*--\s*expect\s*--\s*\n/im, $input); + $expect = '' + unless defined $expect; + + $output = ''; + + # input text may be prefixed with "-- use name --" to indicate a + # Template object in the $ttproc hash which we should use + if ($input =~ s/^\s*--\s*use\s+(\S+)\s*--\s*\n//im) { + my $ttname = $1; + my $ttlookup; + if ($ttlookup = $ttprocs->{ $ttname }) { + $tproc = $ttlookup; + } + else { + warn "no such template object to use: $ttname\n"; + } + } + + # process input text + $tproc->process(\$input, $params, \$output) || do { + warn "Template process failed: ", $tproc->error(), "\n"; + # report failure and automatically fail the expect match + ok(0, "$name process FAILED: " . subtext($input)); + ok(0, '(obviously did not match expected)'); + next; + }; + + # processed OK + ok(1, "$name processed OK: " . subtext($input)); + + # another hack: if the '-- expect --' section starts with + # '-- process --' then we process the expected output + # before comparing it with the generated output. This is + # slightly twisted but it makes it possible to run tests + # where the expected output isn't static. See t/date.t for + # an example. + + if ($expect =~ s/^\s*--+\s*process\s*--+\s*\n//im) { + my $out; + $tproc->process(\$expect, $params, \$out) || do { + warn("Template process failed (expect): ", + $tproc->error(), "\n"); + # report failure and automatically fail the expect match + ok(0, "failed to process expected output [" + . subtext($expect) . ']'); + next; + }; + $expect = $out; + }; + + # strip any trailing blank lines from expected and real output + foreach ($expect, $output) { + s/\n*\Z//mg; + } + + $match = ($expect eq $output) ? 1 : 0; + if (! $match || $DEBUG) { + print "MATCH FAILED\n" + unless $match; + + my ($copyi, $copye, $copyo) = ($input, $expect, $output); + unless ($PRESERVE) { + foreach ($copyi, $copye, $copyo) { + s/\n/\\n/g; + } + } + printf(" input: [%s]\nexpect: [%s]\noutput: [%s]\n", + $copyi, $copye, $copyo); + } + + ok($match, $match ? "$name matched expected" : "$name did not match expected"); + }; +} + +#------------------------------------------------------------------------ +# callsign() +# +# Returns a hash array mapping lower a..z to their phonetic alphabet +# equivalent. +#------------------------------------------------------------------------ + +sub callsign { + my %callsign; + @callsign{ 'a'..'z' } = qw( + alpha bravo charlie delta echo foxtrot golf hotel india + juliet kilo lima mike november oscar papa quebec romeo + sierra tango umbrella victor whisky x-ray yankee zulu ); + return \%callsign; +} + + +#------------------------------------------------------------------------ +# banner($text) +# +# Prints a banner with the specified text if $DEBUG is set. +#------------------------------------------------------------------------ + +sub banner { + return unless $DEBUG; + my $text = join('', @_); + my $count = $ok_count ? $ok_count - 1 : scalar @results; + print "-" x 72, "\n$text ($count tests completed)\n", "-" x 72, "\n"; +} + + +sub subtext { + my $text = shift; + $text =~ s/\s*$//sg; + $text = substr($text, 0, 32) . '...' if length $text > 32; + $text =~ s/\n/\\n/g; + return $text; +} + + +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::Test - Module for automating TT2 test scripts + +=head1 SYNOPSIS + + use Template::Test; + + $Template::Test::DEBUG = 0; # set this true to see each test running + $Template::Test::EXTRA = 2; # 2 extra tests follow test_expect()... + + # ok() can be called any number of times before test_expect + ok( $true_or_false ) + + # test_expect() splits $input into individual tests, processes each + # and compares generated output against expected output + test_expect($input, $template, \%replace ); + + # $input is text or filehandle (e.g. DATA section after __END__) + test_expect( $text ); + test_expect( \*DATA ); + + # $template is a Template object or configuration hash + my $template_cfg = { ... }; + test_expect( $input, $template_cfg ); + my $template_obj = Template->new($template_cfg); + test_expect( $input, $template_obj ); + + # $replace is a hash reference of template variables + my $replace = { + a => 'alpha', + b => 'bravo' + }; + test_expect( $input, $template, $replace ); + + # ok() called after test_expect should be declared in $EXTRA (2) + ok( $true_or_false ) + ok( $true_or_false ) + +=head1 DESCRIPTION + +The Template::Test module defines the test_expect() and other related +subroutines which can be used to automate test scripts for the +Template Toolkit. See the numerous tests in the 't' sub-directory of +the distribution for examples of use. + +The test_expect() subroutine splits an input document into a number +of separate tests, processes each one using the Template Toolkit and +then compares the generated output against an expected output, also +specified in the input document. It generates the familiar "ok/not +ok" output compatible with Test::Harness. + +The test input should be specified as a text string or a reference to +a filehandle (e.g. GLOB or IO::Handle) from which it can be read. In +particular, this allows the test input to be placed after the __END__ +marker and read via the DATA filehandle. + + use Template::Test; + + test_expect(\*DATA); + + __END__ + # this is the first test (this is a comment) + -- test -- + blah blah blah [% foo %] + -- expect -- + blah blah blah value_of_foo + + # here's the second test (no surprise, so is this) + -- test -- + more blah blah [% bar %] + -- expect -- + more blah blah value_of_bar + +Blank lines between test sections are generally ignored. Any line starting +with '#' is treated as a comment and is ignored. + +The second and third parameters to test_expect() are optional. The second +may be either a reference to a Template object which should be used to +process the template fragments, or a reference to a hash array containing +configuration values which should be used to instantiate a new Template +object. + + # pass reference to config hash + my $config = { + INCLUDE_PATH => '/here/there:/every/where', + POST_CHOMP => 1, + }; + test_expect(\*DATA, $config); + + # or create Template object explicitly + my $template = Template->new($config); + test_expect(\*DATA, $template); + + +The third parameter may be used to reference a hash array of template +variable which should be defined when processing the tests. This is +passed to the Template process() method. + + my $replace = { + a => 'alpha', + b => 'bravo', + }; + + test_expect(\*DATA, $config, $replace); + +The second parameter may be left undefined to specify a default Template +configuration. + + test_expect(\*DATA, undef, $replace); + +For testing the output of different Template configurations, a +reference to a list of named Template objects also may be passed as +the second parameter. + + my $tt1 = Template->new({ ... }); + my $tt2 = Template->new({ ... }); + my @tts = [ one => $tt1, two => $tt1 ]; + +The first object in the list is used by default. Other objects may be +switched in with the '-- use $name --' marker. This should immediately +follow a '-- test --' line. That object will then be used for the rest +of the test, or until a different object is selected. + + -- test -- + -- use one -- + [% blah %] + -- expect -- + blah, blah + + -- test -- + still using one... + -- expect -- + ... + + -- test -- + -- use two -- + [% blah %] + -- expect -- + blah, blah, more blah + +The test_expect() sub counts the number of tests, and then calls ntests() +to generate the familiar "1..$ntests\n" test harness line. Each +test defined generates two test numbers. The first indicates +that the input was processed without error, and the second that the +output matches that expected. + +Additional test may be run before test_expect() by calling ok(). +These test results are cached until ntests() is called and the final +number of tests can be calculated. Then, the "1..$ntests" line is +output, along with "ok $n" / "not ok $n" lines for each of the cached +test result. Subsequent calls to ok() then generate an output line +immediately. + + my $something = SomeObject->new(); + ok( $something ); + + my $other = AnotherThing->new(); + ok( $other ); + + test_expect(\*DATA); + +If any tests are to follow after test_expect() is called then these +should be pre-declared by setting the $EXTRA package variable. This +value (default: 0) is added to the grand total calculated by ntests(). +The results of the additional tests are also registered by calling ok(). + + $Template::Test::EXTRA = 2; + + # can call ok() any number of times before test_expect() + ok( $did_that_work ); + ok( $make_sure ); + ok( $dead_certain ); + + # <some> number of tests... + test_expect(\*DATA, $config, $replace); + + # here's those $EXTRA tests + ok( defined $some_result && ref $some_result eq 'ARRAY' ); + ok( $some_result->[0] eq 'some expected value' ); + +If you don't want to call test_expect() at all then you can call +ntests($n) to declare the number of tests and generate the test +header line. After that, simply call ok() for each test passing +a true or false values to indicate that the test passed or failed. + + ntests(2); + ok(1); + ok(0); + +If you're really lazy, you can just call ok() and not bother declaring +the number of tests at all. All tests results will be cached until the +end of the script and then printed in one go before the program exits. + + ok( $x ); + ok( $y ); + +You can identify only a specific part of the input file for testing +using the '-- start --' and '-- stop --' markers. Anything before the +first '-- start --' is ignored, along with anything after the next +'-- stop --' marker. + + -- test -- + this is test 1 (not performed) + -- expect -- + this is test 1 (not performed) + + -- start -- + + -- test -- + this is test 2 + -- expect -- + this is test 2 + + -- stop -- + + ... + +For historical reasons and general utility, the module also defines a +'callsign' subroutine which returns a hash mapping a..z to their phonetic +alphabet equivalent (e.g. radio callsigns). This is used by many +of the test scripts as a "known source" of variable values. + + test_expect(\*DATA, $config, callsign()); + +A banner() subroutine is also provided which prints a simple banner +including any text passed as parameters, if $DEBUG is set. + + banner('Testing something-or-other'); + +example output: + + #------------------------------------------------------------ + # Testing something-or-other (27 tests completed) + #------------------------------------------------------------ + +The $DEBUG package variable can be set to enable debugging mode. + +The $PRESERVE package variable can be set to stop the test_expect() +from converting newlines in the output and expected output into +the literal strings '\n'. + +=head1 HISTORY + +This module started its butt-ugly life as the t/texpect.pl script. It +was cleaned up to became the Template::Test module some time around +version 0.29. It underwent further cosmetic surgery for version 2.00 +but still retains some rear-end resemblances. + +=head1 BUGS / KNOWN "FEATURES" + +Imports all methods by default. This is generally a Bad Thing, but +this module is only used in test scripts (i.e. at build time) so a) we +don't really care and b) it saves typing. + +The line splitter may be a bit dumb, especially if it sees lines like +-- this -- that aren't supposed to be special markers. So don't do that. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.69, 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|Template> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Tools/tpage.pod b/lib/Template/Tools/tpage.pod new file mode 100644 index 0000000..e2b20fa --- /dev/null +++ b/lib/Template/Tools/tpage.pod @@ -0,0 +1,76 @@ + +#------------------------------------------------------------------------ +# 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::Tools::tpage - Process templates from command line + +=head1 USAGE + + tpage [ --define var=value ] file(s) + +=head1 DESCRIPTION + +The B<tpage> script is a simple wrapper around the Template Toolkit processor. +Files specified by name on the command line are processed in turn by the +template processor and the resulting output is sent to STDOUT and can be +redirected accordingly. e.g. + + tpage myfile > myfile.out + tpage header myfile footer > myfile.html + +If no file names are specified on the command line then B<tpage> will read +STDIN for input. + +The C<--define> option can be used to set the values of template variables. +e.g. + + tpage --define author="Andy Wardley" skeleton.pm > MyModule.pm + +See L<Template> for general information about the Perl Template +Toolkit and the template language and features. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +2.69, 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<ttree|Template::Tools::ttree> + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Tools/ttree.pod b/lib/Template/Tools/ttree.pod new file mode 100644 index 0000000..8984c51 --- /dev/null +++ b/lib/Template/Tools/ttree.pod @@ -0,0 +1,332 @@ + +#------------------------------------------------------------------------ +# 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::Tools::ttree - Process entire directory trees of templates + +=head1 SYNOPSIS + + ttree [options] [files] + +=head1 DESCRIPTION + +The F<ttree> script is used to process entire directory trees containing +template files. The resulting output from processing each file is then +written to a corresponding file in a destination directory. The script +compares the modification times of source and destination files (where +they already exist) and processes only those files that have been modified. +In other words, it is the equivalent of 'make' for the Template Toolkit. + +It supports a number of options which can be used to configure +behaviour, define locations and set Template Toolkit options. The +script first reads the F<.ttreerc> configuration file in the HOME +directory, or an alternative file specified in the TTREERC environment +variable. Then, it processes any command line arguments, including +any additional configuration files specified via the C<-f> (file) +option. + +=head2 The F<.ttreerc> Configuration File + +When you run F<ttree> for the first time it will ask you if you want +it to create a F<.ttreerc> file for you. This will be created in your +home directory. + + $ ttree + Do you want me to create a sample '.ttreerc' file for you? + (file: /home/abw/.ttreerc) [y/n]: y + /home/abw/.ttreerc created. Please edit accordingly and re-run ttree + +The purpose of this file is to set any I<global> configuration options +that you want applied I<every> time F<ttree> is run. For example, you +can use the C<ignore> and C<copy> option to provide regular expressions +that specify which files should be ignored and which should be copied +rather than being processed as templates. You may also want to set +flags like C<verbose> and C<recurse> according to your preference. + +A minimal F<.ttreerc>: + + # ignore these files + ignore = \b(CVS|RCS)\b + ignore = ^# + ignore = ~$ + + # copy these files + copy = \.(gif|png|jpg|pdf)$ + + # recurse into directories + recurse + + # provide info about what's going on + verbose + +In most cases, you'll want to create a different F<ttree> configuration +file for each project you're working on. The C<cfg> option allows you +to specify a directory where F<ttree> can find further configuration +files. + + cfg = /home/abw/.ttree + +The C<-f> command line option can be used to specify which configuration +file should be used. You can specify a filename using an absolute or +relative path: + + $ ttree -f /home/abw/web/example/etc/ttree.cfg + $ ttree -f ./etc/ttree.cfg + $ ttree -f ../etc/ttree.cfg + +If the configuration file does not begin with C</> or C<.> or something +that looks like a MS-DOS absolute path (e.g. C<C:\\etc\\ttree.cfg>) then +F<ttree> will look for it in the directory specified by the C<cfg> option. + + $ ttree -f test1 # /home/abw/.ttree/test1 + +The C<cfg> option can only be used in the F<.ttreerc> file. All the +other options can be used in the F<.ttreerc> or any other F<ttree> +configuration file. They can all also be specified as command line +options. + +Remember that F<.ttreerc> is always processed I<before> any +configuration file specified with the C<-f> option. Certain options +like C<lib> can be used any number of times and accumulate their values. + +For example, consider the following configuration files: + +F</home/abw/.ttreerc>: + + cfg = /home/abw/.ttree + lib = /usr/local/tt2/templates + +F</home/abw/.ttree/myconfig>: + + lib = /home/abw/web/example/templates/lib + +When F<ttree> is invoked as follows: + + $ ttree -f myconfig + +the C<lib> option will be set to the following directories: + + /usr/local/tt2/templates + /home/abw/web/example/templates/lib + +Any templates located under F</usr/local/tt2/templates> will be used +in preference to those located under +F</home/abw/web/example/templates/lib>. This may be what you want, +but then again, it might not. For this reason, it is good practice to +keep the F<.ttreerc> as simple as possible and use different +configuration files for each F<ttree> project. + +=head2 Directory Options + +The C<src> option is used to define the directory containing the +source templates to be processed. It can be provided as a command +line option or in a configuration file as shown here: + + src = /home/abw/web/example/templates/src + +Each template in this directory typically corresponds to a single +web page or other document. + +The C<dest> option is used to specify the destination directory for the +generated output. + + dest = /home/abw/web/example/html + +The C<lib> option is used to define one or more directories containing +additional library templates. These templates are not documents in +their own right and typically comprise of smaller, modular components +like headers, footers and menus that are incorporated into pages templates. + + lib = /home/abw/web/example/templates/lib + lib = /usr/local/tt2/templates + +The C<lib> option can be used repeatedly to add further directories to +the search path. + +A list of templates can be passed to F<ttree> as command line arguments. + + $ ttree foo.html bar.html + +It looks for these templates in the C<src> directory and processes them +through the Template Toolkit, using any additional template components +from the C<lib> directories. The generated output is then written to +the corresponding file in the C<dest> directory. + +If F<ttree> is invoked without explicitly specifying any templates +to be processed then it will process every file in the C<src> directory. +If the C<-r> (recurse) option is set then it will additionally iterate +down through sub-directories and process and other template files it finds +therein. + + $ ttree -r + +If a template has been processed previously, F<ttree> will compare the +modification times of the source and destination files. If the source +template (or one it is dependant on) has not been modified more +recently than the generated output file then F<ttree> will not process +it. The F<-a> (all) option can be used to force F<ttree> to process +all files regardless of modification time. + + $ tree -a + +Any templates explicitly named as command line argument are always +processed and the modification time checking is bypassed. + +=head2 File Options + +The C<ignore>, C<copy> and C<accept> options are used to specify Perl +regexen to filter file names. Files that match any of the C<ignore> +options will not be processed. Remaining files that match any of the +C<copy> regexen will be copied to the destination directory. Remaining +files that then match any of the C<accept> criteria are then processed +via the Template Toolkit. If no C<accept> parameter is specified then +all files will be accepted for processing if not already copied or +ignored. + + # ignore these files + ignore = \b(CVS|RCS)\b + ignore = ^# + ignore = ~$ + + # copy these files + copy = \.(gif|png|jpg|pdf)$ + + # accept only .tt2 templates + accept = \.tt2$ + +The C<suffix> option is used to define mappings between the file +extensions for source templates and the generated output files. The +following example specifies that source templates with a C<.tt2> +suffix should be output as C<.html> files: + + suffix tt2=html + +Or on the command line, + + --suffix tt2=html + +You can provide any number of different suffix mappings by repeating +this option. + +=head2 Template Dependencies + +The C<depend> and C<depend_file> options allow you to specify +how any given template file depends on another file or group of files. +The C<depend> option is used to express a single dependency. + + $ ttree --depend foo=bar,baz + +This command line example shows the C<--depend> option being used to +specify that the F<foo> file is dependant on the F<bar> and F<baz> +templates. This option can be used many time on the command line: + + $ ttree --depend foo=bar,baz --depend crash=bang,wallop + +or in a configuration file: + + depend foo=bar,baz + depend crash=bang,wallop + +The file appearing on the left of the C<=> is specified relative to +the C<src> or C<lib> directories. The file(s) appearing on the right +can be specified relative to any of these directories or as absolute +file paths. + +For example: + + $ ttree --depend foo=bar,/tmp/baz + +To define a dependency that applies to all files, use C<*> on the +left of the C<=>. + + $ ttree --depend *=header,footer + +or in a configuration file: + + depend *=header,footer + +Any templates that are defined in the C<pre_process>, C<post_process>, +C<process> or C<wrapper> options will automatically be added to the +list of global dependencies that apply to all templates. + +The C<depend_file> option can be used to specify a file that contains +dependency information. + + $ ttree --depend_file=/home/abw/web/example/etc/ttree.dep + +Here is an example of a dependency file: + + # This is a comment. It is ignored. + + index.html: header footer menubar + + header: titlebar hotlinks + + menubar: menuitem + + # spanning multiple lines with the backslash + another.html: header footer menubar \ + sidebar searchform + +Lines beginning with the C<#> character are comments and are ignored. +Blank lines are also ignored. All other lines should provide a +filename followed by a colon and then a list of dependant files +separated by whitespace, commas or both. Whitespace around the colon +is also optional. Lines ending in the C<\> character are continued +onto the following line. + +Files that contain spaces can be quoted. That is only necessary +for files after the colon (':'). The file before the colon may be +quoted if it contains a colon. + +As with the command line options, the C<*> character can be used +as a wildcard to specify a dependency for all templates. + + * : config,header + +=head2 Template Toolkit Options + +F<ttree> also provides access to the usual range of Template Toolkit +options. For example, the C<--pre_chomp> and C<--post_chomp> F<ttree> +options correspond to the C<PRE_CHOMP> and C<POST_CHOMP> options. + +Run C<ttree -h> for a summary of the options available. + +=head1 AUTHORS + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + +With contributions from Dylan William Hardison (support for +dependencies), Bryce Harrington (C<absolute> and C<relative> options), +Mark Anderson (C<suffix> and C<debug> options), Harald Joerg and Leon +Brocard who gets everywhere, it seems. + +=head1 VERSION + +2.69, 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<tpage|Template::Tools::tpage> + diff --git a/lib/Template/Tutorial.pod b/lib/Template/Tutorial.pod new file mode 100644 index 0000000..b6c894f --- /dev/null +++ b/lib/Template/Tutorial.pod @@ -0,0 +1,109 @@ +#============================================================= -*-perl-*- +# +# Template::Tutorial +# +# DESCRIPTION +# This section includes tutorials on using the Template Toolkit. +# Subjects currently include an general overview of the Template +# Toolkit, showing users how to get quickly up to speed building web +# content, and a tutorial on generating and using data files, with +# particular reference to XML. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Tutorial - Template Toolkit Tutorials + +=head1 DESCRIPTION + +This section includes tutorials on using the Template Toolkit. Subjects +currently include an general overview of the Template Toolkit, showing +users how to get quickly up to speed building web content, and a +tutorial on generating and using data files, with particular reference +to XML. + +=over 4 + +=item L<Template::Tutorial::Web|Template::Tutorial::Web> + +Generating Web Content Using the Template Toolkit + +This tutorial provides an introduction to the Template Toolkit and a +"quick start" guide to getting up to speed. Its primarily focus is on +using the Template Toolkit to build web content and it covers 4 basic +areas: using tpage and ttree; using the Template.pm module in CGI +scripts; writing Apache/mod_perl handlers; and extending the toolkit by +writing plugins. + +=item L<Template::Tutorial::Datafile|Template::Tutorial::Datafile> + +Creating Data Output Files Using the Template Toolkit + +This tutorial gives an overview of the Template Toolkit, showing in +particular how to use it to read and write data files in various +different formats and styles. It was written by Dave Cross and first +appeared as a lead article at http://www.perl.com/ earlier in the year +(2001). + + + +=back + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Tutorial/Datafile.pod b/lib/Template/Tutorial/Datafile.pod new file mode 100644 index 0000000..652f79d --- /dev/null +++ b/lib/Template/Tutorial/Datafile.pod @@ -0,0 +1,461 @@ +#============================================================= -*-perl-*- +# +# Template::Tutorial::Datafile +# +# DESCRIPTION +# This tutorial gives an overview of the Template Toolkit, showing in +# particular how to use it to read and write data files in various +# different formats and styles. It was written by Dave Cross and +# first appeared as a lead article at http://www.perl.com/ earlier in +# the year (2001). +# +# AUTHOR +# Dave Cross <dave@dave.org.uk> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Tutorial::Datafile - Creating Data Output Files Using the Template Toolkit + +=head1 DESCRIPTION + +This tutorial gives an overview of the Template Toolkit, showing in +particular how to use it to read and write data files in various +different formats and styles. It was written by Dave Cross and first +appeared as a lead article at http://www.perl.com/ earlier in the year +(2001). + +=head1 Introducing the Template Toolkit + +There are a number of Perl modules that are universally +recognised as The Right Thing To Use for certain tasks. If you +accessed a database without using DBI, pulled data from the WWW +without using one of the LWP modules or parsed XML without using +XML::Parser or one of its subclasses then you'd run the risk of +being shunned by polite Perl society. + +I believe that the year 2000 saw the emergence of another 'must +have' Perl module - the Template Toolkit. I don't think I'm +alone in this belief as the Template Toolkit won the 'Best New +Module' award at the Perl Conference last summer. Version 2.0 of +the Template Toolkit (known as TT2 to its friends) was recently +released to the CPAN. + +TT2 was designed and written by Andy Wardley E<lt>abw@wardley.orgE<gt>. +It was born out of Andy's previous templating module, +Text::Metatext, in best Fred Brooks 'plan to throw one away' +manner; and aims to be the most useful (or, at least, the most +I<used>) Perl templating system. + +TT2 provides a way to take a file of fixed boilerplate text +(the template) and embed variable data within it. One obvious +use of this is in the creation of dynamic web pages and this is +where a lot of the attention that TT2 has received has been +focussed. In this article, I hope to demonstrate that TT2 is +just as useful in non-web applications. + +=head1 Using the Template Toolkit + +Let's look at how we'd use TT2 to process a simple data file. +TT2 is an object oriented Perl module. Having downloaded it from +CPAN and installed it in the usual manner, using it in your +program is as easy as putting the lines + + use Template; + my $tt = Template->new; + +in your code. The constructor function, C<new>, takes +a number of optional parameters which are documented in the +copious manual pages that come with the module, but for the +purposes of this article we'll keep things as simple as +possible. + +To process the template, you would call the C<process> method +like this + + $tt->process('my_template', \%data) + || die $tt->error; + +We pass two parameters to C<process>, the first is the name of +the file containing the template to process (in this case, +my_template) and the second is a reference to a hash which +contains the data items that you want to use in the template. If +processing the template gives any kind of error, the program +will die with a (hopefully) useful error message. + +So what kinds of things can go in C<%data>? The answer is just +about anything. Here's an example showing data about English +Premier League football teams. + + my @teams = ({ name => 'Man Utd', + played => 16, + won => 12, + drawn => 3, + lost => 1 }, + { name => 'Bradford', + played => 16, + won => 2, + drawn => 5, + lost => 9 }); + + my %data = ( name => 'English Premier League', + season => '2000/01', + teams => \@teams ); + +This creates three data items which can be accessed within the +template, called C<name>, C<season> and C<teams>. Notice that +C<teams> is a complex data structure. + +Here is a template that we might use to process this data. + + League Standings + + League Name: [% name %] + Season : [% season %] + + Teams: + [% FOREACH team = teams -%] + [% team.name %] [% team.played -%] + [% team.won %] [% team.drawn %] [% team.lost %] + [% END %] + +Running this template with this data gives us the following +output + + League Standings + + League Name: English Premier League + Season : 2000/01 + + Teams: + Man Utd 16 12 3 1 + Bradford 16 2 5 9 + +Hopefully the syntax of the template is simple enough to +follow. There are a few points to note. + +=over 4 + +=item * + +Template processing directives are written using a simple +language which is not Perl. + +=item * + +The keys of the C<%data> have become the names of the data +variables within the template. + +=item * + +Template processing directives are surrounded by C<[%> and +C<%]> sequences. + +=item * + +If these tags are replaced with C<[%-> C<-%]> then the preceding +or following linefeed is suppressed. + +=item * + +In the C<FOREACH> loop, each element of the C<teams> list was +assigned, in turn, to the temporary variable C<team>. + +=item * + +Each item assigned to the C<team> variable is a Perl hash. +Individual values within the hash are accessed using a dot notation. + +=back + +It's probably the first and last of these points which are the +most important. The first point emphasises the separation of the +data acquisition logic from the presentation logic. The person +creating the presentation template doesn't need to know Perl, +they only need to know the data items which will be passed into +the template. + +The last point demonstrates the way that TT2 protects the +template designer from the implementation of the data structures. +The data objects passed to the template processor can be scalars, +arrays, hashes, objects or even subroutines. The template +processor will just interpret your data correctly and Do The +Right Thing to return the correct value to you. In this example +each team was a hash, but in a larger system each team might be +an object, in which case C<name>, C<played>, etc. would be accessor +methods to the underlying object attributes. No changes would be +required to the template as the template processor would realise +that it needed to call methods rather than access hash values. + +=head2 A more complex example + +Stats about the English Football League are usually presented in +a slightly more complex format than the one we used above. A +full set of stats will show the number of games that a team has +won, lost or drawn, the number of goals scored for and against +the team and the number of points that the team therefore has. +Teams gain three points for a win and one point for a draw. When +teams have the same number of points they are separated by the +goal difference, that is the number of goals the team has scored +minus the number of team scored against them. To complicate +things even further, the games won, drawn and lost and the goals +for and against are often split between home and away games. + +Therefore if you have a data source which lists the team name +togther with the games won, drawn and lost and the goals for and +against split into home and away (a total of eleven data items) +you can calculate all of the other items (goal difference, +points awarded and even position in the league). Let's take such +a file, but we'll only look at the top three teams. It will look +something like this: + + Man Utd,7,1,0,26,4,5,2,1,15,6 + Arsenal,7,1,0,17,4,2,3,3,7,9 + Leicester,4,3,1,10,8,4,2,2,7,4 + +A simple script to read this data into an array of hashes will +look something like this (I've simplified the names of the data +columns - w, d, and l are games won, drawn and lost and f and a +are goals scored for and against; h and a at the front of a data +item name indicates whether it's a home or away statistic): + + my @cols = qw(name hw hd hl hf ha aw ad al af aa); + + my @teams; + while (<>) { + chomp; + + my %team; + + @team{@cols} = split /,/; + + push @teams, \%team; + } + +We can then go thru the teams again and calculate all of the +derived data items: + + foreach (@teams) { + $_->{w} = $_->{hw} + $_->{aw}; + $_->{d} = $_->{hd} + $_->{ad}; + $_->{l} = $_->{hl} + $_->{al}; + + $_->{pl} = $_->{w} + $_->{d} + $_->{l}; + + $_->{f} = $_->{hf} + $_->{af}; + $_->{a} = $_->{ha} + $_->{aa}; + + $_->{gd} = $_->{f} - $_->{a}; + $_->{pt} = (3 * $_->{w}) + $_->{d}; + } + +And then produce a list sorted in descending order: + + @teams = sort { + $b->{pt} <=> $b->{pt} || $b->{gd} <=> $a->{gd} + } @teams; + +And finally add the league position data item: + + $teams[$_]->{pos} = $_ + 1 + foreach 0 .. $#teams; + +Having pulled all of our data into an internal data structure +we can start to produce output using out templates. A template +to create a CSV file containing the data split between home and +away stats would look like this: + + [% FOREACH team = teams -%] + [% team.pos %],[% team.name %],[% team.pl %],[% team.hw %], + [%- team.hd %],[% team.hl %],[% team.hf %],[% team.ha %], + [%- team.aw %],[% team.ad %],[% team.al %],[% team.af %], + [%- team.aa %],[% team.gd %],[% team.pt %] + [%- END %] + +And processing it like this: + + $tt->process('split.tt', { teams => \@teams }, 'split.csv') + || die $tt->error; + +produces the following output: + + 1,Man Utd,16,7,1,0,26,4,5,2,1,15,6,31,39 + 2,Arsenal,16,7,1,0,17,4,2,3,3,7,9,11,31 + 3,Leicester,16,4,3,1,10,8,4,2,2,7,4,5,29 + +Notice that we've introduced the third parameter to C<process>. +If this parameter is missing then the TT2 sends its output to +C<STDOUT>. If this parameter is a scalar then it is taken as the +name of a file to write the output to. This parameter can also be +(amongst other things) a filehandle or a reference to an object w +hich is assumed to implement a C<print> method. + +If we weren't interested in the split between home and away games, +then we could use a simpler template like this: + + [% FOREACH team = teams -%] + [% team.pos %],[% team.name %],[% team.pl %],[% team.w %], + [%- team.d %],[% team.l %],[% team.f %],[% team.a %], + [%- team.aa %],[% team.gd %],[% team.pt %] + [% END -%] + +Which would produce output like this: + + 1,Man Utd,16,12,3,1,41,10,6,31,39 + 2,Arsenal,16,9,4,3,24,13,9,11,31 + 3,Leicester,16,8,5,3,17,12,4,5,29 + +=head1 Producing XML + +This is starting to show some of the power and flexibility of +TT2, but you may be thinking that you could just as easily produce +this output with a C<foreach> loop and a couple of C<print> +statements in your code. This is, of course, true; but that's +because I've chosen a deliberately simple example to explain the +concepts. What if we wanted to produce an XML file containing the +data? And what if (as I mentioned earlier) the league data was held +in an object? The code would then look even easier as most of the code +we've written earlier would be hidden away in C<FootballLeague.pm>. + + use FootballLeague; + use Template; + + my $league = FootballLeague->new(name => 'English Premier'); + + my $tt = Template->new; + + $tt->process('league_xml.tt', { league => $league }) + || die $tt->error; + +And the template in C<league_xml.tt> would look something like this: + + <?xml version="1.0"?> + <!DOCTYPE LEAGUE SYSTEM "league.dtd"> + + <league name="[% league.name %]" season="[% league.season %]"> + [% FOREACH team = league.teams -%] + <team name="[% team.name %]" + pos="[% team.pos %]" + played="[% team.pl %]" + goal_diff="[% team.gd %]" + points="[% team.pt %]"> + <stats type="home"> + win="[% team.hw %]" + draw="[%- team.hd %]" + lose="[% team.hl %]" + for="[% team.hf %]" + against="[% team.ha %]" /> + <stats type="away"> + win="[% team.aw %]" + draw="[%- team.ad %]" + lose="[% team.al %]" + for="[% team.af %]" + against="[% team.aa %]" /> + </team> + [% END -%] + &/league> + +Notice that as we've passed the whole object into C<process> then +we need to put an extra level of indirection on our template +variables - everything is now a component of the C<league> variable. +Other than that, everything in the template is very similar to what +we've used before. Presumably now C<team.name> calls an accessor +function rather than carrying out a hash lookup, but all of this +is transparent to our template designer. + +=head1 Multiple Formats + +As a final example, let's suppose that we need to create output +football league tables in a number of formats. Perhaps we are +passing this data on to other people and they can't all use the +same format. Some of our users need CSV files and others need +XML. Some require data split between home and away matches and +other just want the totals. In total, then, we'll need four +different templates, but the good news is that they can use the +same data object. All the script needs to do is to establish +which template is required and process it. + + use FootballLeague; + use Template; + + my ($name, $type, $stats) = @_; + + my $league = FootballLeague->new(name => $name); + + my $tt = Template->new; + + $tt->process("league_${type}_$stats.tt", + { league => $league } + "league_$stats.$type") + || die $tt->error; + +For example, you can call this script as + + league.pl 'English Premier' xml split + +This will process a template called C<league_xml_split.tt> +and put the results in a file called C<league_split.xml>. + +This starts to show the true strength of the Template Toolkit. +If we later wanted to add another file format - perhaps we +wanted to create a league table HTML page or even a LaTeX +document - then we would just need to create the appropriate +template and name it according to our existing naming +convention. We would need to make no changes to the code. + +I hope you can now see why the Template Toolkit is fast becoming +an essential part of many people's Perl installation. + +=head1 AUTHOR + +Dave Cross E<lt>dave@dave.org.ukE<gt> + + + + +=head1 VERSION + +Template Toolkit version 2.13, released on 30 January 2004. + +=head1 COPYRIGHT + + +Copyright (C) 2001 Dave Cross E<lt>dave@dave.org.ukE<gt> + +This module is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/Tutorial/Web.pod b/lib/Template/Tutorial/Web.pod new file mode 100644 index 0000000..a5ed9bb --- /dev/null +++ b/lib/Template/Tutorial/Web.pod @@ -0,0 +1,801 @@ +#============================================================= -*-perl-*- +# +# Template::Tutorial::Web +# +# DESCRIPTION +# This tutorial provides an introduction to the Template Toolkit and +# a "quick start" guide to getting up to speed. Its primarily focus +# is on using the Template Toolkit to build web content and it covers +# 4 basic areas: using tpage and ttree; using the Template.pm module +# in CGI scripts; writing Apache/mod_perl handlers; and extending the +# toolkit by writing plugins. +# +# AUTHOR +# Andy Wardley <abw@andywardley.com> +# +# COPYRIGHT +# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved. +# Copyright (C) 1998-2001 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. +# +# REVISION +# +# +#======================================================================== + + +#------------------------------------------------------------------------ +# 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::Tutorial::Web - Generating Web Content Using the Template Toolkit + +=head1 DESCRIPTION + +This tutorial document provides a introduction to the Template Toolkit +and demonstrates some of the typical ways it may be used for +generating web content. It covers the generation of static pages from +templates using the L<tpage|Template::Tools::tpage> and +L<ttree|Template::Tools::ttree> scripts and then goes on to +show dynamic content generation using CGI scripts and Apache/mod_perl +handlers. + +Various features of the Template Toolkit are introduced and described +briefly and explained by use of example. For further information, +see L<Template>, L<Template::Manual> and the various sections within +it. e.g. + + perldoc Template # Template.pm module usage + perldoc Template::Manual # index to manual + perldoc Template::Manual::Config # e.g. configuration options + +The documentation is now also distributed in HTML format (or rather, +in the form of HTML templates). See the 'docs' sub-directory of the +distribution for further information on building the HTML documentation. + +If you're already reading this as part of the HTML documentation, then +you don't need to worry about all that. You can have a seat, sit back. + back and enjoy the rest of the tutorial... + +=head1 INTRODUCTION + +The Template Toolkit is a set of Perl modules which collectively +implement a template processing system. In this context, a template +is a text document containing special markup tags called 'directives'. +A directive is an instruction for the template processor to perform +some action and substitute the result into the document in place of +the original directive. Directives include those to define or insert +a variable value, iterate through a list of values (FOREACH), declare +a conditional block (IF/UNLESS/ELSE), include and process another template +file (INCLUDE) and so on. + +In all other respects, the document is a plain text file and may +contain any other content (e.g. HTML, XML, RTF, LaTeX, etc). Directives +are inserted in the document within the special markup tags which are +'[%' and '%]' by default, but can be changed via the module +configuration options. Here's an example of an HTML document with +additional Template Toolkit directives. + + [% INCLUDE header + title = 'This is an HTML example' + %] + + <h1>Some Interesting Links</h1> + + [% webpages = [ + { url => 'http://foo.org', title => 'The Foo Organisation' } + { url => 'http://bar.org', title => 'The Bar Organisation' } + ] + %] + + Links: + <ul> + [% FOREACH link = webpages %] + <li><a href="[% link.url %]">[% link.title %]</a> + [% END %] + </ul> + + [% INCLUDE footer %] + +This example shows how the INCLUDE directive is used to load and process +separate 'header' and 'footer' template files, including the output in +the current document. These files might look like this: + +header: + + <html> + <head> + <title>[% title %]</title> + </head> + + <body bgcolor="#ffffff"> + +footer: + + <hr> + + <center> + © Copyright 2000 Me, Myself, I + </center> + + </body> + </html> + +The example also uses the FOREACH directive to iterate through the +'webpages' list to build a table of links. In this example, we have +defined this list within the template to contain a number of hash references, +each containing a 'url' and 'title' member. The FOREACH directive +iterates through the list, aliasing 'link' to each item (hash ref). +The B<[% link.url %]> and B<[% link.title %]> directives then access +the individual values in the hash and insert them into the document. + +The following sections show other ways in which data can be defined for +use in a template. + +=head1 GENERATING STATIC PAGES + +Having created a template file we can now process it to generate some +real output. The quickest and easiest way to do this is to use the +F<tpage> script. This is provided as part of the Template Toolkit and +should be installed in your usual Perl bin directory. + +Assuming you saved your template file as 'mypage.html', you would run +the command: + + tpage mypage.html + +This will process the template file, sending the output to STDOUT +(i.e. whizzing past you on the screen). You may want to redirect the +output to a file but be careful not to specify the same name as the +template file, or you'll overwrite it. You may want to use one prefix +for your templates such as '.atml' (for 'Another Template Markup +Language', perhaps?) and the regular '.html' for the output files +(assuming you're creating HTML, that is). Alternatively, you might +redirect the output to another directory. e.g. + + tpage mypage.atml > mypage.html + tpage templates/mypage.html > html/mypage.html + +The B<tpage> script is very basic and only really intended to give you +an easy way to process a template without having to write any Perl code. +A much more flexible tool is B<ttree>, described below, but for now let's +look at the output generated by processing the above example (some +whitespace removed for brevity): + + <html> + <head> + <title>This is an HTML example</title> + </head> + + <body bgcolor="#ffffff"> + + <h1>Some Interesting Links</h1> + + Links: + <ul> + <li><a href="http://foo.org">The Foo Organsiation</a> + <li><a href="http://bar.org">The Bar Organsiation</a> + </ul> + + <hr> + + <center> + © Copyright 2000 Me, Myself, I + </center> + + </body> + </html> + +The F<header> and F<footer> template files have been included (assuming +you created them and they're in the current directory) and the link data +has been built into an HTML list. + +The F<ttree> script, also distributed as part of the Template Toolkit, +provides a more flexible way to process template documents. The first +time you run the script, it will ask you if it should create a +configuration file, usually called '.ttreerc' in your home directory. +Answer 'y' to have it create the file. + +The F<ttree> documentation describes how you can change the location +of this file and also explains the syntax and meaning of the various +options in the file. Comments are written to the sample configuration +file which should also help. + + perldoc ttree + ttree -h + +In brief, the configuration file describes the directories in which +template files are to be found (src), where the corresponding output +should be written to (dest), and any other directories (lib) that may +contain template files that you plan to INCLUDE into your source +documents. You can also specify processing options (such as 'verbose' +and 'recurse') and provide regular expression to match files that you +don't want to process (ignore, accept) or should be copied instead of +processed (copy). + +An example F<.ttreerc> file is shown here: + +$HOME/.ttreerc: + verbose + recurse + + # this is where I keep other ttree config files + cfg = ~/.ttree + + src = ~/websrc/src + lib = ~/websrc/lib + dest = ~/public_html/test + + ignore = \b(CVS|RCS)\b + ignore = ^# + +You can create many different configuration files and store them +in the directory specified in the 'cfg' option, shown above. You then +add the C<-f filename> option to F<ttree> to have it read that file. + +When you run the script, it compares all the files in the 'src' directory +(including those in sub-directories if the 'recurse' option is set), with +those in the 'dest' directory. If the destination file doesn't exist or +has an earlier modification time than the corresponding source file, then +the source will be processed with the output written to the destination +file. The C<-a> option forces all files to be processed, regardless of +modification times. + +The script I<doesn't> process any of the files in the 'lib' directory, +but it does add it to the INCLUDE_PATH for the template processor so +that it can locate these files via an INCLUDE or PROCESS directive. +Thus, the 'lib' directory is an excellent place to keep template elements +such as header, footers, etc., that aren't complete documents in their +own right. + +You can also specify various Template Toolkit options from the configuration +file. Consult the B<ttree> documentation and help summary (C<ttree -h>) +for full details. e.g. + +$HOME/.ttreerc: + pre_process = config + interpolate + post_chomp + +The 'pre_process' option allows you to specify a template file which +should be processed before each file. Unsurprisingly, there's also a +'post_process' option to add a template after each file. In the +fragment above, we have specified that the 'config' template should be +used as a prefix template. We can create this file in the 'lib' +directory and use it to define some common variables, including those +web page links we defined earlier and might want to re-use in other +templates. We could also include an HTML header, title, or menu bar +in this file which would then be prepended to each and every template +file, but for now we'll keep all that in a separate 'header' file. + +$lib/config: + + [% root = '~/abw' + home = "$root/index.html" + images = "$root/images" + email = 'abw@wardley.org' + graphics = 1 + webpages = [ + { url => 'http://foo.org', title => 'The Foo Organsiation' } + { url => 'http://bar.org', title => 'The Bar Organsiation' } + ] + %] + +Assuming you've created or copied the 'header' and 'footer' files from the +earlier example into your 'lib' directory, you can now start to create +web pages like the following in your 'src' directory and process them +with F<ttree>. + +$src/newpage.html: + + [% INCLUDE header + title = 'Another Template Toolkit Test Page' + %] + + <a href="[% home %]">Home</a> + <a href="mailto:[% email %]">Email</a> + + [% IF graphics %] + <img src="[% images %]/logo.gif" align=right width=60 height=40> + [% END %] + + [% INCLUDE footer %] + +Here we've shown how pre-defined variables can be used as flags to +enable certain feature (e.g. 'graphics') and to specify common items +such as an email address and URL's for the home page, images directory +and so on. This approach allows you to define these values once so +that they're consistent across all pages and can easily be changed to +new values. + +When you run B<ttree>, you should see output similar to the following +(assuming you have the verbose flag set). + + ttree 1.14 (Template Toolkit version 1.02a) + + Source: /home/abw/websrc/src + Destination: /home/abw/public_html/test + Include Path: [ /home/abw/websrc/lib ] + Ignore: [ \b(CVS|RCS)\b, ^# ] + Copy: [ ] + Accept: [ * ] + + + newpage.html + +The '+' before 'newpage.html' shows that the file was processed, with +the output being written to the destination directory. If you run the +same command again, you'll see the following line displayed instead +showing a '-' and giving a reason why the file wasn't processed. + + - newpage.html (not modified) + +It has detected a 'newpage.html' in the destination directory which is +more recent than that in the source directory and so hasn't bothered +to waste time re-processing it. To force all files to be processed, +use the C<-a> option. You can also specify one or more filenames as +command line arguments to F<ttree>: + + tpage newpage.html + +This is what the destination page looks like. + +$dest/newpage.html: + + <html> + <head> + <title>Another Template Toolkit Test Page</title> + </head> + + <body bgcolor="#ffffff"> + + <a href="~/abw/index.html">Home</a> + <a href="mailto:abw@wardley.org">Email me</a> + + <img src="~/abw/images/logo.gif" align=right width=60 height=40> + + <hr> + + <center> + © Copyright 2000 Me, Myself, I + </center> + + </body> + </html> + +You can add as many documents as you like to the 'src' directory and +F<ttree> will apply the same process to them all. In this way, it is +possible to build an entire tree of static content for a web site with +a single command. The added benefit is that you can be assured of +consistency in links, header style, or whatever else you choose to +implement in terms of common templates elements or variables. + +=head1 DYNAMIC CONTENT GENERATION VIA CGI SCRIPT + +The L<Template|Template> module provides a simple front-end to the Template +Toolkit for use in CGI scripts and Apache/mod_perl handlers. Simply +'use' the Template module, create an object instance with the new() +method and then call the process() method on the object, passing the +name of the template file as a parameter. The second parameter passed +is a reference to a hash array of variables that we want made available +to the template: + + #!/usr/bin/perl -w + + use strict; + use Template; + + my $file = 'src/greeting.html'; + my $vars = { + message => "Hello World\n" + }; + + my $template = Template->new(); + + $template->process($file, $vars) + || die "Template process failed: ", $template->error(), "\n"; + +So that our scripts will work with the same template files as our earlier +examples, we'll can add some configuration options to the constructor to +tell it about our environment: + + my $template->new({ + # where to find template files + INCLUDE_PATH => '/home/abw/websrc/src:/home/abw/websrc/lib', + # pre-process lib/config to define any extra values + PRE_PROCESS => 'config', + }); + +Note that here we specify the 'config' file as a PRE_PROCESS option. +This means that the templates we process can use the same global +variables defined earlier for our static pages. We don't have to +replicate their definitions in this script. However, we can supply +additional data and functionality specific to this script via the hash +of variables that we pass to the process() method. + +These entries in this hash may contain simple text or other values, +references to lists, others hashes, sub-routines or objects. The Template +Toolkit will automatically apply the correct procedure to access these +different types when you use the variables in a template. + +Here's a more detailed example to look over. Amongst the different +template variables we define in C<$vars>, we create a reference to a +CGI object and a 'get_user_projects' sub-routine. + + #!/usr/bin/perl -w + + use strict; + use Template; + use CGI; + + $| = 1; + print "Content-type: text/html\n\n"; + + my $file = 'userinfo.html'; + my $vars = { + 'version' => 3.14, + 'days' => [ qw( mon tue wed thu fri sat sun ) ], + 'worklist' => \&get_user_projects, + 'cgi' => CGI->new(), + 'me' => { + 'id' => 'abw', + 'name' => 'Andy Wardley', + }, + }; + + sub get_user_projects { + my $user = shift; + my @projects = ... # do something to retrieve data + return \@projects; + } + + my $template = Template->new({ + INCLUDE_PATH => '/home/abw/websrc/src:/home/abw/websrc/lib', + PRE_PROCESS => 'config', + }); + + $template->process($file, $vars) + || die $template->error(); + +Here's a sample template file that we might create to build the output +for this script. + +$src/userinfo.html: + + [% INCLUDE header + title = 'Template Toolkit CGI Test' + %] + + <a href="mailto:[% email %]">Email [% me.name %]</a> + + <p>This is version [% version %]</p> + + <h3>Projects</h3> + <ul> + [% FOREACH project = worklist(me.id) %] + <li> <a href="[% project.url %]">[% project.name %]</a> + [% END %] + </ul> + + [% INCLUDE footer %] + +This example shows how we've separated the Perl implementation (code) from +the presentation (HTML) which not only makes them easier to maintain in +isolation, but also allows the re-use of existing template elements +such as headers and footers, etc. By using template to create the +output of your CGI scripts, you can give them the same consistency +as your static pages built via L<ttree|Template::Tools::ttree> or +other means. + +Furthermore, we can modify our script so that it processes any one of a +number of different templates based on some condition. A CGI script to +maintain a user database, for example, might process one template to +provide an empty form for new users, the same form with some default +values set for updating an existing user record, a third template for +listing all users in the system, and so on. You can use any Perl +functionality you care to write to implement the logic of your +application and then choose one or other template to generate the +desired output for the application state. + +=head1 DYNAMIC CONTENT GENERATION VIA APACHE/MOD_PERL HANDLER + +B<NOTE:> the Apache::Template module is now available from CPAN +and provides a simple and easy to use Apache/mod_perl interface to the +Template Toolkit. It's only in it's first release (0.01) at the time +of writing and it currently only offers a fairly basic facility, but +it implements most, if not all of what is described below, and it +avoids the need to write your own handler. However, in many cases, +you'll want to write your own handler to customise processing for your +own need, and this section will show you how to get started. + +The Template module can be used in a similar way from an Apache/mod_perl +handler. Here's an example of a typical Apache F<httpd.conf> file: + + PerlModule CGI; + PerlModule Template + PerlModule MyOrg::Apache::User + + PerlSetVar websrc_root /home/abw/websrc + + <Location /user/bin> + SetHandler perl-script + PerlHandler MyOrg::Apache::User + </Location> + +This defines a location called '/user/bin' to which all requests will +be forwarded to the handler() method of the MyOrg::Apache::User +module. That module might look something like this: + + package MyOrg::Apache::User; + + use strict; + use vars qw( $VERSION ); + use Apache::Constants qw( :common ); + use Template qw( :template ); + use CGI; + + $VERSION = 1.59; + + sub handler { + my $r = shift; + + my $websrc = $r->dir_config('websrc_root') + or return fail($r, SERVER_ERROR, + "'websrc_root' not specified"); + + my $template = Template->new({ + INCLUDE_PATH => "$websrc/src/user:$websrc/lib", + PRE_PROCESS => 'config', + OUTPUT => $r, # direct output to Apache request + }); + + my $params = { + uri => $r->uri, + cgi => CGI->new, + }; + + # use the path_info to determine which template file to process + my $file = $r->path_info; + $file =~ s[^/][]; + + $r->content_type('text/html'); + $r->send_http_header; + + $template->process($file, $params) + || return fail($r, SERVER_ERROR, $template->error()); + + return OK; + } + + sub fail { + my ($r, $status, $message) = @_; + $r->log_reason($message, $r->filename); + return $status; + } + +The handler accepts the request and uses it to determine the 'websrc_root' +value from the config file. This is then used to define an INCLUDE_PATH +for a new Template object. The URI is extracted from the request and a +CGI object is created. These are both defined as template variables. + +The name of the template file itself is taken from the PATH_INFO element +of the request. In this case, it would comprise the part of the URL +coming after '/user/bin', e.g for '/user/bin/edit', the template file +would be 'edit' located in "$websrc/src/user". The headers are sent +and the template file is processed. All output is sent directly to the +print() method of the Apache request object. + +=head1 USING PLUGINS TO EXTEND FUNCTIONALITY + +As we've already shown, it is possible to bind Perl data and functions +to template variables when creating dynamic content via a CGI script +or Apache/mod_perl process. The Template Toolkit also supports a +plugin interface which allows you define such additional data and/or +functionality in a separate module and then load and use it as +required with the USE directive. + +The main benefit to this approach is that you can load the extension into +any template document, even those that are processed "statically" by +F<tpage> or F<ttree>. You I<don't> need to write a Perl wrapper to +explicitly load the module and make it available via the stash. + +Let's demonstrate this principle using the DBI plugin written by Simon +Matthews E<lt>sam@knowledgepool.comE<gt>. You can create this +template in your 'src' directory and process it using F<ttree> to see +the results. Of course, this example relies on the existence of the +appropriate SQL database but you should be able to adapt it to your +own resources, or at least use it as a demonstrative example of what's +possible. + + [% INCLUDE header + title = 'User Info' + %] + + [% USE DBI('dbi:mSQL:mydbname') %] + + <table border=0 width="100%"> + <tr> + <th>User ID</th> + <th>Name</th> + <th>Email</th> + </tr> + + [% FOREACH user = DBI.query('SELECT * FROM user ORDER BY id') %] + <tr> + <td>[% user.id %]</td> + <td>[% user.name %]</td> + <td>[% user.email %]</td> + </tr> + [% END %] + + </table> + + [% INCLUDE footer %] + +A plugin is simply a Perl module in a known location and conforming to +a known standard such that the Template Toolkit can find and load it +automatically. You can create your own plugin by inheriting from the +F<Template::Plugin> module. + +Here's an example which defines some data items ('foo' and 'people') +and also an object method ('bar'). We'll call the plugin 'FooBar' for +want of a better name and create it in the 'MyOrg::Template::Plugin::FooBar' +package. We've added a 'MyOrg' to the regular 'Template::Plugin::*' package +to avoid any conflict with existing plugins. + +You can create a module stub using the Perl utlity F<h2xs>: + + h2xs -A -X -n MyOrg::Template::Plugin::FooBar + +This will create a directory structure representing the package name +along with a set of files comprising your new module. You can then +edit FooBar.pm to look something like this: + + package MyOrg::Template::Plugin::FooBar; + + use Template::Plugin; + use vars qw( $VERSION ); + use base qw( Template::Plugin ); + + $VERSION = 1.23; + + sub new { + my ($class, $context, @params) = @_; + + bless { + _CONTEXT => $context, + foo => 25, + people => [ 'tom', 'dick', 'harry' ], + }, $class; + } + + sub bar { + my ($self, @params) = @_; + # ...do something... + return $some_value; + } + +The plugin constructor new() receives the class name as the first +parameter, as is usual in Perl, followed by a reference to something +called a Template::Context object. You don't need to worry too much +about this at the moment, other than to know that it's the main +processing object for the Template Toolkit. It provides access to the +functionality of the processor and some plugins may need to +communicate with it. We don't at this stage, but we'll save the +reference anyway in the '_CONTEXT' member. The leading underscore is +a convention which indicates that this item is private and the +Template Toolkit won't attempt to access this member. The other +members defined, 'foo' and 'people' are regular data items which will be +made available to templates using this plugin. Following the context +reference are passed any additional parameters specified with the +USE directive, such as the data source parameter, 'dbi:mSQL:mydbname', +that we used in the earlier DBI example. + +If you used F<h2xs> to create the module stub then you'll already +have a Makefile.PL and you can incite the familiar incantation to +build and install it. Don't forget to add some tests to test.pl! + + perl Makefile.PL + make + make test + make install + +If you don't or can't install it to the regular place for your Perl +modules (perhaps because you don't have the required privileges) then +you can set the PERL5LIB environment variable to specify another location. +If you're using F<ttree> then you can add the following line to your +configuration file instead. This has the effect of add '/path/to/modules' +to the @INC array to a similar end. + +$HOME/.ttreerc: + + perl5lib = /path/to/modules + +One further configuration item must be added to inform the toolkit of +the new package name we have adopted for our plugins: + +$HOME/.ttreerc: + + plugin_base = 'MyOrg::Template::Plugin' + +If you're writing Perl code to control the Template modules directly, +then this value can be passed as a configuration parameter when you +create the module. + + use Template; + + my $template = Template->new({ + PLUGIN_BASE => 'MyOrg::Template::Plugin' + }); + +Now we can create a template which uses this plugin: + + [% INCLUDE header + title = 'FooBar Plugin Test' + %] + + [% USE FooBar %] + + Some values available from this plugin: + [% FooBar.foo %] [% FooBar.bar %] + + The users defined in the 'people' list: + [% FOREACH uid = FooBar.people %] + * [% uid %] + [% END %] + + [% INCLUDE footer %] + +The 'foo', 'bar' and 'people' items of the FooBar plugin are +automatically resolved to the appropriate data items or method calls +on the underlying object. + +Using this approach, it is possible to create application +functionality in a single module which can then be loaded and used on +demand in any template. The simple interface between template +directives and plugin objects allows complex, dynamic content to be +built from a few simple template documents without knowing anything +about the underlying implementation. + +=head1 AUTHOR + +Andy Wardley E<lt>abw@andywardley.comE<gt> + +L<http://www.andywardley.com/|http://www.andywardley.com/> + + + + +=head1 VERSION + +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. + + + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/View.pm b/lib/Template/View.pm new file mode 100644 index 0000000..6de8d42 --- /dev/null +++ b/lib/Template/View.pm @@ -0,0 +1,752 @@ +#============================================================= -*-Perl-*- +# +# Template::View +# +# DESCRIPTION +# A custom view of a template processing context. Can be used to +# implement custom "skins". +# +# 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. +# +# TODO +# * allowing print to have a hash ref as final args will cause problems +# if you do this: [% view.print(hash1, hash2, hash3) %]. Current +# work-around is to do [% view.print(hash1); view.print(hash2); +# view.print(hash3) %] or [% view.print(hash1, hash2, hash3, { }) %] +# +# REVISION +# $Id: View.pm,v 2.9 2004/01/30 18:36:11 abw Exp $ +# +#============================================================================ + +package Template::View; + +require 5.004; + +use strict; +use vars qw( $VERSION $DEBUG $AUTOLOAD @BASEARGS $MAP ); +use base qw( Template::Base ); + +$VERSION = sprintf("%d.%02d", q$Revision: 2.9 $ =~ /(\d+)\.(\d+)/); +$DEBUG = 0 unless defined $DEBUG; +@BASEARGS = qw( context ); +$MAP = { + HASH => 'hash', + ARRAY => 'list', + TEXT => 'text', + default => '', +}; + +$DEBUG = 0; + +#------------------------------------------------------------------------ +# _init(\%config) +# +# Initialisation method called by the Template::Base class new() +# constructor. $self->{ context } has already been set, by virtue of +# being named in @BASEARGS. Remaining config arguments are presented +# as a hash reference. +#------------------------------------------------------------------------ + +sub _init { + my ($self, $config) = @_; + + # move 'context' somewhere more private + $self->{ _CONTEXT } = $self->{ context }; + delete $self->{ context }; + + # generate table mapping object types to templates + my $map = $config->{ map } || { }; + $map->{ default } = $config->{ default } unless defined $map->{ default }; + $self->{ map } = { + %$MAP, + %$map, + }; + + # local BLOCKs definition table + $self->{ _BLOCKS } = $config->{ blocks } || { }; + + # name of presentation method which printed objects might provide + $self->{ method } = defined $config->{ method } + ? $config->{ method } : 'present'; + + # view is sealed by default preventing variable update after + # definition, however we don't actually seal a view until the + # END of the view definition + my $sealed = $config->{ sealed }; + $sealed = 1 unless defined $sealed; + $self->{ sealed } = $sealed ? 1 : 0; + + # copy remaining config items from $config or set defaults + foreach my $arg (qw( base prefix suffix notfound silent )) { + $self->{ $arg } = $config->{ $arg } || ''; + } + + # name of data item used by view() + $self->{ item } = $config->{ item } || 'item'; + + # map methods of form ${include_prefix}_foobar() to include('foobar')? + $self->{ include_prefix } = $config->{ include_prefix } || 'include_'; + # what about mapping foobar() to include('foobar')? + $self->{ include_naked } = defined $config->{ include_naked } + ? $config->{ include_naked } : 1; + + # map methods of form ${view_prefix}_foobar() to include('foobar')? + $self->{ view_prefix } = $config->{ view_prefix } || 'view_'; + # what about mapping foobar() to view('foobar')? + $self->{ view_naked } = $config->{ view_naked } || 0; + + # the view is initially unsealed, allowing directives in the initial + # view template to create data items via the AUTOLOAD; once sealed via + # call to seal(), the AUTOLOAD will not update any internal items. + delete @$config{ qw( base method map default prefix suffix notfound item + include_prefix include_naked silent sealed + view_prefix view_naked blocks ) }; + $config = { %{ $self->{ base }->{ data } }, %$config } + if $self->{ base }; + $self->{ data } = $config; + $self->{ SEALED } = 0; + + return $self; +} + + +#------------------------------------------------------------------------ +# seal() +# unseal() +# +# Seal or unseal the view to allow/prevent new datat items from being +# automatically created by the AUTOLOAD method. +#------------------------------------------------------------------------ + +sub seal { + my $self = shift; + $self->{ SEALED } = $self->{ sealed }; +} + +sub unseal { + my $self = shift; + $self->{ SEALED } = 0; +} + + +#------------------------------------------------------------------------ +# clone(\%config) +# +# Cloning method which takes a copy of $self and then applies to it any +# modifications specified in the $config hash passed as an argument. +# Configuration items may also be specified as a list of "name => $value" +# arguments. Returns a reference to the cloned Template::View object. +# +# NOTE: may need to copy BLOCKS??? +#------------------------------------------------------------------------ + +sub clone { + my $self = shift; + my $clone = bless { %$self }, ref $self; + my $config = ref $_[0] eq 'HASH' ? shift : { @_ }; + + # merge maps + $clone->{ map } = { + %{ $self->{ map } }, + %{ $config->{ map } || { } }, + }; + + # "map => { default=>'xxx' }" can be specified as "default => 'xxx'" + $clone->{ map }->{ default } = $config->{ default } + if defined $config->{ default }; + + # update any remaining config items + my @args = qw( base prefix suffix notfound item method include_prefix + include_naked view_prefix view_naked ); + foreach my $arg (@args) { + $clone->{ $arg } = $config->{ $arg } if defined $config->{ $arg }; + } + push(@args, qw( default map )); + delete @$config{ @args }; + + # anything left is data + my $data = $clone->{ data } = { %{ $self->{ data } } }; + @$data{ keys %$config } = values %$config; + + return $clone; +} + + +#------------------------------------------------------------------------ +# print(@items, ..., \%config) +# +# Prints @items in turn by mapping each to an approriate template using +# the internal 'map' hash. If an entry isn't found and the item is an +# object that implements the method named in the internal 'method' item, +# (default: 'present'), then the method will be called passing a reference +# to $self, against which the presenter method may make callbacks (e.g. +# to view_item()). If the presenter method isn't implemented, then the +# 'default' map entry is consulted and used if defined. The final argument +# may be a reference to a hash array providing local overrides to the internal +# defaults for various items (prefix, suffix, etc). In the presence +# of this parameter, a clone of the current object is first made, applying +# any configuration updates, and control is then delegated to it. +#------------------------------------------------------------------------ + +sub print { + my $self = shift; + + # if final config hash is specified then create a clone and delegate to it + # NOTE: potential problem when called print(\%data_hash1, \%data_hash2); + if ((scalar @_ > 1) && (ref $_[-1] eq 'HASH')) { + my $cfg = pop @_; + my $clone = $self->clone($cfg) + || return; + return $clone->print(@_) + || $self->error($clone->error()); + } + my ($item, $type, $template, $present); + my $method = $self->{ method }; + my $map = $self->{ map }; + my $output = ''; + + # print each argument + foreach $item (@_) { + my $newtype; + + if (! ($type = ref $item)) { + # non-references are TEXT + $type = 'TEXT'; + $template = $map->{ $type }; + } + elsif (! defined ($template = $map->{ $type })) { + # no specific map entry for object, maybe it implements a + # 'present' (or other) method? + if ( $method && UNIVERSAL::can($item, $method) ) { + $present = $item->$method($self); ## call item method + # undef returned indicates error, note that we expect + # $item to have called error() on the view + return unless defined $present; + $output .= $present; + next; ## NEXT + } + elsif ( UNIVERSAL::isa($item, 'HASH' ) + && defined($newtype = $item->{$method}) + && defined($template = $map->{"$method=>$newtype"})) { + } + elsif ( defined($newtype) + && defined($template = $map->{"$method=>*"}) ) { + $template =~ s/\*/$newtype/; + } + elsif (! ($template = $map->{ default }) ) { + # default not defined, so construct template name from type + ($template = $type) =~ s/\W+/_/g; + } + } +# else { +# $self->DEBUG("defined map type for $type: $template\n"); +# } + $self->DEBUG("printing view '", $template || '', "', $item\n") if $DEBUG; + $output .= $self->view($template, $item) + if $template; + } + return $output; +} + + +#------------------------------------------------------------------------ +# view($template, $item, \%vars) +# +# Wrapper around include() which expects a template name, $template, +# followed by a data item, $item, and optionally, a further hash array +# of template variables. The $item is added as an entry to the $vars +# hash (which is created empty if not passed as an argument) under the +# name specified by the internal 'item' member, which is appropriately +# 'item' by default. Thus an external object present() method can +# callback against this object method, simply passing a data item to +# be displayed. The external object doesn't have to know what the +# view expects the item to be called in the $vars hash. +#------------------------------------------------------------------------ + +sub view { + my ($self, $template, $item) = splice(@_, 0, 3); + my $vars = ref $_[0] eq 'HASH' ? shift : { @_ }; + $vars->{ $self->{ item } } = $item if defined $item; + $self->include($template, $vars); +} + + +#------------------------------------------------------------------------ +# include($template, \%vars) +# +# INCLUDE a template, $template, mapped according to the current prefix, +# suffix, default, etc., where $vars is an optional hash reference +# containing template variable definitions. If the template isn't found +# then the method will default to any 'notfound' template, if defined +# as an internal item. +#------------------------------------------------------------------------ + +sub include { + my ($self, $template, $vars) = @_; + my $context = $self->{ _CONTEXT }; + + $template = $self->template($template); + + $vars = { } unless ref $vars eq 'HASH'; + $vars->{ view } ||= $self; + + $context->include( $template, $vars ); + +# DEBUGGING +# my $out = $context->include( $template, $vars ); +# print STDERR "VIEW return [$out]\n"; +# return $out; +} + + +#------------------------------------------------------------------------ +# template($template) +# +# Returns a compiled template for the specified template name, according +# to the current configuration parameters. +#------------------------------------------------------------------------ + +sub template { + my ($self, $name) = @_; + my $context = $self->{ _CONTEXT }; + return $context->throw(Template::Constants::ERROR_VIEW, + "no view template specified") + unless $name; + + my $notfound = $self->{ notfound }; + my $base = $self->{ base }; + my ($template, $block, $error); + + return $block + if ($block = $self->{ _BLOCKS }->{ $name }); + + # try the named template + $template = $self->template_name($name); + $self->DEBUG("looking for $template\n") if $DEBUG; + eval { $template = $context->template($template) }; + + # try asking the base view if not found + if (($error = $@) && $base) { + $self->DEBUG("asking base for $name\n") if $DEBUG; + eval { $template = $base->template($name) }; + } + + # try the 'notfound' template (if defined) if that failed + if (($error = $@) && $notfound) { + unless ($template = $self->{ _BLOCKS }->{ $notfound }) { + $notfound = $self->template_name($notfound); + $self->DEBUG("not found, looking for $notfound\n") if $DEBUG; + eval { $template = $context->template($notfound) }; + + return $context->throw(Template::Constants::ERROR_VIEW, $error) + if $@; # return first error + } + } + elsif ($error) { + $self->DEBUG("no 'notfound'\n") + if $DEBUG; + return $context->throw(Template::Constants::ERROR_VIEW, $error); + } + return $template; +} + + +#------------------------------------------------------------------------ +# template_name($template) +# +# Returns the name of the specified template with any appropriate prefix +# and/or suffix added. +#------------------------------------------------------------------------ + +sub template_name { + my ($self, $template) = @_; + $template = $self->{ prefix } . $template . $self->{ suffix } + if $template; + + $self->DEBUG("template name: $template\n") if $DEBUG; + return $template; +} + + +#------------------------------------------------------------------------ +# default($val) +# +# Special case accessor to retrieve/update 'default' as an alias for +# '$map->{ default }'. +#------------------------------------------------------------------------ + +sub default { + my $self = shift; + return @_ ? ($self->{ map }->{ default } = shift) + : $self->{ map }->{ default }; +} + + +#------------------------------------------------------------------------ +# AUTOLOAD +# + +# Returns/updates public internal data items (i.e. not prefixed '_' or +# '.') or presents a view if the method matches the view_prefix item, +# e.g. view_foo(...) => view('foo', ...). Similarly, the +# include_prefix is used, if defined, to map include_foo(...) to +# include('foo', ...). If that fails then the entire method name will +# be used as the name of a template to include iff the include_named +# parameter is set (default: 1). Last attempt is to match the entire +# method name to a view() call, iff view_naked is set. Otherwise, a +# 'view' exception is raised reporting the error "no such view member: +# $method". +#------------------------------------------------------------------------ + +sub AUTOLOAD { + my $self = shift; + my $item = $AUTOLOAD; + $item =~ s/.*:://; + return if $item eq 'DESTROY'; + + if ($item =~ /^[\._]/) { + return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW, + "attempt to view private member: $item"); + } + elsif (exists $self->{ $item }) { + # update existing config item (e.g. 'prefix') if unsealed + return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW, + "cannot update config item in sealed view: $item") + if @_ && $self->{ SEALED }; + $self->DEBUG("accessing item: $item\n") if $DEBUG; + return @_ ? ($self->{ $item } = shift) : $self->{ $item }; + } + elsif (exists $self->{ data }->{ $item }) { + # get/update existing data item (must be unsealed to update) + if (@_ && $self->{ SEALED }) { + return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW, + "cannot update item in sealed view: $item") + unless $self->{ silent }; + # ignore args if silent + @_ = (); + } + $self->DEBUG(@_ ? "updating data item: $item <= $_[0]\n" + : "returning data item: $item\n") if $DEBUG; + return @_ ? ($self->{ data }->{ $item } = shift) + : $self->{ data }->{ $item }; + } + elsif (@_ && ! $self->{ SEALED }) { + # set data item if unsealed + $self->DEBUG("setting unsealed data: $item => @_\n") if $DEBUG; + $self->{ data }->{ $item } = shift; + } + elsif ($item =~ s/^$self->{ view_prefix }//) { + $self->DEBUG("returning view($item)\n") if $DEBUG; + return $self->view($item, @_); + } + elsif ($item =~ s/^$self->{ include_prefix }//) { + $self->DEBUG("returning include($item)\n") if $DEBUG; + return $self->include($item, @_); + } + elsif ($self->{ include_naked }) { + $self->DEBUG("returning naked include($item)\n") if $DEBUG; + return $self->include($item, @_); + } + elsif ($self->{ view_naked }) { + $self->DEBUG("returning naked view($item)\n") if $DEBUG; + return $self->view($item, @_); + } + else { + return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW, + "no such view member: $item"); + } +} + + +1; + + +__END__ + +=head1 NAME + +Template::View - customised view of a template processing context + +=head1 SYNOPSIS + + # define a view + [% VIEW view + # some standard args + prefix => 'my_', + suffix => '.tt2', + notfound => 'no_such_file' + ... + + # any other data + title => 'My View title' + other_item => 'Joe Random Data' + ... + %] + # add new data definitions, via 'my' self reference + [% my.author = "$abw.name <$abw.email>" %] + [% my.copy = "© Copyright 2000 $my.author" %] + + # define a local block + [% BLOCK header %] + This is the header block, title: [% title or my.title %] + [% END %] + + [% END %] + + # access data items for view + [% view.title %] + [% view.other_item %] + + # access blocks directly ('include_naked' option, set by default) + [% view.header %] + [% view.header(title => 'New Title') %] + + # non-local templates have prefix/suffix attached + [% view.footer %] # => [% INCLUDE my_footer.tt2 %] + + # more verbose form of block access + [% view.include( 'header', title => 'The Header Title' ) %] + [% view.include_header( title => 'The Header Title' ) %] + + # very short form of above ('include_naked' option, set by default) + [% view.header( title => 'The Header Title' ) %] + + # non-local templates have prefix/suffix attached + [% view.footer %] # => [% INCLUDE my_footer.tt2 %] + + # fallback on the 'notfound' template ('my_no_such_file.tt2') + # if template not found + [% view.include('missing') %] + [% view.include_missing %] + [% view.missing %] + + # print() includes a template relevant to argument type + [% view.print("some text") %] # type=TEXT, template='text' + + [% BLOCK my_text.tt2 %] # 'text' with prefix/suffix + Text: [% item %] + [% END %] + + # now print() a hash ref, mapped to 'hash' template + [% view.print(some_hash_ref) %] # type=HASH, template='hash' + + [% BLOCK my_hash.tt2 %] # 'hash' with prefix/suffix + hash keys: [% item.keys.sort.join(', ') + [% END %] + + # now print() a list ref, mapped to 'list' template + [% view.print(my_list_ref) %] # type=ARRAY, template='list' + + [% BLOCK my_list.tt2 %] # 'list' with prefix/suffix + list: [% item.join(', ') %] + [% END %] + + # print() maps 'My::Object' to 'My_Object' + [% view.print(myobj) %] + + [% BLOCK my_My_Object.tt2 %] + [% item.this %], [% item.that %] + [% END %] + + # update mapping table + [% view.map.ARRAY = 'my_list_template' %] + [% view.map.TEXT = 'my_text_block' %] + + + # change prefix, suffix, item name, etc. + [% view.prefix = 'your_' %] + [% view.default = 'anyobj' %] + ... + +=head1 DESCRIPTION + +TODO + +=head1 METHODS + +=head2 new($context, \%config) + +Creates a new Template::View presenting a custom view of the specified +$context object. + +A reference to a hash array of configuration options may be passed as the +second argument. + +=over 4 + +=item prefix + +Prefix added to all template names. + + [% USE view(prefix => 'my_') %] + [% view.view('foo', a => 20) %] # => my_foo + +=item suffix + +Suffix added to all template names. + + [% USE view(suffix => '.tt2') %] + [% view.view('foo', a => 20) %] # => foo.tt2 + +=item map + +Hash array mapping reference types to template names. The print() +method uses this to determine which template to use to present any +particular item. The TEXT, HASH and ARRAY items default to 'test', +'hash' and 'list' appropriately. + + [% USE view(map => { ARRAY => 'my_list', + HASH => 'your_hash', + My::Foo => 'my_foo', } ) %] + + [% view.print(some_text) %] # => text + [% view.print(a_list) %] # => my_list + [% view.print(a_hash) %] # => your_hash + [% view.print(a_foo) %] # => my_foo + + [% BLOCK text %] + Text: [% item %] + [% END %] + + [% BLOCK my_list %] + list: [% item.join(', ') %] + [% END %] + + [% BLOCK your_hash %] + hash keys: [% item.keys.sort.join(', ') + [% END %] + + [% BLOCK my_foo %] + Foo: [% item.this %], [% item.that %] + [% END %] + +=item method + +Name of a method which objects passed to print() may provide for presenting +themselves to the view. If a specific map entry can't be found for an +object reference and it supports the method (default: 'present') then +the method will be called, passing the view as an argument. The object +can then make callbacks against the view to present itself. + + package Foo; + + sub present { + my ($self, $view) = @_; + return "a regular view of a Foo\n"; + } + + sub debug { + my ($self, $view) = @_; + return "a debug view of a Foo\n"; + } + +In a template: + + [% USE view %] + [% view.print(my_foo_object) %] # a regular view of a Foo + + [% USE view(method => 'debug') %] + [% view.print(my_foo_object) %] # a debug view of a Foo + +=item default + +Default template to use if no specific map entry is found for an item. + + [% USE view(default => 'my_object') %] + + [% view.print(objref) %] # => my_object + +If no map entry or default is provided then the view will attempt to +construct a template name from the object class, substituting any +sequence of non-word characters to single underscores, e.g. + + # 'fubar' is an object of class Foo::Bar + [% view.print(fubar) %] # => Foo_Bar + +Any current prefix and suffix will be added to both the default template +name and any name constructed from the object class. + +=item notfound + +Fallback template to use if any other isn't found. + +=item item + +Name of the template variable to which the print() method assigns the current +item. Defaults to 'item'. + + [% USE view %] + [% BLOCK list %] + [% item.join(', ') %] + [% END %] + [% view.print(a_list) %] + + [% USE view(item => 'thing') %] + [% BLOCK list %] + [% thing.join(', ') %] + [% END %] + [% view.print(a_list) %] + +=item view_prefix + +Prefix of methods which should be mapped to view() by AUTOLOAD. Defaults +to 'view_'. + + [% USE view %] + [% view.view_header() %] # => view('header') + + [% USE view(view_prefix => 'show_me_the_' %] + [% view.show_me_the_header() %] # => view('header') + +=item view_naked + +Flag to indcate if any attempt should be made to map method names to +template names where they don't match the view_prefix. Defaults to 0. + + [% USE view(view_naked => 1) %] + + [% view.header() %] # => view('header') + +=back + +=head2 print( $obj1, $obj2, ... \%config) + +TODO + +=head2 view( $template, \%vars, \%config ); + +TODO + +=head1 AUTHOR + +Andy Wardley E<lt>abw@kfs.orgE<gt> + +=head1 REVISION + +$Revision: 2.9 $ + +=head1 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. + +=head1 SEE ALSO + +L<Template::Plugin|Template::Plugin>, + +=cut + + + + + |
