File Coverage

blib/lib/Dancer2/Core/Role/Template.pm
Criterion Covered Total %
statement 68 72 94.4
branch 19 26 73.0
condition 7 9 77.7
subroutine 17 18 94.4
pod 7 9 77.7
total 118 134 88.0


line stmt bran cond sub pod time code
1             # ABSTRACT: Role for template engines
2              
3             package Dancer2::Core::Role::Template;
4             $Dancer2::Core::Role::Template::VERSION = '2.0.1';
5 126     126   382034 use Dancer2::Core::Types;
  126         374  
  126         1376  
6 126     126   1944350 use Dancer2::FileUtils 'path';
  126         389  
  126         11122  
7 126     126   915 use Carp 'croak';
  126         332  
  126         8092  
8 126     126   4424 use Ref::Util qw< is_ref >;
  126         4292  
  126         7993  
9              
10 126     126   1258 use Moo::Role;
  126         16702  
  126         1586  
11             with 'Dancer2::Core::Role::Engine';
12              
13             sub hook_aliases {
14             {
15 118     118 0 1306 before_template_render => 'engine.template.before_render',
16             after_template_render => 'engine.template.after_render',
17             before_layout_render => 'engine.template.before_layout_render',
18             after_layout_render => 'engine.template.after_layout_render',
19             }
20             }
21              
22 30     30 0 75 sub supported_hooks { values %{ shift->hook_aliases } }
  30         153  
23              
24 0     0   0 sub _build_type {'Template'}
25              
26             requires 'render';
27              
28             has log_cb => (
29             is => 'ro',
30             isa => CodeRef,
31             default => sub { sub {1} },
32             );
33              
34             has name => (
35             is => 'ro',
36             lazy => 1,
37             builder => 1,
38             );
39              
40             sub _build_name {
41 2     2   1145 ( my $name = ref shift ) =~ s/^Dancer2::Template:://;
42 2         12 $name;
43             }
44              
45             has charset => (
46             is => 'ro',
47             isa => Str,
48             default => sub {'UTF-8'},
49             );
50              
51             has default_tmpl_ext => (
52             is => 'ro',
53             isa => Str,
54             default => sub { shift->config->{extension} || 'tt' },
55             );
56              
57             has engine => (
58             is => 'ro',
59             isa => Object,
60             lazy => 1,
61             builder => 1,
62             );
63              
64             has settings => (
65             is => 'ro',
66             isa => HashRef,
67             lazy => 1,
68             default => sub { +{} },
69             writer => 'set_settings',
70             );
71              
72             # The attributes views, layout and layout_dir have triggers in
73             # Dancer2::Core::App that enable their values to be modified by
74             # the `set` keyword. As such, these are defined as read-write attrs.
75              
76             has views => (
77             is => 'rw',
78             isa => Maybe [Str],
79             );
80              
81             has layout => (
82             is => 'rw',
83             isa => Maybe [Str],
84             );
85              
86             has layout_dir => (
87             is => 'rw',
88             isa => Maybe [Str],
89             );
90              
91             sub _template_name {
92 171     171   535 my ( $self, $view ) = @_;
93 171         758 my $def_tmpl_ext = $self->default_tmpl_ext();
94 171 100       2226 $view .= ".$def_tmpl_ext" if $view !~ /\.\Q$def_tmpl_ext\E$/;
95 171         639 return $view;
96             }
97              
98             sub view_pathname {
99 141     141 1 1448 my ( $self, $view ) = @_;
100              
101 141         557 $view = $self->_template_name($view);
102 141         3618 return path( $self->views, $view );
103             }
104              
105             sub layout_pathname {
106 2     2 1 6 my ( $self, $layout ) = @_;
107              
108 2         34 return path(
109             $self->views,
110             $self->layout_dir,
111             $self->_template_name($layout),
112             );
113             }
114              
115             sub pathname_exists {
116 119     119 1 346 my ( $self, $pathname ) = @_;
117 119         8670 return -f $pathname;
118             }
119              
120             sub render_layout {
121 6     6 1 20 my ( $self, $layout, $tokens, $content ) = @_;
122              
123 6         27 $layout = $self->layout_pathname($layout);
124              
125             # FIXME: not sure if I can "just call render"
126 6         70 $self->render( $layout, { %$tokens, content => $content } );
127             }
128              
129             sub apply_renderer {
130 41     41 1 115 my ( $self, $view, $tokens ) = @_;
131 41 100       296 $view = $self->view_pathname($view) if !is_ref($view);
132 41         256 $tokens = $self->_prepare_tokens_options( $tokens );
133              
134 41         232 $self->execute_hook( 'engine.template.before_render', $tokens );
135              
136 37         456 my $content = $self->render( $view, $tokens );
137 35         252 $self->execute_hook( 'engine.template.after_render', \$content );
138              
139             # make sure to avoid ( undef ) in list context return
140 35 50       451 defined $content and return $content;
141 0         0 return;
142             }
143              
144             sub apply_layout {
145 35     35 1 117 my ( $self, $content, $tokens, $options ) = @_;
146              
147 35         123 $tokens = $self->_prepare_tokens_options( $tokens );
148              
149             # If 'layout' was given in the options hashref, use it if it's a true value,
150             # or don't use a layout if it was false (0, or undef); if layout wasn't
151             # given in the options hashref, go with whatever the current layout setting
152             # is.
153             my $layout =
154             exists $options->{layout}
155             ? ( $options->{layout} ? $options->{layout} : undef )
156 35 50 66     879 : ( $self->layout || $self->config->{layout} );
    100          
157              
158             # that should only be $self->config, but the layout ain't there ???
159              
160 35 50       486 defined $content or return;
161 35 100       160 defined $layout or return $content;
162              
163 6         26 $self->execute_hook(
164             'engine.template.before_layout_render',
165             $tokens, \$content
166             );
167              
168 6         50 my $full_content = $self->render_layout( $layout, $tokens, $content );
169              
170 6         48 $self->execute_hook( 'engine.template.after_layout_render',
171             \$full_content );
172              
173             # make sure to avoid ( undef ) in list context return
174 6 50       65 defined $full_content and return $full_content;
175 0         0 return;
176             }
177              
178             sub _prepare_tokens_options {
179 76     76   245 my ( $self, $tokens ) = @_;
180              
181             # these are the default tokens provided for template processing
182 76   50     229 $tokens ||= {};
183 76         258 $tokens->{perl_version} = $^V;
184 76         908 $tokens->{dancer_version} = Dancer2->VERSION;
185 76         2088 $tokens->{settings} = $self->settings;
186              
187             # no request when template is called as a global keyword
188 76 100       1070 if ( $self->has_request ) {
189 74         268 $tokens->{request} = $self->request;
190 74         474 $tokens->{params} = $self->request->params;
191 74         337 $tokens->{vars} = $self->request->vars;
192              
193             # a session can not exist if there is no request
194 74 100       588 $tokens->{session} = $self->session->data
195             if $self->has_session;
196             }
197              
198 76         292 return $tokens;
199             }
200              
201             sub process {
202 41     41 1 151 my ( $self, $view, $tokens, $options ) = @_;
203 41         86 my ( $content, $full_content );
204              
205             # it's important that $tokens is not undef, so that things added to it via
206             # a before_template in apply_renderer survive to the apply_layout. GH#354
207 41   100     233 $tokens ||= {};
208 41   100     258 $options ||= {};
209              
210             ## FIXME - Look into PR 654 so we fix the problem here as well!
211              
212             $content =
213             $view
214             ? $self->apply_renderer( $view, $tokens )
215 41 50       275 : delete $options->{content};
216              
217 35 50       230 defined $content
218             and $full_content = $self->apply_layout( $content, $tokens, $options );
219              
220 35 50       262 defined $full_content
221             and return $full_content;
222              
223 0           croak "Template did not produce any content";
224             }
225              
226             1;
227              
228             __END__