NAME
Template::Plugin::TwoStage - two stage processing of template blocks
with first stage caching
VERSION
version 0.08
SYNOPSIS
This is a plugin for the Template Toolkit that facilitates a two stage
processing of BLOCKs/templates with first stage caching. Processing
results of the first (precompilation) stage are cached away for
subsequent repeated processing in the second (runtime) stage.
Precompilation and runtime tags are seperated by using different tag
styles.
Sample Configuration via TT configuration:
Template->new( { ..., TwoStage => { caching_dir => My::Application->path_to_tmp(), ... } } );
Basic usage in the a TT2-template:
[% USE TwoStage;
TwoStage.process(
template => 'cached_page',
keys => { bar => bar },
ttl => 60 * 60
);
BLOCK cached_page;
# use precompile tags or runtime tags here
%]
[* foo # runtime stage evaluation *]
[% IF bar; # precompilation stage evaluation
# ...
ELSE;
# ...
END;
END
%]
FEATURES
* parameterized precompilation of a single block
Pass keys as additional identifiers of a BLOCK/template, based upon
which the BLOCK/template produces a different precompiled
output/version.
* expiration
Give the precompiled BLOCKs/templates a 'time to live' before they
expire.
* namespaces
Distinguish different e.g. applications sharing a common TwoStage
plugin (sub)class and/or caching directory by the use of namespaces.
* development mode
Edit your templates with caching turned off. The development mode
also gives you convenient access to the precompiled versions
produced for validation of your separation of precompilation and
runtime directives.
* flexible configuration
Set your basic configuration in the TT configuration, or in a
subclass of this plugin, and override any configuration option on
plugin construction or even on single plugin method calls to
include() or process() in the templates.
USE CASES
You might benefit from this module if ...
* ... you have static external content e.g. from databases or
.po-files that you want to pull into your templates only once while
still being able to insert dynamic data into the same template
* ... you are following the DRY principle by having a central BLOCK
library that you use to style your GUI HTML components
* ... you do not want to use ttree because you prefer "lazy"
precompilation (only on demand), and you want to see your changes to
the template without running an external program first
CONFIGURATION
Plugin behaviour can be controlled using following options:
Options
caching_dir
The TwoStage plugin will store the resulting precompiled templates from
the first stage on disk in a 'caching directory', that can be specified
with the 'caching_dir' option.
The directory 'TT_P_TwoStage' in your platform specific tmp-directory
determined with the help of File::Spec->tmpdir() is the default setting
for this option. Pass a path in order to change it to some other
directory - it will also be automatically extended by the subdirectories
'TT_P_TwoStage' and the plugin's class name.
This option can NOT be set on plugin construction or calls to
include()/process().
dev_mode
Set this configuration option to a TRUE value in order to disable the
use of cached - precompiled - files, and see your changes to cached
BLOCKs/templates immediately while still having access to the
precompiled versions on disk for their validation.
See also the configuration option 'dir_keys' as another interesting
feature for development.
ttl
Specify the "time to live" for the precompiled versions of the
BLOCKs/templates in seconds - 0 is 'no expiration' and the default
setting.
dir_keys
Usually the keys connected to a precompiled version are included among
other things into the file name of a BLOCK/template in order to identify
a precompiled cached BLOCK/template on disk. This is accomplished by
using the SHA1 hash function.
To make the retrieval of a certain caching file easier for humans, the
configuration parameter 'dir_keys' lets you include the keys into the
file path of the precompiled cached BLOCK/template. This behaviour might
be handy in cases where one wants to inspect the precompiled versions
produced.
Set 'dir_keys' either to an array reference holding a selection of keys
or a scalar holding a TRUE value for all keys. This feature is available
in development mode only! See also configuration option 'dev_mode'.
See also the section 'Caching with plugin object methods process() and
include()' for more on the 'keys' parameter.
namespace
This option can come to rescue in situations where identities of cached
BLOCK/template versions on disk can be ambigious relying only on the
standard measures taken by this plugin in order to disambiguate cached
disk versions (see section 'Avoiding disk cache overlaps' below).
If e.g. you choose not to subclass this module for an application you
can ensure the segmentation of applications by setting the 'namespace'
configuration option accordingly. In this example however this approach
has the drawback that you need to set this configuration option in each
template on plugin construction:
USE TwoStage( namespace => application_name );
runtime_tag_style
Set this option to one of the predefined tag styles TT is offering like
'php', 'mason', 'html', ..., and that are accepted by TT as a value to
its 'TAG_STYLE' configuration option or 'TAGS' directive. Default is:
star ([* *]).
Excursus: precompilation tag style
The precompilation tag style is always the tag style set in the TT
configuration or 'default'. A tag style defined local to the file the
plugin is being called in ( by means of the 'TAGS'-directive at the
beginning of the file) will be handled correctly - this file scoped tag
style will also be used in the BLOCK to be precompiled as precompilation
tag style.
Changing the tag style only for a certain BLOCK that is to be
precompiled is not possible, as the 'TAGS' directive can be set only on
per template file basis. A centralized configuration of the
precompilation tag style to be used is not available to date.
tt_cache_size
As one can produce a lot of precompiled versions of a single
BLOCK/template controlled by this plugin using the 'keys' feature of
process()/include(), it might be advisable in some situations to set the
CACHE_SIZE TT configuration option to a positive value in order to curb
memory consumption when having a TT singleton object around in a
persistent environment.
As the plugin uses a provider object specific to the plugin it will not
respect the CACHE_SIZE configuration property possibly set in your main
TT configuration. Use this method in order to set the CACHE_SIZE
configuration property for the plugin's specific provider object. By
default all precompiled templates are cached.
In contrast to all the other configuration options - except
'caching_dir' - this option can only be set as class data.
Setting options
Configuration options may be set in different ways and with different
scopes.
Setting options in TT configuration
This plugin can be configured using the configuration hash provided to
the Template Toolkit-constructor. Put your plugin configuration options
in a hash, and reference it in the TT configuration hash using the key
'TwoStage'.
Sample code:
Template->new( { ..., TwoStage => { namespace => 'My::Application', ... } } );
The configuration options will apply to all TwoStage plugin-objects
created in this TT object. Note that this does NOT hold true for derived
classes of the plugin!
Note! In order to avoid disk cache overlaps of applications using the
TwoStage plugin without subclassing, configure a unique namespace or a
separate caching directory. See also 'Avoiding disk cache overlaps'
below.
Setting options via subclassing
This plugin is subclassable. Configuration with scope restricted to the
subclass can be achieved by using the inheritable class data accessors
provided (see sample code below).
Subclassing allows you to customize your plugin behaviour at a single
place, and to complement the caching keys via a possible callback method
extend_keys(). At the same time it avoids disk cache overlaps for
applications sharing the same caching directory as long as the derived
plugin is not used in different applications.
Subclasses will not read the plugin options specified in the TT
configuration hash when configuring.
Sample code:
# sample code is based on an imaginary Catalyst application Your::Application
package Your::Application::Template::Plugin::TwoStage;
use base qw( Template::Plugin::TwoStage );
__PACKAGE__->caching_dir( Your::Application->config->{home}.'/tmp' );
__PACKAGE__->dev_mode( 1 );
__PACKAGE__->ttl( 60 * 60 ); # 1 h
__PACKAGE__->dir_keys( 1 );
__PACKAGE__->runtime_tag_style( 'html' );
sub extend_keys {
my $plugin = shift; # the callback receives the plugin object as 1st parameter
my $s = $plugin->{CONTEXT}->stash;
# get Catalyst context from stash
my $c = $s->get( 'c' );
my $r = $c->request;
# hook method for adding standard keys - return the keys => value -hash by reference!
{ domain => $r->uri->authority,
method => $r->method,
logged_in => $c->user_exists
};
}
Don't forget to add your subclass to the plugin base of your
TT2-configuration:
PLUGIN_BASE => [ 'Your::Application::Template::Plugin' ]
or declare it via the PLUGINS TT2-configuration option
PLUGINS => { TwoStage => 'Your::Application::Template::Plugin::TwoStage', ... }
Setting options as named parameters to USE or include()/process()
Any option set via subclassing or the TT configuration hash can be
overridden on plugin construction ( 'USE TwoStage()' ) or even on the
call of the include() or precompile()-methods at your will with only
restricted scope. Configuration provided on plugin construction will
apply to this specific plugin object only; configuration provided as
parameters to include()/process() only to the specific plugin object
call.
Sample code:
USE TwoStage( ttl => 1000 );
TwoStage.process( template => 'some', ttl => 0 );
Note! The following options can not be overridden this way: caching_dir,
tt_cache_size
Object hook methods
extend_keys
With this callback method it is possible to merge some default keys into
the template signature. The values of the keys introduced this way will
be dominated by the values of identical keys passed to process() or
include(). Return a hash reference mapping standard signature keys to
its values!
Have a look at the sample code above (section 'Setting options via
subclassing').
When setting options in the TT configuration hash simply add a reference
to your extend_keys-function with key 'extend_keys'. It will receive the
plugin object as the first parameter when being called.
Template->new(
{ ...,
TwoStage => {
extend_keys => sub {
my $plugin = shift;
return { key => $value };
}, ...
}
}
);
Avoiding disk cache overlaps
Having precompiled a BLOCK/template the TwoStage plugin tries to assign
an unique as possible identity to it - refered to as 'signature' later
-, and writes it to disk into the configured caching directory (see also
corresponding option 'caching_dir') using a SHA1 fingerprint of the
signature as its file name.
What is the unique signature made up of and how unique is it?
* template path
If the component processed with the TwoStage plugin is a template, the
template's path is included in the signature.
Template paths are relative to a template provider object configured
and might also be relative to some entry in the provider's
INCLUDE_PATH or an equivalent (this does not apply to providers
configured to serve templates by absolute path obviously).
* BLOCK name
If the component processed with the TwoStage plugin is a BLOCK its
name is included into the signature. In order to make the BLOCK name
template spanning unique we augment the BLOCK name with the values of
TT meta variables 'component.callers' and 'component.name' - which in
turn is the call stack of BLOCKs and templates to the BLOCK from the
outermost file the BLOCK was included in.
The uniqueness of BLOCK names achieved however is limited as it is
still relative to the template providers and their configuration (see
previous item).
* plugin class name
The precompiled BLOCK/template versions will be saved in a
subdirectory under the caching directory specified and named after the
class/package name of the TwoStage plugin.
* keys passed to process()/include()
See section 'CACHING / Caching with process() and include()' below.
Implications
* Unless you really know about the consequences always set the
configuration option 'caching_dir' to a filesystem directory specific
to the TwoStage plugin subclasses used or to the TT object in case the
plugin is used directly.
* Use the namespace option or add an equivalent key via the
extend_keys() callback function if you have dynamic entries in the
INCLUDE_PATH or an equivalent that reflects the current state auf the
INCLUDE_PATH.
CACHING
Caching with plugin object methods process() and include()
Once the plugin object has been pulled into the template by means of the
'USE' directive, calling the plugin object methods include() or
process() against it will insert the BLOCK/template content with all the
precompilation and caching magic delivered by this plugin into the
template.
Named parameters of process/include:
* template
Specify the name of the BLOCK/template to be processed/included into
the template here. Its name does not have to be template spanning
unique. The plugin takes care that the name is local to the template
it is defined in.
* keys (optional)
Use this parameter in situations where you want to evaluate a
certain stash variable in the precompilation stage, and that
variable can take on only a limited set of discrete values in the
runtime stage but has considerable influence on the precompiled
versions. Examples for such variables might be: template language,
user preferences, user privileges, ...
Each combination of the values of the variables passed as 'keys'
parameter will produce a distinct precompiled version of the
BLOCK/template in question. Take care not to choose to many keys
with to many values in order to produce only a reasonable number of
precompiled versions.
If you find some keys are supposed to be added to each and every
call to process() or include() consider using the extend_keys() hook
method (see also above).
* all of the available configuration options (optional)
For more on those options see section "Configuration Options" in
this documentation.
include() is exposing an identical behaviour as process() with the
exception that it does stash localisation in the runtime stage.
Reset cached content with purge()
In order to set back caching remove the cached templates from your
caching directory. Maybe a script to assist you in this task will be
shipped together with a future version of this plugin.
Using purge() you remove all files from the caching directory of the
class - use this to set back caching from within templates. This method
is used mainly in the self tests of this module. Maybe there are even
more useful applications for it - so it became a public class method.
[% TwoStage = USE TwoStage;
TwoStage.purge;
%]
STAT_TTL and caching
Please note that the STAT_TTL configuration of TT will not work for
cached BLOCKs/templates as you make modifications to the "source"
BLOCKS/templates of those cached templates. You should turn off caching
instead using the plugin option 'dev_mode'.
EXPORTS
TT configuration
The compiled plugin configuration is saved with key '_TwoStage' in the
main TT configuration hash.
TT stash
There is a temporary stash entry 'TwoStage_precompile_mode = 1;' in the
precompilation stage of template processing.
HEURISTICS
In order to avoid common pitfalls when using this module you find some
tips and reminders below:
* Templates used as fragments with runtime directives ought to be
controlled by the TwoStage plugin themselves! This ensures that such
templates can be included into another template either at runtime or
at precompilation stage.
* Upstream keys from included templates ( fragments ) must be
incorporated into the 'keys' option of the including template!
Explanation: They have to be known ex ante meaning prior to a test
for a cached version of a template and can therefor not easily be
collected from upstream templates automatically!
* Situation: A template A includes another template B while both are
using the TwoStage plugin. In addition you pass parameters on
invocation of INCLUDE, PROCESS to template B. Add those parameters
to the 'keys' option when calling the TwoStage plugin in template B
and use them at precompilation stage. This way you can include
template B at runtime and at precompilation at your will.
* Ensure there are no BLOCK definitions inside the BLOCK to be
TwoStage processed! This is nothing specific to the TwoStage plugin
really, but is a common mistake. Simply put those BLOCKs outside the
BLOCK to be TwoStage processed. They will be visible to it anyway.
* When altering option 'dev_mode' on plugin object construction or on
calls to the methods process()/include() one has to be cautious in
situations where calls to the TwoStage plugin methods
process()/include() are nested: Remember to alter the option in the
outermost call to achieve the desired effect!
BUGS
Please report any bugs or feature requests to
"bug-template-plugin-twostage at rt.cpan.org", or through the web
interface at
<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Template-Plugin-TwoStage
>. I will be notified, and then you'll automatically be notified of
progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Template::Plugin::TwoStage
You can also look for information at:
* RT: CPAN's request tracker
<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Template-Plugin-TwoStage>
* AnnoCPAN: Annotated CPAN documentation
<http://annocpan.org/dist/Template-Plugin-TwoStage>
* CPAN Ratings
<http://cpanratings.perl.org/d/Template-Plugin-TwoStage>
* Search CPAN
<http://search.cpan.org/dist/Template-Plugin-TwoStage>
ACKNOWLEDGEMENTS
This module was inspired to some extent by Perrin Harkins
<http://search.cpan.org/dist/Template-Plugin-Cache> and not least by my
CO2 footprint.
SEE ALSO
Template::Plugin, <http://search.cpan.org/dist/Template-Plugin-Cache>
AUTHOR
Alexander Kühne <alexk@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2014 by Alexander Kühne.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.