File Coverage

blib/lib/Dancer/Template/Abstract.pm
Criterion Covered Total %
statement 93 105 88.5
branch 23 32 71.8
condition 14 16 87.5
subroutine 20 22 90.9
pod 5 8 62.5
total 155 183 84.7


line stmt bran cond sub pod time code
1             package Dancer::Template::Abstract;
2             our $AUTHORITY = 'cpan:SUKRIA';
3             #ABSTRACT: abstract class for Dancer's template engines
4             $Dancer::Template::Abstract::VERSION = '1.3520';
5 194     194   2999 use strict;
  194         449  
  194         5806  
6 194     194   1032 use warnings;
  194         454  
  194         4978  
7 194     194   1084 use Carp;
  194         421  
  194         9976  
8              
9 194     194   83343 use Dancer::Logger;
  194         773  
  194         6741  
10 194     194   88900 use Dancer::Factory::Hook;
  194         602  
  194         5545  
11 194     194   1921 use Dancer::FileUtils 'path';
  194         415  
  194         10571  
12 194     194   1331 use Dancer::Exception qw(:all);
  194         442  
  194         20913  
13              
14 194     194   1355 use base 'Dancer::Engine';
  194         509  
  194         262196  
15              
16             Dancer::Factory::Hook->instance->install_hooks(
17             qw/before_template_render after_template_render before_layout_render after_layout_render/
18             );
19              
20             # overloads this method to implement the rendering
21             # args: $self, $template, $tokens
22             # return: a string of $template's content processed with $tokens
23 1     1 1 244 sub render { confess "render not implemented" }
24              
25 110     110 1 315 sub default_tmpl_ext { "tt" }
26              
27             # Work out the template names to look for; this will be the view name passed
28             # as-is, and also the view name with the default template extension appended, if
29             # it did not already end in that.
30             sub _template_name {
31 112     112   253 my ( $self, $view ) = @_;
32 112         238 my @views = ( $view );
33 112   66     374 my $def_tmpl_ext = $self->config->{extension} || $self->default_tmpl_ext();
34 112 100       980 push @views, $view .= ".$def_tmpl_ext" if $view !~ /\.\Q$def_tmpl_ext\E$/;
35 112         415 return @views;
36             }
37              
38             sub view {
39 106     106 1 257 my ($self, $view) = @_;
40              
41 106         365 my $views_dir = Dancer::App->current->setting('views');
42              
43 106         387 for my $template ($self->_template_name($view)) {
44 194         691 my $view_path = path($views_dir, $template);
45 194 100       3695 return $view_path if -f $view_path;
46             }
47              
48             # No matching view path was found
49 5         39 return;
50             }
51              
52             sub layout {
53 5     5 1 23 my ($self, $layout, $tokens, $content) = @_;
54              
55 5         17 my $layouts_dir = path(Dancer::App->current->setting('views'), 'layouts');
56 5         20 my $layout_path;
57 5         16 for my $layout_name ($self->_template_name($layout)) {
58 10         51 $layout_path = path($layouts_dir, $layout_name);
59 10 100       263 last if -e $layout_path;
60             }
61              
62 5         17 my $full_content;
63 5 50       79 if (-e $layout_path) {
64 5         35 $full_content = Dancer::Template->engine->render(
65             $layout_path, {%$tokens, content => $content});
66             } else {
67 0         0 $full_content = $content;
68 0         0 Dancer::Logger::error("Defined layout ($layout) was not found!");
69             }
70 5         27 $full_content;
71             }
72              
73             sub apply_renderer {
74 56     56 0 180 my ($self, $view, $tokens) = @_;
75              
76 56         163 ($tokens, undef) = _prepare_tokens_options($tokens);
77              
78 56         199 $view = $self->view($view);
79              
80 56         362 Dancer::Factory::Hook->execute_hooks('before_template_render', $tokens);
81              
82 45         204 my $content;
83             try {
84 45     45   1641 $content = $self->render($view, $tokens);
85             } continuation {
86 0     0   0 my ($continuation) = @_;
87             # If we have a Route continuation, run the after hook, then
88             # propagate the continuation
89 0         0 Dancer::Factory::Hook->execute_hooks('after_template_render', \$content);
90 0         0 $continuation->rethrow();
91 45         415 };
92              
93 45         1244 Dancer::Factory::Hook->execute_hooks('after_template_render', \$content);
94              
95             # make sure to avoid ( undef ) in list context return
96 42 50       1264 defined $content
97             and return $content;
98 0         0 return;
99             }
100              
101             sub apply_layout {
102 27     27 0 92 my ($self, $content, $tokens, $options) = @_;
103              
104 27         75 ($tokens, $options) = _prepare_tokens_options($tokens, $options);
105              
106             # If 'layout' was given in the options hashref, use it if it's a true value,
107             # or don't use a layout if it was false (0, or undef); if layout wasn't
108             # given in the options hashref, go with whatever the current layout setting
109             # is.
110             my $layout =
111             exists $options->{layout}
112             ? ($options->{layout} ? $options->{layout} : undef)
113 27 100       128 : Dancer::App->current->setting('layout');
    100          
114              
115 27 50       101 defined $content or return;
116              
117 27 100       146 defined $layout or return $content;
118              
119 5         20 Dancer::Factory::Hook->execute_hooks('before_layout_render', $tokens, \$content);
120              
121 5         17 my $full_content;
122              
123             try {
124 5     5   176 $full_content = $self->layout($layout, $tokens, $content);
125             } continuation {
126 0     0   0 my ($continuation) = @_;
127             # If we have a Route continuation, run the after hook, then
128             # propagate the continuation
129 0         0 Dancer::Factory::Hook->execute_hooks('after_layout_render', \$full_content);
130 0         0 $continuation->rethrow();
131 5         35 };
132              
133 5         128 Dancer::Factory::Hook->execute_hooks('after_layout_render', \$full_content);
134              
135             # make sure to avoid ( undef ) in list context return
136 5 50       1582 defined $full_content
137             and return $full_content;
138 0         0 return;
139             }
140              
141             sub _prepare_tokens_options {
142 83     83   203 my ($tokens, $options) = @_;
143              
144 83   100     355 $options ||= {};
145              
146             # these are the default tokens provided for template processing
147 83   100     211 $tokens ||= {};
148 83         231 $tokens->{perl_version} = $];
149 83         165 $tokens->{dancer_version} = $Dancer::VERSION;
150 83         365 $tokens->{settings} = Dancer::Config->settings;
151              
152             # If we're processing a request, also add the request object, params and
153             # vars as tokens:
154 83 50       297 if (my $request = Dancer::SharedData->request) {
155 83         218 $tokens->{request} = $request;
156 83         298 $tokens->{params} = $request->params;
157 83         328 $tokens->{vars} = Dancer::SharedData->vars;
158             }
159              
160             Dancer::App->current->setting('session')
161 83 50       367 and $tokens->{session} = Dancer::Session->get;
162              
163 83         263 return ($tokens, $options);
164             }
165              
166             sub template {
167 39     39 0 133 my ($class, $view, $tokens, $options) = @_;
168 39         59 my ($content, $full_content);
169              
170 39         200 my $engine = Dancer::Template->engine;
171              
172             # it's important that $tokens is not undef, so that things added to it via
173             # a before_template in apply_renderer survive to the apply_layout. GH#354
174 39   100     151 $tokens ||= {};
175 39   100     170 $options ||= {};
176              
177 39 50       117 if ($view) {
178             # check if the requested view exists
179 39   100     204 my $view_path = $engine->view($view) || '';
180 39 100       245 if ($engine->view_exists($view_path)) {
181 38         263 $content = $engine->apply_renderer($view, $tokens);
182             } else {
183 1         12 Dancer::Logger::error(
184             "Supplied view ($view) not found - $view_path does not exist"
185             );
186 1         10 return Dancer::Error->new(
187             code => 500,
188             message => 'view not found',
189             )->render();
190             }
191             } else {
192 0         0 $content = delete $options->{content};
193             }
194              
195 24 50       169 defined $content and $full_content =
196             $engine->apply_layout($content, $tokens, $options);
197              
198 24 50       247 defined $full_content
199             and return $full_content;
200              
201 0         0 Dancer::Error->new(
202             code => 404,
203             message => "Page not found",
204             )->render();
205             }
206              
207 39   66 39 1 654 sub view_exists { return defined $_[1] && -f $_[1] }
208              
209             1;
210              
211             __END__