File Coverage

blib/lib/Text/TemplateLite/Renderer.pm
Criterion Covered Total %
statement 71 80 88.7
branch 20 24 83.3
condition 9 16 56.2
subroutine 16 20 80.0
pod 15 15 100.0
total 131 155 84.5


line stmt bran cond sub pod time code
1             package Text::TemplateLite::Renderer;
2              
3 2     2   29 use 5.006;
  2         7  
  2         69  
4 2     2   11 use strict;
  2         3  
  2         50  
5 2     2   9 use warnings;
  2         3  
  2         105  
6 2     2   15 use Carp;
  2         3  
  2         161  
7 2     2   64 use Scalar::Util qw(blessed);
  2         3  
  2         2152  
8              
9             =head1 NAME
10              
11             Text::TemplateLite::Renderer - A rendering-management class for
12             L
13              
14             =head1 VERSION
15              
16             Version 0.01
17              
18             =cut
19              
20             our $VERSION = '0.01';
21              
22             =head1 SYNOPSIS
23              
24             my $tpl = Text::TemplateLite->new->set(q{<<$var>>});
25             print $tpl->render({ var => 'hello' })->result;
26              
27             # Setting execution limits before rendering:
28             my $rndr = $tpl->new_renderer
29             ->limit(step_length => 1000)
30             ->limit(total_steps => 1000)
31             ->render;
32              
33             # Checking for incomplete results after rendering:
34             croak "Template rendering exceeded resource limits"
35             if $rndr->exceeded_limits;
36              
37             =head1 DESCRIPTION
38              
39             This is the rendering companion class for L. It
40             manages template variables and resource usage when rendering a template.
41              
42             =head1 USER METHODS
43              
44             This section describes methods for normal usage.
45              
46             =head2 new( )
47              
48             This creates and returns a new renderer instance. The renderer must be
49             associated with a template (see L<"template($template)">) before rendering.
50              
51             =cut
52              
53             sub new {
54 5     5 1 9 my ($class) = @_;
55 5         28 my $self = bless {
56             limits => {
57             step_length => undef, # step-wise string length
58             total_steps => undef, # total steps
59             },
60             }, $class;
61              
62 5         13 return $self->reset;
63             }
64              
65             =head2 reset( )
66              
67             This resets the renderer between consecutive renderings. It clears any
68             previous result and associated usage statistics and exceeded-limit
69             information (but not the limits themselves). It returns the rendering
70             object.
71              
72             =cut
73              
74             sub reset {
75 29     29 1 55 my ($self) = @_;
76              
77 29         60 $self->{exceeded} = {};
78 29         106 $self->{info} = {
79             stop => 0,
80             total_steps => 0,
81             undef_calls => 0,
82             };
83 29         66 delete $self->{result};
84              
85 29         56 return $self;
86             }
87              
88             =head2 template( )
89              
90             =head2 template($template)
91              
92             The first form returns the current template engine instance
93             (a L).
94              
95             The second form sets the current template engine instance to $template and
96             returns the rendering object. This is called automatically by
97             L.
98              
99             =cut
100              
101             sub template {
102 6     6 1 622 my ($self, $template) = @_;
103              
104 6 100       23 return $self->{template} if @_ < 2;
105              
106 5 50 33     59 croak "Template is not a Text::TemplateLite" if defined($template)
      33        
107             && (!blessed($template) || !$template->isa('Text::TemplateLite'));
108 5         14 $self->{template} = $template;
109 5         14 return $self;
110             }
111              
112             =head2 limit($type)
113              
114             =head2 limit($type, $limit)
115              
116             The first form returns the current limit for the specified type.
117              
118             The second form sets a limit and returns the rendering object. A numeric
119             limit sets a specific limit; C removes the limit.
120              
121             The limit types supported in this version are:
122              
123             =over
124              
125             =item step_length
126              
127             This is the maximum length (in characters) allowed to be returned as
128             the result of any step (or sequence of steps) in the template execution.
129              
130             For example, given:
131              
132             ??($condition, $some$variables, $default)
133              
134             The substitutions of $condition and $default, the concatenation of
135             $some and $variables, and the return value from function ?? will each be
136             truncated to a length of step_length (and step_length will be marked
137             exceeded) if necessary.
138              
139             =item total_steps
140              
141             This is the maximum number of steps that may be executed in the template
142             code (and across all templates if external template calls are involved).
143              
144             Template execution will stop (and total_steps will be exceeded) after
145             this many steps.
146              
147             =back
148              
149             =cut
150              
151             sub limit {
152 39     39 1 64 my ($self, $type, $limit) = @_;
153              
154 39 100       148 return $self->{limits}{$type} if @_ < 3;
155              
156 12         21 $self->{limits}{$type} = $limit;
157 12         40 return $self;
158             }
159              
160             =head2 render(\%variables)
161              
162             This method renders the associated template with the specified variables
163             and returns the rendering object (not the result; see L<"result( )">).
164              
165             Beware! The hashref of variables is subject to modification during rendering!
166              
167             =cut
168              
169             sub render {
170 29     29 1 55 my ($self, $vars) = @_;
171              
172 29 100       98 $self->reset if exists($self->{result});
173 29   100     114 $self->{vars} = $vars || {};
174              
175 29 50       77 if ($self->{template}) {
176             # Execute the template to render the result
177 29         97 $self->{result} = $self->{template}->execute($self);
178 29         78 delete $self->{last_renderer};
179             } else {
180             # Render without template is undefined
181 0         0 delete $self->{result};
182             }
183              
184 29         95 return $self;
185             }
186              
187             =head2 exceeded_limits( )
188              
189             =head2 exceeded_limits(@limits)
190              
191             The first form returns a list of names of any exceeded limits.
192              
193             The second form adds the specified limits to the list of exceeded limits,
194             stops rendering, and returns the template engine object.
195              
196             See L for types.
197              
198             =cut
199              
200             sub exceeded_limits {
201 18     18 1 27 my $self = shift;
202              
203 18 100       43 return keys(%{$self->{exceeded}}) unless @_;
  15         69  
204              
205 3         15 $self->{exceeded}{$_} = 1 foreach (@_);
206 3         9 $self->{info}{stop} = 1;
207 3         5 return $self;
208             }
209              
210             =head2 result( )
211              
212             This method returns the most recent rendering result, or C if there
213             isn't one.
214              
215             =cut
216              
217 30     30 1 122 sub result { return shift->{result}; }
218              
219             =head2 stop( )
220              
221             This sets the stop flag (visible in L<"info( )">), causing template
222             execution to terminate further processing. It returns the rendering object.
223              
224             =cut
225              
226             sub stop {
227 0     0 1 0 my ($self) = @_;
228              
229 0         0 $self->{info}{stop} = 1;
230 0         0 return $self;
231             }
232              
233             =head2 info( )
234              
235             This returns the most recent rendering's execution information as a
236             hash. The template engine currently returns the following usage (but
237             library functions could potentially add metrics for specific calls):
238              
239             =over
240              
241             =item stop
242              
243             This is true if execution stopped before the end of the template
244             (e.g. because total_steps was exceeded).
245              
246             =item total_steps
247              
248             This is the total number of steps that were executed (including any
249             recorded by external template calls).
250              
251             =item undef_calls
252              
253             This is the number of calls to undefined functions or external templates
254             during rendering.
255              
256             =back
257              
258             =cut
259              
260 39     39 1 161 sub info { return shift->{info}; }
261              
262             =head2 vars( )
263              
264             This method returns the hash of current template variables.
265              
266             =cut
267              
268 4   50 4 1 23 sub vars { return shift->{vars} ||= {}; }
269              
270             =head1 AUTHOR METHODS
271              
272             This section describes methods generally only used by library function
273             authors.
274              
275             =head2 execute_each($list)
276              
277             This method is a short-cut to call the template engine's execute_each
278             method.
279              
280             =cut
281              
282             sub execute_each {
283 0     0 1 0 my ($self, $list) = @_;
284              
285 0         0 return $self->template->execute_each($list, $self);
286             }
287              
288             =head2 execute_sequence($code)
289              
290             This method is a short-cut to call the template engine's execute_sequence
291             method.
292              
293             =cut
294              
295             sub execute_sequence {
296 0     0 1 0 my ($self, $code) = @_;
297              
298 0         0 return $self->template->execute_sequence($code, $self);
299             }
300              
301             =head2 last_renderer( )
302              
303             This method returns the most recent external template renderer. This
304             information is only retained until the current rendering has completed
305             or the next external template call, whichever happens first.
306              
307             =cut
308              
309 0     0 1 0 sub last_renderer { return shift->{last_renderer}; }
310              
311             =head1 ENGINE METHODS
312              
313             These methods are used by the template engine. You should probably not be
314             calling them directly.
315              
316             =head2 step( )
317              
318             =head2 step($step)
319              
320             The first form checks that it is OK to perform another step (based on the
321             total_steps limit) in template execution. If so, it increments the step
322             usage and returns true. Otherwise it returns false.
323              
324             The second form sets the step usage to the specified step number if it is
325             higher than the current value and returns the rendering object.
326              
327             =cut
328              
329             sub step {
330 53     53 1 68 my ($self, $step) = @_;
331 53         91 my $limit = $self->{limits}{total_steps};
332              
333 53 100       123 if (@_ > 1) {
334 8 100       24 $self->{info}{total_steps} = $step
335             if $step > $self->{info}{total_steps};
336 8 50 33     28 $self->exceeded_limits('total_steps')
337             if defined($limit) && $step > $limit;
338 8         17 return $self;
339             }
340              
341 45 100       101 return 0 if $self->{info}{stop};
342              
343 44 100 100     129 if (!defined($limit) || $self->{info}{total_steps} < $limit) {
344             # OK - bump usage
345 43         74 ++$self->{info}{total_steps};
346 43         223 return 1;
347             }
348              
349             # Now exceeding step limit
350 1         4 $self->exceeded_limits('total_steps');
351 1         5 return 0;
352             }
353              
354             =head2 render_external($template, \%vars)
355              
356             Render an external template, communicating resource usage in and out.
357              
358             The external renderer is returned.
359              
360             =cut
361              
362             sub render_external {
363 4     4 1 7 my ($self, $ext_tpl, $vars) = @_;
364 4         12 my $ext_rend = $self->{last_renderer} = $ext_tpl->new_renderer();
365 4         8 my $limits = $self->{limits};
366              
367             # Export rendering state
368 4         17 $ext_rend->limit($_, $limits->{$_}) foreach keys(%$limits);
369 4         13 $ext_rend->step($self->{info}{total_steps});
370              
371             # Render the external template
372 4         10 $ext_rend->render($vars);
373              
374             # Update the internal state to reflect the external rendering
375 4         8 my $ext_info = $ext_rend->info;
376 4         8 $self->step($ext_info->{total_steps})
377             ->exceeded_limits($ext_rend->exceeded_limits);
378 4 50       10 $self->{info}{stop} = 1 if $ext_info->{stop};
379 4         7 $self->{info}{undef_calls} += $ext_info->{undef_calls};
380              
381 4         12 return $ext_rend;
382             }
383              
384             1; # End of Text::TemplateLite::Renderer