package CGI::Application::Plugin::AnyTemplate::Driver::TemplateToolkit;
=head1 NAME
CGI::Application::Plugin::AnyTemplate::Driver::TemplateToolkit - Template::Toolkit plugin to AnyTemplate
=head1 DESCRIPTION
This is a driver for L<CGI::Application::Plugin::AnyTemplate>, which
provides the implementation details specific to rendering templates via
the L<Template::Toolkit|Template> templating system.
All C<AnyTemplate> drivers are designed to be used the same way. For
general usage instructions, see the documentation of
L<CGI::Application::Plugin::AnyTemplate>.
=head1 EMBEDDED COMPONENT SYNTAX (Template::Toolkit)
The L<Template::Toolkit|Template> syntax for embedding components is:
[% CGIAPP.embed("some_run_mode", param1, param2, 'literal string3') %]
This can be overridden by the following configuration variables:
embed_tag_name # default 'CGIAPP'
For instance by setting the following values in your configuration file:
embed_tag_name 'MYAPP'
Then the embedded component tag will look like:
[% MYAPP.embed("some_run_mode") %]
=head1 TT OBJECT CACHING (singleton support)
=head2 Introduction
In a persistent environment, rather than creating a L<Template::Toolkit|Template>
object each time you fill a template, it is much more efficient to load
a single L<Template::Toolkit|Template> object and use this object to render all
of your templates.
However, in a persistent environment, you may have several different
applications running, and they all might need to set different
L<Template::Toolkit|Template> options (such as C<POST_CHOMP>, etc.).
By default, when the C<TemplateToolkit> driver creates a
L<Template::Toolkit|Template> object, it caches it. From that point on, whenever
the same application needs a L<Template::Toolkit|Template> object, the driver
uses the cached object rather than creating a new one.
=head2 Multiple Applications in a Shared Persistent Environment
An attempt is made to prevent different applications from
sharing the same TT object.
Internally, the TT objects are stored in a private hash keyed by the web
application's class name.
You can explicitly specify the class name when you call C<config>:
$self->template->config(
type => 'TemplateToolkit',
TemplateToolkit => {
storage_class => 'My::Project',
},
);
If you don't specify the class name, then the package containing the subroutine
that called C<config> is used. For instance:
package My::Project;
sub setup {
my $self = shift;
$self->template->config( # My::Project is used to store
type => 'TemplateToolkit', # cached TT object
);
}
A typical C<CGI::Application> module hierarchy looks like this:
CGI::Application
My::Project
My::Webapp
In this hierarchy, it makes sense to store the cached TT object in
C<My::Project>. To make this happen, either call C<< $self->template->config >>
from within C<My::Project>, or explicitly name the C<storage_class> when you call
C<< $self->template->config >>.
=head2 Disabling TT Object Caching
You can disable L<Template::Toolkit|Template> object caching entirely by
providing a false value to the C<object_caching> driver config
parameter:
$self->template->config(
type => 'TemplateToolkit',
TemplateToolkit => {
object_caching => 0,
},
);
=head2 TT Object Caching and Include Paths
The C<include_paths> driver config parameter is not cached; it is set
every time you call C<< $self->template->load >>. So you can safely used
cached TT objects even if the applications sharing the TT object need
different C<include_paths>.
=cut
use strict;
use Carp;
use CGI::Application::Plugin::AnyTemplate::ComponentHandler;
use CGI::Application::Plugin::AnyTemplate::Base;
use vars qw(@ISA);
@ISA = ('CGI::Application::Plugin::AnyTemplate::Base');
=head1 CONFIGURATION
The L<CGI::Application::Plugin::AnyTemplate::Driver::TemplateToolkit> driver
accepts the following config parameters:
=over 4
=item embed_tag_name
The name of the tag used for embedding components. Defaults to
C<CGIAPP>.
=item template_extension
If C<auto_add_template_extension> is true, then
L<CGI::Application::Plugin::AnyTemplate> will append the value of
C<template_extension> to C<filename>. By default
the C<template_extension> is C<.xhtml>.
=item emulate_associate_query
B<This feature is now deprecated and will be removed in a future release.>
If this config parameter is true, then L<CGI::Application::Plugin::AnyTemplate::Driver::TemplateToolkit>
will copy all of the webapp's query params into the template.
This is similar to what would happen if you used L<HTML::Template>'s
C<associate> feature with the webapp's query object:
my $driver = HTML::Template->new(
associate => $self->query,
);
By default C<emulate_associate_query> is false.
=item object_caching
Whether or not to cache the L<Template::Toolkit|Template> object in a persistent environment
By default, C<object_caching> is enabled.
See L<"TT OBJECT CACHING (singleton support)">, above.
=item storage_class
What class to use as the storage key when object caching is enabled.
By default, C<storage_class> defaults to the package containing the
subroutine that called C<< $self->template->config >>.
See L<"TT OBJECT CACHING (singleton support)">, above.
=back
All other configuration parameters are passed on unchanged to L<Template::Toolkit|Template>.
=head1 CONFIGURING UTF-8 TEMPLATES
C<AnyTemplate> does NOT support L<Template::Toolkit|Template>'s C<binmode> option at runtime:
# not possible with AnyTemplate
$tt->process($infile, $vars, $outfile, { binmode => 1 })
|| die $tt->error(), "\n";
# not possible with AnyTemplate
$tt->process($infile, $vars, $outfile, binmode => 1)
|| die $tt->error(), "\n";
# not possible with AnyTemplate
$tt->process($infile, $vars, $outfile, binmode => ':utf8')
|| die $tt->error(), "\n";
Instead, use the C<ENCODING> option in the initial config:
$self->template->config(
default_type => 'TemplateToolkit',
TemplateToolkit => {
ENCODING => 'UTF-8'
}
);
If you have a mix of encodings in your templates, use a separate
C<AnyTemplate> configuration for each encoding:
$self->template('ascii')->config(
default_type => 'TemplateToolkit',
);
$self->template('utf-8')->config(
default_type => 'TemplateToolkit',
TemplateToolkit => {
ENCODING => 'UTF-8'
}
);
=cut
sub driver_config_keys {
qw/
storage_class
object_caching
cache_storage_keys
embed_tag_name
template_extension
emulate_associate_query
/;
}
sub default_driver_config {
(
object_caching => 1,
template_extension => '.tmpl',
embed_tag_name => 'CGIAPP',
emulate_associate_query => 0,
);
}
=head2 required_modules
The C<required_modules> function returns the modules required for this driver
to operate. In this case: C<Template>.
=cut
sub required_modules {
return qw(
Template
);
}
=head1 DRIVER METHODS
=over 4
=item initialize
Initializes the C<TemplateToolkit> driver. See the docs for
L<CGI::Application::Plugin::AnyTemplate::Base> for details.
=cut
# create the Template::Toolkit object,
# using:
# $self->{'driver_config'} # config info
# $self->{'include_paths'} # the paths to search for the template file
# $self->filename # the template file
my %TT_Object_Store;
sub initialize {
my $self = shift;
$self->_require_prerequisite_modules;
my %config = %{ $self->{'native_config'} };
$config{'INCLUDE_PATH'} = $self->{'include_paths'};
my $driver;
my $storage_class = $self->{'driver_config'}{'storage_class'};
$storage_class ||= $self->{'callers_package'};
my $config_name = $self->{'conf_name'};
if ($self->{'driver_config'}{'object_caching'} and exists $TT_Object_Store{$storage_class}) {
if (defined $config_name) {
$driver = $TT_Object_Store{$storage_class}{'named'}{$config_name};
}
else {
$driver = $TT_Object_Store{$storage_class}{'default'};
}
}
if (!$driver) {
$driver = Template->new(\%config);
}
if ($self->{'driver_config'}{'object_caching'}) {
if (defined $config_name) {
$TT_Object_Store{$storage_class}{'named'}{$config_name} = $driver;
}
else {
$TT_Object_Store{$storage_class}{'default'} = $driver;
}
}
# Stolen from Cees's CAP::TT
$driver->context->load_templates->[0]->include_path($self->{'include_paths'});
$self->{'driver'} = $driver;
}
=item render_template
Fills the L<Template::Toolkit|Template> object with C<< $self->param >>
If the param C<emulate_associate_query> is true, then set params for
each of $self->{'webapp'}->query, mimicking L<HTML::Template>'s
associate mechanism.
Also set up a L<CGI::Application::Plugin::AnyTemplate::ComponentHandler>
object so that the C<CGIAPP.embed> callback will work.
Returns the output of the filled template as a string reference.
See the docs for L<CGI::Application::Plugin::AnyTemplate::Base> for details.
=back
=cut
sub render_template {
my $self = shift;
my $driver_config = $self->{'driver_config'};
my $template = $self->{'driver'};
my $output = '';
# emulate HTML::Template's 'associate' behaviour
if ($driver_config->{'emulate_associate_query'}) {
my $params = $self->get_param_hash;
if ($self->{'webapp'}) {
foreach ($self->{'webapp'}->query->param) {
$params->{$_} ||= $self->{'webapp'}->query->param($_);
}
}
}
my $component_handler = $self->{'component_handler_class'}->new(
'webapp' => $self->{'webapp'},
'containing_template' => $self,
);
my $params = $self->get_param_hash;
$params->{$driver_config->{'embed_tag_name'}} = $component_handler;
my $filename = $self->filename;
my $string_ref = $self->string_ref;
$string_ref or $filename or croak "TemplateToolkit: file or string must be specified";
$template->process(($string_ref || $filename), $params, \$output) || croak $template->error;
return \$output;
}
=head1 SEE ALSO
CGI::Application::Plugin::AnyTemplate
CGI::Application::Plugin::AnyTemplate::Base
CGI::Application::Plugin::AnyTemplate::ComponentHandler
CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplate
CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplateExpr
CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplatePluggable
CGI::Application::Plugin::AnyTemplate::Driver::Petal
CGI::Application
Template::Toolkit
HTML::Template
HTML::Template::Pluggable
HTML::Template::Plugin::Dot
Petal
Exporter::Renaming
CGI::Application::Plugin::TT
=head1 ACKNOWLEDGEMENTS
Thanks to Cees Hek for discussing the issues of caching in a persistent
environment. And also for his excellent L<CGI::Application::Plugin::TT>
module, from which I stole ideas and some code: especially the bit
about how to change the include path in a TT object after you've
initialized it.
=head1 AUTHOR
Michael Graham, C<< <mgraham@cpan.org> >>
=head1 COPYRIGHT & LICENSE
Copyright 2005 Michael Graham, All Rights Reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut
1;