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.3514_04'; # TRIAL
5             $Dancer::Template::Abstract::VERSION = '1.351404';
6 195     195   1863 use strict;
  195         566  
  195         4890  
7 195     195   884 use warnings;
  195         332  
  195         3795  
8 195     195   828 use Carp;
  195         382  
  195         8760  
9              
10 195     195   66457 use Dancer::Logger;
  195         723  
  195         5681  
11 195     195   77185 use Dancer::Factory::Hook;
  195         466  
  195         4675  
12 195     195   1518 use Dancer::FileUtils 'path';
  195         334  
  195         8466  
13 195     195   1080 use Dancer::Exception qw(:all);
  195         335  
  195         17261  
14              
15 195     195   1148 use base 'Dancer::Engine';
  195         369  
  195         205340  
16              
17             Dancer::Factory::Hook->instance->install_hooks(
18             qw/before_template_render after_template_render before_layout_render after_layout_render/
19             );
20              
21             # overloads this method to implement the rendering
22             # args: $self, $template, $tokens
23             # return: a string of $template's content processed with $tokens
24 1     1 1 198 sub render { confess "render not implemented" }
25              
26 110     110 1 261 sub default_tmpl_ext { "tt" }
27              
28             # Work out the template names to look for; this will be the view name passed
29             # as-is, and also the view name with the default template extension appended, if
30             # it did not already end in that.
31             sub _template_name {
32 112     112   217 my ( $self, $view ) = @_;
33 112         210 my @views = ( $view );
34 112   66     356 my $def_tmpl_ext = $self->config->{extension} || $self->default_tmpl_ext();
35 112 100       738 push @views, $view .= ".$def_tmpl_ext" if $view !~ /\.\Q$def_tmpl_ext\E$/;
36 112         343 return @views;
37             }
38              
39             sub view {
40 106     106 1 205 my ($self, $view) = @_;
41              
42 106         253 my $views_dir = Dancer::App->current->setting('views');
43              
44 106         315 for my $template ($self->_template_name($view)) {
45 194         649 my $view_path = path($views_dir, $template);
46 194 100       3287 return $view_path if -f $view_path;
47             }
48              
49             # No matching view path was found
50 5         32 return;
51             }
52              
53             sub layout {
54 5     5 1 16 my ($self, $layout, $tokens, $content) = @_;
55              
56 5         13 my $layouts_dir = path(Dancer::App->current->setting('views'), 'layouts');
57 5         8 my $layout_path;
58 5         13 for my $layout_name ($self->_template_name($layout)) {
59 10         27 $layout_path = path($layouts_dir, $layout_name);
60 10 100       168 last if -e $layout_path;
61             }
62              
63 5         15 my $full_content;
64 5 50       62 if (-e $layout_path) {
65 5         29 $full_content = Dancer::Template->engine->render(
66             $layout_path, {%$tokens, content => $content});
67             } else {
68 0         0 $full_content = $content;
69 0         0 Dancer::Logger::error("Defined layout ($layout) was not found!");
70             }
71 5         22 $full_content;
72             }
73              
74             sub apply_renderer {
75 56     56 0 149 my ($self, $view, $tokens) = @_;
76              
77 56         162 ($tokens, undef) = _prepare_tokens_options($tokens);
78              
79 56         170 $view = $self->view($view);
80              
81 56         336 Dancer::Factory::Hook->execute_hooks('before_template_render', $tokens);
82              
83 45         194 my $content;
84             try {
85 45     45   1357 $content = $self->render($view, $tokens);
86             } continuation {
87 0     0   0 my ($continuation) = @_;
88             # If we have a Route continuation, run the after hook, then
89             # propagate the continuation
90 0         0 Dancer::Factory::Hook->execute_hooks('after_template_render', \$content);
91 0         0 $continuation->rethrow();
92 45         359 };
93              
94 45         1061 Dancer::Factory::Hook->execute_hooks('after_template_render', \$content);
95              
96             # make sure to avoid ( undef ) in list context return
97 42 50       899 defined $content
98             and return $content;
99 0         0 return;
100             }
101              
102             sub apply_layout {
103 27     27 0 69 my ($self, $content, $tokens, $options) = @_;
104              
105 27         65 ($tokens, $options) = _prepare_tokens_options($tokens, $options);
106              
107             # If 'layout' was given in the options hashref, use it if it's a true value,
108             # or don't use a layout if it was false (0, or undef); if layout wasn't
109             # given in the options hashref, go with whatever the current layout setting
110             # is.
111             my $layout =
112             exists $options->{layout}
113             ? ($options->{layout} ? $options->{layout} : undef)
114 27 100       113 : Dancer::App->current->setting('layout');
    100          
115              
116 27 50       65 defined $content or return;
117              
118 27 100       78 defined $layout or return $content;
119              
120 5         17 Dancer::Factory::Hook->execute_hooks('before_layout_render', $tokens, \$content);
121              
122 5         14 my $full_content;
123              
124             try {
125 5     5   178 $full_content = $self->layout($layout, $tokens, $content);
126             } continuation {
127 0     0   0 my ($continuation) = @_;
128             # If we have a Route continuation, run the after hook, then
129             # propagate the continuation
130 0         0 Dancer::Factory::Hook->execute_hooks('after_layout_render', \$full_content);
131 0         0 $continuation->rethrow();
132 5         31 };
133              
134 5         97 Dancer::Factory::Hook->execute_hooks('after_layout_render', \$full_content);
135              
136             # make sure to avoid ( undef ) in list context return
137 5 50       1335 defined $full_content
138             and return $full_content;
139 0         0 return;
140             }
141              
142             sub _prepare_tokens_options {
143 83     83   152 my ($tokens, $options) = @_;
144              
145 83   100     294 $options ||= {};
146              
147             # these are the default tokens provided for template processing
148 83   100     162 $tokens ||= {};
149 83         210 $tokens->{perl_version} = $];
150 83         160 $tokens->{dancer_version} = $Dancer::VERSION;
151 83         305 $tokens->{settings} = Dancer::Config->settings;
152              
153             # If we're processing a request, also add the request object, params and
154             # vars as tokens:
155 83 50       261 if (my $request = Dancer::SharedData->request) {
156 83         171 $tokens->{request} = $request;
157 83         265 $tokens->{params} = $request->params;
158 83         216 $tokens->{vars} = Dancer::SharedData->vars;
159             }
160              
161             Dancer::App->current->setting('session')
162 83 50       299 and $tokens->{session} = Dancer::Session->get;
163              
164 83         234 return ($tokens, $options);
165             }
166              
167             sub template {
168 39     39 0 111 my ($class, $view, $tokens, $options) = @_;
169 39         73 my ($content, $full_content);
170              
171 39         187 my $engine = Dancer::Template->engine;
172              
173             # it's important that $tokens is not undef, so that things added to it via
174             # a before_template in apply_renderer survive to the apply_layout. GH#354
175 39   100     125 $tokens ||= {};
176 39   100     171 $options ||= {};
177              
178 39 50       81 if ($view) {
179             # check if the requested view exists
180 39   100     145 my $view_path = $engine->view($view) || '';
181 39 100       198 if ($engine->view_exists($view_path)) {
182 38         198 $content = $engine->apply_renderer($view, $tokens);
183             } else {
184 1         10 Dancer::Logger::error(
185             "Supplied view ($view) not found - $view_path does not exist"
186             );
187 1         10 return Dancer::Error->new(
188             code => 500,
189             message => 'view not found',
190             )->render();
191             }
192             } else {
193 0         0 $content = delete $options->{content};
194             }
195              
196 24 50       113 defined $content and $full_content =
197             $engine->apply_layout($content, $tokens, $options);
198              
199 24 50       162 defined $full_content
200             and return $full_content;
201              
202 0         0 Dancer::Error->new(
203             code => 404,
204             message => "Page not found",
205             )->render();
206             }
207              
208 39   66 39 1 519 sub view_exists { return defined $_[1] && -f $_[1] }
209              
210             1;
211              
212             __END__