1package Dancer::Template::TemplateToolkit; 2our $AUTHORITY = 'cpan:SUKRIA'; 3#ABSTRACT: Template Toolkit wrapper for Dancer 4$Dancer::Template::TemplateToolkit::VERSION = '1.3513'; 5use strict; 6use warnings; 7use Carp; 8use Dancer::Config 'setting'; 9use Dancer::ModuleLoader; 10use Dancer::Exception qw(:all); 11 12use base 'Dancer::Template::Abstract'; 13 14my $_engine; 15 16sub init { 17 my ($self) = @_; 18 19 my $class = $self->config->{subclass} || "Template"; 20 raise core_template => "$class is needed by Dancer::Template::TemplateToolkit" 21 if !$class->can("process") and !Dancer::ModuleLoader->load($class); 22 23 my $charset = setting('charset') || ''; 24 my @encoding = length($charset) ? ( ENCODING => $charset ) : (); 25 26 my $is_subclass = $class ne 'Template'; 27 28 my @anycase = $is_subclass ? () : ( ANYCASE => 1 ); 29 my @absolute = $is_subclass ? () : ( ABSOLUTE => 1 ); 30 31 my @inc_path = $is_subclass ? () 32 : ( INCLUDE_PATH => $self->config->{INCLUDE_PATH} || setting('views') ); 33 34 my $start_tag = $is_subclass 35 ? $self->config->{start_tag} 36 : $self->config->{start_tag} || '<%'; 37 38 my $stop_tag = $is_subclass 39 ? $self->config->{stop_tag} || $self->config->{end_tag} 40 : $self->config->{stop_tag} || $self->config->{end_tag} || '%>'; 41 42 # TT expects quotemeta()'ed values here to be used as-is within 43 # its regexp-based tokenizer. To support existing Dancer users who 44 # prefer the default TT tags and who've already figured this out, 45 # let's skip this if the tags are already ok. 46 # Just FYI: TT hardcodes '\[%' and '%\]' as default. 47 # 48 my @start = (); 49 if (defined $start_tag) { 50 @start = ( START_TAG => $start_tag eq '\[%' || $start_tag eq '\[\%' 51 ? $start_tag 52 : quotemeta($start_tag) 53 ); 54 } 55 my @stop = (); 56 if (defined $stop_tag) { 57 @stop = ( END_TAG => $stop_tag eq '%\]' || $stop_tag eq '\%\]' 58 ? $stop_tag 59 : quotemeta($stop_tag) 60 ); 61 } 62 my @embedded = (); 63 if ($self->config->{embedded_templates}) { 64 Dancer::ModuleLoader->load('Template::Provider::FromDATA') 65 or croak "The Package Template::Provider::FromDATA must be installed to use embedded_templates"; 66 67 @embedded = ( LOAD_TEMPLATES => [Template::Provider::FromDATA->new()] ); 68 } 69 70 my $tt_config = { 71 @anycase, 72 @absolute, 73 @encoding, 74 @inc_path, 75 @start, 76 @stop, 77 @embedded, 78 %{$self->config}, 79 }; 80 81 $_engine = $class->new(%$tt_config); 82} 83 84sub set_wrapper { 85 my ($self, $when, $file) = @_; 86 my $wrappers = $_engine->{SERVICE}->{WRAPPER}; 87 unless (defined $file) { 88 $file = $when; 89 my @orig = @$wrappers; 90 $self->{orig_wrappers} = \@orig; 91 @$wrappers = ($file); 92 return; 93 } 94 if ($when eq 'outer') { 95 unshift @$wrappers => $file; 96 } elsif ($when eq 'inner') { 97 push @$wrappers => $file; 98 } else { 99 raise core_template => "'$when' isn't a valid identifier"; 100 } 101} 102 103sub reset_wrapper { 104 my ($self) = @_; 105 my $wrappers = $_engine->{SERVICE}->{WRAPPER}; 106 my $orig = $self->{orig_wrappers} || []; 107 my @old = @$wrappers; 108 @$wrappers = @$orig; 109 return @old; 110} 111 112sub unset_wrapper { 113 my ($self, $when) = @_; 114 my $wrappers = $_engine->{SERVICE}->{WRAPPER}; 115 if ($when eq 'outer') { 116 return shift @$wrappers; 117 } elsif ($when eq 'inner') { 118 return pop @$wrappers; 119 } else { 120 raise core_template => "'$when' isn't a valid identifier"; 121 } 122} 123 124sub render { 125 my ($self, $template, $tokens) = @_; 126 127 $self->view_exists($template) or raise core_template => "'$template' doesn't exist or not a regular file"; 128 129 my $content = ""; 130 my $charset = setting('charset') || ''; 131 my @options = length($charset) ? ( binmode => ":encoding($charset)" ) : (); 132 $_engine->process($template, $tokens, \$content, @options) or raise core_template => $_engine->error; 133 return $content; 134} 135 136sub view_exists { 137 my ($self, $view) = @_; 138 139 return 1 if ref $view; 140 141 if ($self->config->{embedded_templates}) { 142 eval { 143 $_engine->context->template($view); 144 }; 145 return ! $@; 146 } 147 148 return -f $view; 149} 150 151sub view { 152 my ($self, $view) = @_; 153 154 if ($self->config->{embedded_templates}) { 155 return $view; 156 } 157 else { 158 $self->SUPER::view($view); 159 } 160} 161 1621; 163 164__END__ 165 166=pod 167 168=encoding UTF-8 169 170=head1 NAME 171 172Dancer::Template::TemplateToolkit - Template Toolkit wrapper for Dancer 173 174=head1 VERSION 175 176version 1.3513 177 178=head1 DESCRIPTION 179 180This class is an interface between Dancer's template engine abstraction layer 181and the L<Template> module. 182 183This template engine is recommended for production purposes, but depends on the 184Template module. 185 186In order to use this engine, use the template setting: 187 188 template: template_toolkit 189 190This can be done in your config.yml file or directly in your app code with the 191B<set> keyword. 192 193Note that by default, Dancer configures the Template::Toolkit engine to use 194<% %> brackets instead of its default [% %] brackets. This can be changed 195within your config file - for example: 196 197 template: template_toolkit 198 engines: 199 template_toolkit: 200 start_tag: '[%' 201 stop_tag: '%]' 202 203You can also add any options you would normally add to the Template module's 204initialization. You could, for instance, enable saving the compiled templates: 205 206 engines: 207 template_toolkit: 208 COMPILE_DIR: 'caches/templates' 209 COMPILE_EXT: '.ttc' 210 211Note though that unless you change them, Dancer sets both of the Template 212options C<ANYCASE> and C<ABSOLUTE> on, as well as pointing C<INCLUDE_PATH> 213to your B<views> directory and setting C<ENCODING> to your B<charset> 214setting. 215 216=head1 SUBCLASSING 217 218By default, L<Template> is used, but you can configure Dancer to use a 219subclass of Template with the C<subclass> option. 220 221 engines: 222 template_toolkit: 223 subclass: My::Template 224 225When used like this, Dancer skips the defaults mentioned above. Only those 226included in your config file are sent to the subclass. 227 228=head1 WRAPPER, META variables, SETs 229 230Dancer already provides a WRAPPER-like ability, which we call a "layout". The 231reason we do not use TT's WRAPPER (which also makes it incompatible with it) is 232because not all template systems support it. Actually, most don't. 233 234However, you might want to use it, and be able to define META variables and 235regular L<Template::Toolkit> variables. 236 237These few steps will get you there: 238 239=over 4 240 241=item * Disable the layout in Dancer 242 243You can do this by simply commenting (or removing) the C<layout> configuration 244in the F<config.yml> file. 245 246=item * Use Template Toolkit template engine 247 248Change the configuration of the template to Template Toolkit: 249 250 # in config.yml 251 template: "template_toolkit" 252 253=item * Tell the Template Toolkit engine who's your wrapper 254 255 # in config.yml 256 # ... 257 engines: 258 template_toolkit: 259 WRAPPER: layouts/main.tt 260 261=back 262 263Done! Everything will work fine out of the box, including variables and META 264variables. 265 266=head1 EMBEDDED TEMPLATES 267 268You can embed your templates in your script file, to get a self-contained dancer 269application in one file (inspired by L<http://advent.perldancer.org/2011/3>). 270 271To enable this: 272 273 # in app.pl 274 # ... 275 set engines => { 276 template_toolkit => { 277 embedded_templates => 1, 278 }, 279 }; 280 set template => 'template_toolkit'; 281 282This feature requires L<Template::Provider::FromDATA>. Put your templates in the 283__DATA__ section, and start every template with __${templatename}__. 284 285=head1 USING TT'S WRAPPER STACK 286 287This engine provides three additional methods to access the WRAPPER stack of 288TemplateToolkit. 289 290=head2 set_wrapper 291 292Synopsis: 293 294 engine('template')->set_wrapper( inner => 'inner_layout.tt' ); 295 engine('template')->set_wrapper( outer => 'outer_layout.tt' ); 296 engine('template')->set_wrapper( 'only_layout.tt' ); 297 298The first two lines pushes/unshifts layout files to the wrapper array. 299The third line overwrites the wrapper array with a single element. 300 301=head2 unset_wrapper 302 303Synopsis: 304 305 engine('template')->unset_wrapper('inner') # returns 'inner_layout.tt'; 306 engine('template')->unset_wrapper('outer') # returns 'outer_layout.tt'; 307 308These lines pops/shifts layout files from the wrapper array and returns the 309removed elements. 310 311=head2 reset_wrapper 312 313Synopsis: 314 315 engine('template')->reset_wrapper; 316 317This method restores the wrapper array after a set_wrapper call. 318 319=head1 SEE ALSO 320 321L<Dancer>, L<Template> 322 323=head1 AUTHOR 324 325Dancer Core Developers 326 327=head1 COPYRIGHT AND LICENSE 328 329This software is copyright (c) 2010 by Alexis Sukrieh. 330 331This is free software; you can redistribute it and/or modify it under 332the same terms as the Perl 5 programming language system itself. 333 334=cut 335