File Coverage

lib/Any/Renderer/Template.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Any::Renderer::Template;
2              
3             # $Id: Template.pm,v 1.21 2006/09/04 12:15:53 johna Exp $
4              
5 1     1   5 use vars qw($VERSION %Formats $Cache $CacheMaxItems $CacheMaxAtime $CachePurgeInterval $CacheLastPurged);
  1         2  
  1         97  
6              
7 1     1   390 use Any::Template;
  0            
  0            
8             use Cache::AgainstFile;
9             use strict;
10              
11             use constant MUST_COMPILE => $ENV{ANY_RENDERER_AT_SAFE};
12              
13             $VERSION = sprintf"%d.%03d", q$Revision: 1.21 $ =~ /: (\d+)\.(\d+)/;
14              
15             #Package-level cache and settings
16             $Cache = undef; #Generated on first use
17             $CacheMaxItems = 1000;
18             $CacheMaxAtime = 6 * 60 * 60; # seconds (6 hours)
19             $CachePurgeInterval = 60 * 60 ; # seconds (1 hour)
20             $CacheLastPurged = undef;
21              
22             sub new
23             {
24             my ( $class, $format, $options ) = @_;
25             die("You must specify a format in the Any::Renderer::Template constructor") unless(defined $format && length $format);
26              
27             unless($Formats{$format}) {
28             _scan_available_formats(); #Discover if it's appeared recently
29             die("The format '$format' doesn't appear to be supported by Any::Template") unless($Formats{$format});
30             }
31              
32             #Separate options for this module from options passed through to backend
33             $options ||= {};
34             my $backend_options = $options->{TemplateOptions} || {
35             map {$_ => $options->{$_}}
36             grep {$_ !~ /^(Template|TemplateFilename|TemplateString|NoCache)$/}
37             keys %$options
38             };
39              
40             $Cache ||= _init_cache ();
41              
42             my $self = {
43             'format' => $format,
44             'template_file' => $options->{'Template'} || $options->{'TemplateFilename'},
45             'template_string' => $options->{'TemplateString'},
46             'options' => $backend_options,
47             'cache' => $options->{'NoCache'}? undef: $Cache,
48             };
49              
50             die("You must specify either a template filename or string containing a template") unless($self->{template_file} || defined $self->{template_string});
51              
52             bless $self, $class;
53             return $self;
54             }
55              
56             # load the template via Any::Template or Cache::AgainstFile
57             sub render
58             {
59             my ( $self, $data ) = @_;
60              
61             my $template;
62             my $template_file = $self->{template_file};
63             my $template_string = $self->{template_string};
64             if ( $template_file )
65             {
66             if( $self->{cache} )
67             {
68             TRACE ( "Loading template '" . $template_file . "' via Cache" );
69             $template = $self->{cache}->get($template_file, $self->{ 'format' }, $self->{ 'options' });
70             _purge_cache($self->{cache}) if(time - $CacheLastPurged > $CachePurgeInterval);
71             }
72             else
73             {
74             TRACE ( "No cache found, loading template via _template_from_file" );
75             $template = _template_from_file ( $template_file, $self->{'format'}, $self->{'options'} );
76             }
77             }
78             elsif ( $template_string )
79             {
80             TRACE ( "Using in-memory template and new Any::Template" );
81             $template = _template_from_string($template_string, $self->{'format'}, $self->{'options'});
82             }
83             else
84             {
85             die ( "No template provided!" );
86             }
87              
88             return $template->process ( $data );
89             }
90              
91             # returns whether or not this format requires a template
92             sub requires_template
93             {
94             return 1; #True in all cases
95             }
96              
97             # return a list reference of the formats that we handle
98             sub available_formats
99             {
100             _scan_available_formats();
101             return [ sort keys %Formats ];
102             }
103              
104             #
105             # Private routines
106             #
107              
108             # a sub-routine for loading of templates
109             sub _template_from_file
110             {
111             my ( $filename, $format, $options ) = @_;
112              
113             TRACE ( "_template_from_file : loading '$filename' as '$format'" );
114             DUMP ( "options", $options );
115              
116             my %ops = (
117             'Options' => $options,
118             'Filename' => $filename,
119             );
120             $ops{'Backend'} = $format unless($format eq 'Any::Template');
121              
122             return new Any::Template ( \%ops );
123             }
124              
125             sub _template_from_string
126             {
127             my($string, $format, $options) = @_;
128             my %ops = (
129             'Options' => $options,
130             'String' => $string,
131             );
132             $ops{'Backend'} = $format unless($format eq 'Any::Template');
133              
134             return new Any::Template ( \%ops );
135             }
136              
137             sub _scan_available_formats
138             {
139             my $formats = Any::Template::available_backends();
140             if (MUST_COMPILE) {
141             @$formats = grep {
142             eval {new Any::Template({Backend => $_, String => ""})} , !$@
143             } @$formats;
144             }
145             %Formats = map {$_ => 1} (@$formats, 'Any::Template');
146             }
147              
148             #
149             # Cache management
150             #
151              
152             sub _init_cache
153             {
154             $CacheLastPurged = time();
155             return new Cache::AgainstFile ( \&_template_from_file, {
156             'Method' => 'Memory',
157             'Grace' => 0, # seconds
158             'MaxATime' => $CacheMaxAtime,
159             'MaxItems' => $CacheMaxItems,
160             } );
161             }
162              
163             sub _purge_cache
164             {
165             my $cache = shift;
166             return unless defined $cache;
167             $cache->purge();
168             $CacheLastPurged = time();
169             }
170              
171              
172             sub TRACE {}
173             sub DUMP {}
174              
175             1;
176              
177             =head1 NAME
178              
179             Any::Renderer::Template - render data structure using a template
180              
181             =head1 SYNOPSIS
182              
183             use Any::Renderer;
184              
185             my %options = ( 'Template' => 'path/to/template.tmpl' );
186              
187             my $format = "HTML::Template";
188             my $r = new Any::Renderer ( $format, \%options );
189              
190             my $data_structure = [...]; # arbitrary structure code
191             my $string = $r->render ( $data_structure );
192              
193             You can get a list of all formats that this module handles using the following syntax:
194              
195             my $list_ref = Any::Renderer::Template::available_formats ();
196              
197             Also, determine whether or not a format requires a template with requires_template:
198              
199             my $bool = Any::Renderer::Template::requires_template ( $format );
200              
201             =head1 DESCRIPTION
202              
203             Any::Renderer::Template renders any Perl data structure passed to it with
204             Any::Template. The Any::Template backend used depends on the 'format' parameter passed to
205             the object constructor.
206              
207             Templates expressed as filenames are cached using a package-level in-memory cache with Cache::AgainstFile.
208             This will stat the file to validate the cache before using the cached object, so if the template is updated,
209             this will be immediately picked up by all processes holding a cached copy.
210              
211             =head1 FORMATS
212              
213             All the formats supported by Any::Template. Try this to find out what's available on your system:
214              
215             perl -MAny::Renderer::Template -e "print join(qq{\n}, sort @{Any::Renderer::Template::available_formats()})"
216              
217             An B format is also provided. This uses the default backend (as specified in the ANY_TEMPLATE_DEFAULT environment variable).
218              
219             =head1 METHODS
220              
221             =over 4
222              
223             =item $r = new Any::Renderer::Template($format,\%options)
224              
225             See L for a description of valid values for C<$format>.
226             See L for a description of valid C<%options>.
227              
228             =item $scalar = $r->render($data_structure)
229              
230             The main method.
231              
232             =item $bool = Any::Renderer::Template::requires_template($format)
233              
234             This will be true for these formats.
235              
236             =item $list_ref = Any::Renderer::Template::available_formats()
237              
238             This will discover the formats supported by your Any::Template installation.
239              
240             =back
241              
242             =head1 OPTIONS
243              
244             =over 4
245              
246             =item Template (aka TemplateFilename)
247              
248             Name of file containing template. Mandatory unless TemplateString is defined.
249              
250             =item TemplateString
251              
252             String containing template. Mandatory unless Template or TemplateFilename is defined.
253              
254             =item NoCache
255              
256             Suppress in-memory caching of templates loaded from the filesystem.
257              
258             =item TemplateOptions
259              
260             A hashref of options for the backend templating engine.
261              
262             If C is not explicitly specified,
263             all options passed to this module that are not recognised will be passed through
264             Any::Template (via the C constructor option) to the backend templating engine for the rendering process.
265             This flatter options structure may be more convenient but does introduce the risk of a nameclash between an option name in an obscure back-end templating module
266             and an option specific to Any::Render::Template - it's your choice.
267              
268             Further information on the options for each backend module can be found in the documentation for Any::Template::$backend
269             or the documentation for the backend templating module itself.
270              
271             =back
272              
273             =head1 GLOBAL VARIABLES
274              
275             The package-level template cache is created on demand the first time it's needed.
276             There are a few global variables which you can tune before it's created (i.e. before you create any objects):
277              
278             =over 4
279              
280             =item $Any::Renderer::Template::CacheMaxItems
281              
282             Maximum number of template objects held in the cache. Default is 1000.
283              
284             =item $Any::Renderer::Template::CacheMaxAtime
285              
286             Items older than this will be purged from the cache when the next purge() call happens. In Seconds. Default is 6 hours.
287              
288             =item $Any::Renderer::Template::CachePurgeInterval
289              
290             How often to purge the cache. In Seconds. Default is 1 hour.
291              
292             =back
293              
294             =head1 ENVIRONMENT
295              
296             Set the C environment variable to a true value if you want to check each Any::Template
297             backend compiles before adding it to the list of available formats.
298             This is safer in that modules with missing dependencies are not advertised as available but it incurs a
299             CPU and memory overhead.
300              
301             =head1 SEE ALSO
302              
303             L, L, L
304              
305             =head1 VERSION
306              
307             $Revision: 1.21 $ on $Date: 2006/09/04 12:15:53 $ by $Author: johna $
308              
309             =head1 AUTHOR
310              
311             Matt Wilson and John Alden
312              
313             =head1 COPYRIGHT
314              
315             (c) BBC 2006. This program is free software; you can redistribute it and/or modify it under the GNU GPL.
316              
317             See the file COPYING in this distribution, or http://www.gnu.org/licenses/gpl.txt
318              
319             =cut