1 #============================================================= -*-Perl-*-
6 # Module implementing a template processing service which wraps a
7 # template within PRE_PROCESS and POST_PROCESS templates and offers
11 # Andy Wardley <abw@wardley.org>
14 # Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
16 # This module is free software; you can redistribute it and/or
17 # modify it under the same terms as Perl itself.
19 #============================================================================
21 package Template::Service;
25 use base 'Template::Base';
27 use Template::Exception;
28 use Template::Constants;
29 use Scalar::Util 'blessed';
31 use constant EXCEPTION => 'Template::Exception';
34 our $DEBUG = 0 unless defined $DEBUG;
38 #========================================================================
39 # ----- PUBLIC METHODS -----
40 #========================================================================
42 #------------------------------------------------------------------------
43 # process($template, \%params)
45 # Process a template within a service framework. A service may encompass
46 # PRE_PROCESS and POST_PROCESS templates and an ERROR hash which names
47 # templates to be substituted for the main template document in case of
48 # error. Each service invocation begins by resetting the state of the
49 # context object via a call to reset(). The AUTO_RESET option may be set
50 # to 0 (default: 1) to bypass this step.
51 #------------------------------------------------------------------------
54 my ($self, $template, $params) = @_;
55 my $context = $self->{ CONTEXT };
56 my ($name, $output, $procout, $error);
59 $self->debug("process($template, ",
60 defined $params ? $params : '<no params>',
61 ')') if $self->{ DEBUG };
64 if $self->{ AUTO_RESET };
66 # pre-request compiled template from context so that we can alias it
67 # in the stash for pre-processed templates to reference
68 eval { $template = $context->template($template) };
69 return $self->error($@)
72 # localise the variable stash with any parameters passed
73 # and set the 'template' variable
75 # TODO: change this to C<||=> so we can use a template parameter
76 $params->{ template } = $template
77 unless ref $template eq 'CODE';
78 $context->localise($params);
83 foreach $name (@{ $self->{ PRE_PROCESS } }) {
84 $self->debug("PRE_PROCESS: $name") if $self->{ DEBUG };
85 $output .= $context->process($name);
88 last SERVICE if ($error = $@);
92 foreach $name (@{ $self->{ PROCESS } || [ $template ] }) {
93 $self->debug("PROCESS: $name") if $self->{ DEBUG };
94 $procout .= $context->process($name);
99 unless defined ($procout = $self->_recover(\$error));
102 if (defined $procout) {
105 foreach $name (reverse @{ $self->{ WRAPPER } }) {
106 $self->debug("WRAPPER: $name") if $self->{ DEBUG };
107 $procout = $context->process($name, { content => $procout });
110 last SERVICE if ($error = $@);
116 foreach $name (@{ $self->{ POST_PROCESS } }) {
117 $self->debug("POST_PROCESS: $name") if $self->{ DEBUG };
118 $output .= $context->process($name);
121 last SERVICE if ($error = $@);
124 $context->delocalise();
125 delete $params->{ template };
128 # $error = $error->as_string if ref $error;
129 return $self->error($error);
136 #------------------------------------------------------------------------
139 # Returns the internal CONTEXT reference.
140 #------------------------------------------------------------------------
143 return $_[0]->{ CONTEXT };
147 #========================================================================
148 # -- PRIVATE METHODS --
149 #========================================================================
152 my ($self, $config) = @_;
153 my ($item, $data, $context, $block, $blocks);
154 my $delim = $config->{ DELIMITER };
155 $delim = ':' unless defined $delim;
157 # coerce PRE_PROCESS, PROCESS and POST_PROCESS to arrays if necessary,
158 # by splitting on non-word characters
159 foreach $item (qw( PRE_PROCESS PROCESS POST_PROCESS WRAPPER )) {
160 $data = $config->{ $item };
161 $self->{ $item } = [ ], next unless (defined $data);
162 $data = [ split($delim, $data || '') ]
163 unless ref $data eq 'ARRAY';
164 $self->{ $item } = $data;
166 # unset PROCESS option unless explicitly specified in config
167 $self->{ PROCESS } = undef
168 unless defined $config->{ PROCESS };
170 $self->{ ERROR } = $config->{ ERROR } || $config->{ ERRORS };
171 $self->{ AUTO_RESET } = defined $config->{ AUTO_RESET }
172 ? $config->{ AUTO_RESET } : 1;
173 $self->{ DEBUG } = ( $config->{ DEBUG } || 0 )
174 & Template::Constants::DEBUG_SERVICE;
176 $context = $self->{ CONTEXT } = $config->{ CONTEXT }
177 || Template::Config->context($config)
178 || return $self->error(Template::Config->error);
184 #------------------------------------------------------------------------
185 # _recover(\$exception)
187 # Examines the internal ERROR hash array to find a handler suitable
188 # for the exception object passed by reference. Selecting the handler
189 # is done by delegation to the exception's select_handler() method,
190 # passing the set of handler keys as arguments. A 'default' handler
191 # may also be provided. The handler value represents the name of a
192 # template which should be processed.
193 #------------------------------------------------------------------------
196 my ($self, $error) = @_;
197 my $context = $self->{ CONTEXT };
198 my ($hkey, $handler, $output);
200 # there shouldn't ever be a non-exception object received at this
201 # point... unless a module like CGI::Carp messes around with the
204 unless blessed($$error) && $$error->isa(EXCEPTION);
206 # a 'stop' exception is thrown by [% STOP %] - we return the output
207 # buffer stored in the exception object
208 return $$error->text()
209 if $$error->type() eq 'stop';
211 my $handlers = $self->{ ERROR }
212 || return undef; ## RETURN
214 if (ref $handlers eq 'HASH') {
215 if ($hkey = $$error->select_handler(keys %$handlers)) {
216 $handler = $handlers->{ $hkey };
217 $self->debug("using error handler for $hkey") if $self->{ DEBUG };
219 elsif ($handler = $handlers->{ default }) {
220 # use default handler
221 $self->debug("using default error handler") if $self->{ DEBUG };
224 return undef; ## RETURN
228 $handler = $handlers;
229 $self->debug("using default error handler") if $self->{ DEBUG };
232 eval { $handler = $context->template($handler) };
235 return undef; ## RETURN
238 $context->stash->set('error', $$error);
240 $output .= $context->process($handler);
244 return undef; ## RETURN
252 #------------------------------------------------------------------------
255 # Debug method which return a string representing the internal object
257 #------------------------------------------------------------------------
261 my $context = $self->{ CONTEXT }->_dump();
262 $context =~ s/\n/\n /gm;
264 my $error = $self->{ ERROR };
267 (map { " $_ => $error->{ $_ }\n" }
275 PRE_PROCESS => [ @{ $self->{ PRE_PROCESS } } ]
276 POST_PROCESS => [ @{ $self->{ POST_PROCESS } } ]
289 Template::Service - General purpose template processing service
293 use Template::Service;
295 my $service = Template::Service->new({
296 PRE_PROCESS => [ 'config', 'header' ],
297 POST_PROCESS => 'footer',
299 user => 'user/index.html',
300 dbi => 'error/database',
301 default => 'error/default',
305 my $output = $service->process($template_name, \%replace)
306 || die $service->error(), "\n";
310 The C<Template::Service> module implements an object class for providing
311 a consistent template processing service.
313 Standard header (L<PRE_PROCESS|PRE_PROCESS_POST_PROCESS>) and footer
314 (L<POST_PROCESS|PRE_PROCESS_POST_PROCESS>) templates may be specified which
315 are prepended and appended to all templates processed by the service (but not
316 any other templates or blocks C<INCLUDE>d or C<PROCESS>ed from within). An
317 L<ERROR> hash may be specified which redirects the service to an alternate
318 template file in the case of uncaught exceptions being thrown. This allows
319 errors to be automatically handled by the service and a guaranteed valid
320 response to be generated regardless of any processing problems encountered.
322 A default C<Template::Service> object is created by the L<Template> module.
323 Any C<Template::Service> options may be passed to the L<Template>
324 L<new()|Template#new()> constructor method and will be forwarded to the
325 L<Template::Service> constructor.
329 my $template = Template->new({
330 PRE_PROCESS => 'header',
331 POST_PROCESS => 'footer',
334 Similarly, the C<Template::Service> constructor will forward all configuration
335 parameters onto other default objects (e.g. L<Template::Context>) that it may
338 A C<Template::Service> object (or subclass) can be explicitly instantiated and
339 passed to the L<Template> L<new()|Template#new()> constructor method as the
343 use Template::Service;
345 my $service = Template::Service->new({
346 PRE_PROCESS => 'header',
347 POST_PROCESS => 'footer',
350 my $template = Template->new({
354 The C<Template::Service> module can be sub-classed to create custom service
358 use MyOrg::Template::Service;
360 my $service = MyOrg::Template::Service->new({
361 PRE_PROCESS => 'header',
362 POST_PROCESS => 'footer',
363 COOL_OPTION => 'enabled in spades',
366 my $template = Template->new({
370 The L<Template> module uses the L<Template::Config>
371 L<service()|Template::Config#service()> factory method to create a default
372 service object when required. The C<$Template::Config::SERVICE> package
373 variable may be set to specify an alternate service module. This will be
374 loaded automatically and its L<new()> constructor method called by the
375 L<service()|Template::Config#service()> factory method when a default service
376 object is required. Thus the previous example could be written as:
380 $Template::Config::SERVICE = 'MyOrg::Template::Service';
382 my $template = Template->new({
383 PRE_PROCESS => 'header',
384 POST_PROCESS => 'footer',
385 COOL_OPTION => 'enabled in spades',
392 The C<new()> constructor method is called to instantiate a C<Template::Service>
393 object. Configuration parameters may be specified as a HASH reference or
394 as a list of C<name =E<gt> value> pairs.
396 my $service1 = Template::Service->new({
397 PRE_PROCESS => 'header',
398 POST_PROCESS => 'footer',
401 my $service2 = Template::Service->new( ERROR => 'error.html' );
403 The C<new()> method returns a C<Template::Service> object or C<undef> on
404 error. In the latter case, a relevant error message can be retrieved by the
405 L<error()|Template::Base#error()> class method or directly from the
406 C<$Template::Service::ERROR> package variable.
408 my $service = Template::Service->new(\%config)
409 || die Template::Service->error();
411 my $service = Template::Service->new(\%config)
412 || die $Template::Service::ERROR;
414 =head2 process($input, \%replace)
416 The C<process()> method is called to process a template specified as the first
417 parameter, C<$input>. This may be a file name, file handle (e.g. C<GLOB> or
418 C<IO::Handle>) or a reference to a text string containing the template text. An
419 additional hash reference may be passed containing template variable
422 The method processes the template, adding any
423 L<PRE_PROCESS|PRE_PROCESS_POST_PROCESS> or
424 L<POST_PROCESS|PRE_PROCESS_POST_PROCESS> templates defined, and returns the
425 output text. An uncaught exception thrown by the template will be handled by a
426 relevant L<ERROR> handler if defined. Errors that occur in the
427 L<PRE_PROCESS|PRE_PROCESS_POST_PROCESS> or
428 L<POST_PROCESS|PRE_PROCESS_POST_PROCESS> templates, or those that occur in the
429 main input template and aren't handled, cause the method to return C<undef> to
430 indicate failure. The appropriate error message can be retrieved via the
431 L<error()|Template::Base#error()> method.
433 $service->process('myfile.html', { title => 'My Test File' })
434 || die $service->error();
438 Returns a reference to the internal context object which is, by default, an
439 instance of the L<Template::Context> class.
441 =head1 CONFIGURATION OPTIONS
443 The following list summarises the configuration options that can be provided
444 to the C<Template::Service> L<new()> constructor. Please consult
445 L<Template::Manual::Config> for further details and examples of each
446 configuration option in use.
448 =head2 PRE_PROCESS, POST_PROCESS
450 The L<PRE_PROCESS|Template::Manual::Config#PRE_PROCESS_POST_PROCESS> and
451 L<POST_PROCESS|Template::Manual::Config#PRE_PROCESS_POST_PROCESS> options may
452 be set to contain the name(s) of template files which should be processed
453 immediately before and/or after each template. These do not get added to
454 templates processed into a document via directives such as C<INCLUDE>
455 C<PROCESS>, C<WRAPPER>, etc.
457 my $service = Template::Service->new({
458 PRE_PROCESS => 'header',
459 POST_PROCESS => 'footer',
462 Multiple templates may be specified as a reference to a list. Each is
463 processed in the order defined.
465 my $service = Template::Service->new({
466 PRE_PROCESS => [ 'config', 'header' ],
467 POST_PROCESS => 'footer',
472 The L<PROCESS|Template::Manual::Config#PROCESS> option may be set to contain
473 the name(s) of template files which should be processed instead of the main
474 template passed to the C<Template::Service> L<process()> method. This can be used to
475 apply consistent wrappers around all templates, similar to the use of
476 L<PRE_PROCESS|PRE_PROCESS_POST_PROCESS> and
477 L<POST_PROCESS|PRE_PROCESS_POST_PROCESS> templates.
479 my $service = Template::Service->new({
480 PROCESS => 'content',
483 # processes 'content' instead of 'foo.html'
484 $service->process('foo.html');
486 A reference to the original template is available in the C<template>
487 variable. Metadata items can be inspected and the template can be
488 processed by specifying it as a variable reference (i.e. prefixed by
489 'C<$>') to an C<INCLUDE>, C<PROCESS> or C<WRAPPER> directive.
491 Example C<PROCESS> template:
495 <title>[% template.title %]</title>
498 [% PROCESS $template %]
504 The L<ERROR|Template::Manual::Config#ERROR> (or C<ERRORS> if you prefer)
505 configuration item can be used to name a single template or specify a hash
506 array mapping exception types to templates which should be used for error
507 handling. If an uncaught exception is raised from within a template then the
508 appropriate error template will instead be processed.
510 If specified as a single value then that template will be processed
511 for all uncaught exceptions.
513 my $service = Template::Service->new({
514 ERROR => 'error.html'
517 If the L<ERROR/ERRORS|Template::Manual::Config#ERROR> item is a hash reference
518 the keys are assumed to be exception types and the relevant template for a
519 given exception will be selected. A C<default> template may be provided for
522 my $service = Template::Service->new({
524 user => 'user/index.html',
525 dbi => 'error/database',
526 default => 'error/default',
532 The L<AUTO_RESET|Template::Manual::Config#AUTO_RESET> option is set by default
533 and causes the local C<BLOCKS> cache for the L<Template::Context> object to be
534 reset on each call to the L<Template> L<process()|Template#process()> method.
535 This ensures that any C<BLOCK>s defined within a template will only persist until
536 that template is finished processing.
540 The L<DEBUG|Template::Manual::Config#DEBUG> option can be used to enable
541 debugging messages from the C<Template::Service> module by setting it to include
542 the C<DEBUG_SERVICE> value.
544 use Template::Constants qw( :debug );
546 my $template = Template->new({
547 DEBUG => DEBUG_SERVICE,
552 Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
556 Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
558 This module is free software; you can redistribute it and/or
559 modify it under the same terms as Perl itself.
563 L<Template>, L<Template::Context>
569 # perl-indent-level: 4
570 # indent-tabs-mode: nil
573 # vim: expandtab shiftwidth=4: