File Coverage

blib/lib/Dancer2/Template/TemplateToolkit.pm
Criterion Covered Total %
statement 49 49 100.0
branch 9 16 56.2
condition 4 11 36.3
subroutine 12 12 100.0
pod 1 4 25.0
total 75 92 81.5


line stmt bran cond sub pod time code
1             # ABSTRACT: Template toolkit engine for Dancer2
2              
3             package Dancer2::Template::TemplateToolkit;
4             $Dancer2::Template::TemplateToolkit::VERSION = '1.0.0';
5 9     9   25454 use Moo;
  9         29  
  9         80  
6 9     9   3659 use Carp qw<croak>;
  9         47  
  9         522  
7 9     9   535 use Dancer2::Core::Types;
  9         48  
  9         115  
8 9     9   124510 use Dancer2::FileUtils qw<path>;
  9         27  
  9         529  
9 9     9   64 use Scalar::Util ();
  9         25  
  9         184  
10 9     9   2561 use Template;
  9         79017  
  9         5009  
11              
12             with 'Dancer2::Core::Role::Template';
13              
14             has '+engine' => ( isa => InstanceOf ['Template'], );
15              
16             sub _build_engine {
17 7     7   106 my $self = shift;
18 7         27 my $charset = $self->charset;
19             my %tt_config = (
20             ANYCASE => 1,
21             ABSOLUTE => 1,
22             length($charset) ? ( ENCODING => $charset ) : (),
23 7 50       36 %{ $self->config },
  7         58  
24             );
25              
26 7         27 my $start_tag = $self->config->{'start_tag'};
27 7   33     67 my $stop_tag = $self->config->{'stop_tag'} || $self->config->{end_tag};
28 7 50 33     45 $tt_config{'START_TAG'} = $start_tag
29             if defined $start_tag && $start_tag ne '[%';
30 7 50 33     42 $tt_config{'END_TAG'} = $stop_tag
31             if defined $stop_tag && $stop_tag ne '%]';
32              
33 7         46 Scalar::Util::weaken( my $ttt = $self );
34 7         45 my $include_path = $self->config->{include_path};
35             $tt_config{'INCLUDE_PATH'} ||= [
36             ( defined $include_path ? $include_path : () ),
37 24     24   7539 sub { [ $ttt->views ] },
38 7 50 50     119 ];
39              
40 7         128 my $tt = Template->new(%tt_config);
41 7 50       149906 $Template::Stash::PRIVATE = undef if $self->config->{show_private_variables};
42 7         212 return $tt;
43             }
44              
45             sub render {
46 16     16 1 53 my ( $self, $template, $tokens ) = @_;
47              
48 16         39 my $content = '';
49 16         74 my $charset = $self->charset;
50 16 50       116 my @options = length($charset) ? ( binmode => ":encoding($charset)" ) : ();
51 16 50       338 $self->engine->process( $template, $tokens, \$content, @options )
52             or croak 'Failed to render template: ' . $self->engine->error;
53              
54 16         112937 return $content;
55             }
56              
57             # Override *_pathname methods from Dancer2::Core::Role::Template
58             # Let TT2 do the concatenation of paths to template names.
59             #
60             # TT2 will look in a its INCLUDE_PATH for templates.
61             # Typically $self->views is an absolute path, and we set ABSOLUTE=> 1 above.
62             # In that case TT2 does NOT iterate through what is set for INCLUDE_PATH
63             # However, if its not absolute, we want to allow TT2 iterate through the
64             # its INCLUDE_PATH, which we set to be $self->views.
65              
66             sub view_pathname {
67 20     20 0 1808 my ( $self, $view ) = @_;
68 20         91 return $self->_template_name($view);
69             }
70              
71             sub layout_pathname {
72 4     4 0 14 my ( $self, $layout ) = @_;
73 4         75 return path(
74             $self->layout_dir,
75             $self->_template_name($layout),
76             );
77             }
78              
79             sub pathname_exists {
80 7     7 0 20 my ( $self, $pathname ) = @_;
81 7         17 my $exists = eval {
82             # dies if pathname can not be found via TT2's INCLUDE_PATH search
83 7         348 $self->engine->service->context->template( $pathname );
84 3         58311 1;
85             };
86 7 100       503 $self->log_cb->( debug => $@ ) if ! $exists;
87 7         59 return $exists;
88             }
89              
90             1;
91              
92             __END__
93              
94             =pod
95              
96             =encoding UTF-8
97              
98             =head1 NAME
99              
100             Dancer2::Template::TemplateToolkit - Template toolkit engine for Dancer2
101              
102             =head1 VERSION
103              
104             version 1.0.0
105              
106             =head1 SYNOPSIS
107              
108             To use this engine, you may configure L<Dancer2> via C<config.yaml>:
109              
110             template: "template_toolkit"
111              
112             Or you may also change the rendering engine on a per-route basis by
113             setting it manually with C<set>:
114              
115             # code code code
116             set template => 'template_toolkit';
117              
118             Most configuration variables available when creating a new instance of a
119             L<Template>::Toolkit object can be declared inside the template toolkit
120             section on the engines configuration in your config.yml file. For example:
121              
122             engines:
123             template:
124             template_toolkit:
125             start_tag: '<%'
126             end_tag: '%>'
127              
128             (Note: C<start_tag> and C<end_tag> are regexes. If you want to use PHP-style
129             tags, you will need to list them as C<< <\? >> and C<< \?> >>.)
130             See L<Template::Manual::Config> for the configuration variables.
131              
132             In addition to the standard configuration variables, the option C<show_private_variables>
133             is also available. Template::Toolkit, by default, does not render private variables
134             (the ones starting with an underscore). If in your project it gets easier to disable
135             this feature than changing variable names, add this option to your configuration.
136              
137             show_private_variables: true
138              
139             B<Warning:> Given the way Template::Toolkit implements this option, different Dancer2
140             applications running within the same interpreter will share this option!
141              
142             =head1 DESCRIPTION
143              
144             This template engine allows you to use L<Template>::Toolkit in L<Dancer2>.
145              
146             =head1 METHODS
147              
148             =head2 render($template, \%tokens)
149              
150             Renders the template. The first arg is a filename for the template file
151             or a reference to a string that contains the template. The second arg
152             is a hashref for the tokens that you wish to pass to
153             L<Template::Toolkit> for rendering.
154              
155             =head1 ADVANCED CUSTOMIZATION
156              
157             L<Template>::Toolkit allows you to replace certain parts, like the internal
158             STASH (L<Template::Stash>). In order to do that, one usually passes an object of another
159             implementation such as L<Template::Stash::AutoEscaping> into the constructor.
160              
161             Unfortunately that is not possible when you configure L<Template>::Toolkit from
162             your Dancer2 configuration file. You cannot instantiate a Perl object in a yaml file.
163             Instead, you need to subclass this module, and use the subclass in your configuration file.
164              
165             A subclass to use the aforementioned L<Template::Stash::AutoEscaping> might look like this:
166              
167             package Dancer2::Template::TemplateToolkit::AutoEscaping;
168             # or MyApp::
169            
170             use Moo;
171             use Template::Stash::AutoEscaping;
172            
173             extends 'Dancer2::Template::TemplateToolkit';
174            
175             around '_build_engine' => sub {
176             my $orig = shift;
177             my $self = shift;
178            
179             my $tt = $self->$orig(@_);
180            
181             # replace the stash object
182             $tt->service->context->{STASH} = Template::Stash::AutoEscaping->new(
183             $self->config->{STASH}
184             );
185            
186             return $tt;
187             };
188            
189             1;
190              
191             You can then use this new subclass in your config file instead of C<template_toolkit>.
192              
193             # in config.yml
194             engines:
195             template:
196             TemplateToolkit::AutoEscaping:
197             start_tag: '<%'
198             end_tag: '%>'
199             # optional arguments here
200             STASH:
201              
202             The same approach should work for SERVICE (L<Template::Service>), CONTEXT (L<Template::Context>),
203             PARSER (L<Template::Parser>) and GRAMMAR (L<Template::Grammar>). If you intend to replace
204             several of these components in your app, it is suggested to create an app-specific subclass
205             that handles all of them at the same time.
206              
207             =head2 Template Caching
208              
209             L<Template>::Tookit templates can be cached by adding the C<COMPILE_EXT> property to your
210             template configuration settings:
211              
212             # in config.yml
213             engines:
214             template:
215             template_toolkit:
216             start_tag: '<%'
217             end_tag: '%>'
218             COMPILE_EXT: '.tcc' # cached file extension
219              
220             Template caching will avoid the need to re-parse template files or blocks each time they are
221             used. Cached templates are automatically updated when you update the original template file.
222              
223             By default, cached templates are saved in the same directory as your template. To save
224             cached templates in a different directory, you can set the C<COMPILE_DIR> property in your
225             Dancer2 configuration file.
226              
227             Please see L<Template::Manual::Config/Caching_and_Compiling_Options> for further
228             details and more caching options.
229              
230             =head1 SEE ALSO
231              
232             L<Dancer2>, L<Dancer2::Core::Role::Template>, L<Template::Toolkit>.
233              
234             =head1 AUTHOR
235              
236             Dancer Core Developers
237              
238             =head1 COPYRIGHT AND LICENSE
239              
240             This software is copyright (c) 2023 by Alexis Sukrieh.
241              
242             This is free software; you can redistribute it and/or modify it under
243             the same terms as the Perl 5 programming language system itself.
244              
245             =cut