File Coverage

blib/lib/Dancer2/Template/Implementation/ForkedTiny.pm
Criterion Covered Total %
statement 64 68 94.1
branch 21 26 80.7
condition 5 6 83.3
subroutine 11 11 100.0
pod 2 3 66.6
total 103 114 90.3


line stmt bran cond sub pod time code
1             $Dancer2::Template::Implementation::ForkedTiny::VERSION = '0.400000';
2             # ABSTRACT: Dancer2 own implementation of Template::Tiny
3              
4             use 5.00503;
5 113     113   273414 use strict;
  113         553  
6 113     113   580 no warnings;
  113         261  
  113         2421  
7 113     113   533 use Ref::Util qw<is_arrayref is_coderef is_plain_hashref>;
  113         245  
  113         4140  
8 113     113   2422  
  113         7185  
  113         119128  
9             # Evaluatable expression
10             my $EXPR = qr/ [a-z_][\w.]* /xs;
11              
12             my $self = bless {
13             start_tag => '[%',
14 21     21 1 29086 end_tag => '%]',
15             @_[ 1 .. $#_ ]
16             },
17             $_[0];
18              
19             # Opening tag including whitespace chomping rules
20             my $LEFT = $self->{LEFT} = qr/
21             (?:
22 21         564 (?: (?:^|\n) [ \t]* )? \Q$self->{start_tag}\E\-
23             |
24             \Q$self->{start_tag}\E \+?
25             ) \s*
26             /xs;
27              
28             # Closing %] tag including whitespace chomping rules
29             my $RIGHT = $self->{RIGHT} = qr/
30             \s* (?:
31 21         288 \+? \Q$self->{end_tag}\E
32             |
33             \-\Q$self->{end_tag}\E (?: [ \t]* \n )?
34             )
35             /xs;
36              
37             # Preparsing run for nesting tags
38             $self->{PREPARSE} = qr/
39             $LEFT ( IF | UNLESS | FOREACH ) \s+
40 21         1127 (
41             (?: \S+ \s+ IN \s+ )?
42             \S+ )
43             $RIGHT
44             (?!
45             .*?
46             $LEFT (?: IF | UNLESS | FOREACH ) \b
47             )
48             ( .*? )
49             (?:
50             $LEFT ELSE $RIGHT
51             (?!
52             .*?
53             $LEFT (?: IF | UNLESS | FOREACH ) \b
54             )
55             ( .+? )
56             )?
57             $LEFT END $RIGHT
58             /xs;
59              
60             $self->{CONDITION} = qr/
61             \Q$self->{start_tag}\E\s
62 21         938 ( ([IUF])\d+ ) \s+
63             (?:
64             ([a-z]\w*) \s+ IN \s+
65             )?
66             ( $EXPR )
67             \s\Q$self->{end_tag}\E
68             ( .*? )
69             (?:
70             \Q$self->{start_tag}\E\s \1 \s\Q$self->{end_tag}\E
71             ( .+? )
72             )?
73             \Q$self->{start_tag}\E\s \1 \s\Q$self->{end_tag}\E
74             /xs;
75              
76             $self;
77             }
78 21         226  
79             # Copy and modify
80             my $self = shift;
81             my $text = shift;
82             $self->_preprocess( \$text );
83 3     3 0 6 return $text;
84 3         3 }
85 3         8  
86 3         8 my $self = shift;
87             my $copy = ${ shift() };
88             my $stash = shift || {};
89              
90 30     30 1 2699 local $@ = '';
91 30         57 local $^W = 0;
  30         83  
92 30   50     96  
93             # Preprocess to establish unique matching tag sets
94 30         57 $self->_preprocess( \$copy );
95 30         115  
96             # Process down the nested tree of conditions
97             my $result = $self->_process( $stash, $copy );
98 30         302 if (@_) {
99             ${ $_[0] } = $result;
100             }
101 30         119 elsif ( defined wantarray ) {
102 30 50       2169 require Carp;
    0          
103 30         55 Carp::carp(
  30         152  
104             'Returning of template results is deprecated in Template::Tiny 0.11'
105             );
106 0         0 return $result;
107 0         0 }
108             else {
109             print $result;
110 0         0 }
111             }
112              
113 0         0  
114             ######################################################################
115             # Support Methods
116              
117             # The only reason this is a standalone is so we can
118             # do more in-depth testing.
119             my $self = shift;
120             my $copy = shift;
121              
122             # Preprocess to establish unique matching tag sets
123             my $id = 0;
124 33     33   58 1 while $$copy =~ s/
125 33         65 $self->{PREPARSE}
126             /
127             my $tag = substr($1, 0, 1) . ++$id;
128 33         56 "\[\% $tag $2 \%\]$3\[\% $tag \%\]"
129 33         1486 . (defined($4) ? "$4\[\% $tag \%\]" : '');
130             /sex;
131             }
132 20         54  
133 20 100       958 my ( $self, $stash, $text ) = @_;
134              
135             $text =~ s/
136             $self->{CONDITION}
137             /
138             ($2 eq 'F')
139 50     50   133 ? $self->_foreach($stash, $3, $4, $5)
140             : eval {
141 50         927 $2 eq 'U'
142             xor
143             !! # Force boolification
144             $self->_expression($stash, $4)
145             }
146 18 100       40 ? $self->_process($stash, $5)
    100          
147 16   100     35 : $self->_process($stash, $6)
148             /gsex;
149              
150             # Resolve expressions
151             $text =~ s/
152             $self->{LEFT} ( $EXPR ) $self->{RIGHT}
153             /
154             eval {
155             $self->_expression($stash, $1)
156             . '' # Force stringification
157 50         1023 }
158             /gsex;
159              
160 59         112 # Trim the document
161 59         151 $text =~ s/^\s*(.+?)\s*\z/$1/s if $self->{TRIM};
162              
163             return $text;
164             }
165              
166             # Special handling for foreach
167 50 100       135 my ( $self, $stash, $term, $expr, $text ) = @_;
168              
169 50         215 # Resolve the expression
170             my $list = $self->_expression( $stash, $expr );
171             is_arrayref($list) or return '';
172              
173             # Iterate
174 2     2   7 return join '',
175             map { $self->_process( { %$stash, $term => $_ }, $text ) } @$list;
176             }
177 2         6  
178 2 50       6 # Evaluates a stash expression
179             my $cursor = $_[1];
180             my @path = split /\./, $_[2];
181             foreach (@path) {
182 2         4  
  4         13  
183             # Support for private keys
184             return if substr( $_, 0, 1 ) eq '_';
185              
186             # Split by data type
187 77     77   108 ref $cursor or return '';
188 77         196 if ( is_arrayref($cursor) ) {
189 77         142 return '' unless /^(?:0|[0-9]\d*)\z/;
190             $cursor = $cursor->[$_];
191             }
192 104 100       221 elsif ( is_plain_hashref($cursor) ) {
193             $cursor = $cursor->{$_};
194             }
195 102 100       196 else {
196 100 100       203 $cursor = $cursor->$_();
    100          
197 5 100       16 }
198 4         8 }
199              
200             # If the last expression is a CodeRef, execute it
201 93         160 is_coderef($cursor)
202             and $cursor = $cursor->();
203             return $cursor;
204 2         8 }
205              
206             1;
207              
208              
209 72 50       131 =pod
210              
211 72         382 =encoding UTF-8
212              
213             =head1 NAME
214              
215             Dancer2::Template::Implementation::ForkedTiny - Dancer2 own implementation of Template::Tiny
216              
217             =head1 VERSION
218              
219             version 0.400000
220              
221             =head1 SYNOPSIS
222              
223             my $template = Dancer2::Template::Implementation::ForkedTiny->new(
224             TRIM => 1,
225             );
226              
227             # Print the template results to STDOUT
228             $template->process( <<'END_TEMPLATE', { foo => 'World' } );
229             Hello [% foo %]!
230             END_TEMPLATE
231              
232             =head1 DESCRIPTION
233              
234             B<Dancer2::Template::Implementation::ForkedTiny> is a reimplementation of a subset of the functionality from
235             L<Template> Toolkit in as few lines of code as possible.
236              
237             It is intended for use in light-usage, low-memory, or low-cpu templating
238             situations, where you may need to upgrade to the full feature set in the
239             future, or if you want the retain the familiarity of TT-style templates.
240              
241             For the subset of functionality it implements, it has fully-compatible template
242             and stash API. All templates used with B<Dancer2::Template::Implementation::ForkedTiny> should be able to be
243             transparently upgraded to full Template Toolkit.
244              
245             Unlike Template Toolkit, B<Dancer2::Template::Implementation::ForkedTiny> will process templates without a
246             compile phase (but despite this is still quicker, owing to heavy use of
247             the Perl regular expression engine.
248              
249             =head2 SUPPORTED USAGE
250              
251             By default, the C<[% %]> tag style is used. You can change the start tag and
252             end tag by specifying them at object creation :
253              
254             my $template = Dancer2::Template::Implementation::ForkedTiny->new(
255             start_tag => '<%',
256             end_tag => '%>,
257             );
258              
259             In the rest of the documentation, C<[% %]> will be used, but it can be of
260             course your specified start / end tags.
261              
262             Both the C<[%+ +%]> style explicit whitespace and the C<[%- -%]> style
263             explicit chomp B<are> support, although the C<[%+ +%]> version is unneeded
264             in practice as B<Dancer2::Template::Implementation::ForkedTiny> does not support default-enabled C<PRE_CHOMP>
265             or C<POST_CHOMP>.
266              
267             Variable expressions in the form C<[% foo.bar.baz %]> B<are> supported.
268              
269             Appropriate simple behaviours for C<ARRAY> references, C<HASH> references and
270             objects are supported. "VMethods" such as [% array.length %] are B<not>
271             supported at this time.
272              
273             If the resulting expression is a CodeRef, it'll be evaluated.
274              
275             C<IF>, C<ELSE> and C<UNLESS> conditional blocks B<are> supported, but only with
276             simple C<[% foo.bar.baz %]> conditions.
277              
278             Support for looping (or rather iteration) is available in simple
279             C<[% FOREACH item IN list %]> form B<is> supported. Other loop structures are
280             B<not> supported. Because support for arbitrary or infinite looping is not
281             available, B<Dancer2::Template::Implementation::ForkedTiny> templates are not turing complete. This is
282             intentional.
283              
284             All of the four supported control structures C<IF>/C<ELSE>/C<UNLESS>/C<FOREACH>
285             can be nested to arbitrary depth.
286              
287             The treatment of C<_private> hash and method keys is compatible with
288             L<Template> Toolkit, returning null or false rather than the actual content
289             of the hash key or method.
290              
291             Anything beyond the above is currently out of scope.
292              
293             =head1 NAME
294              
295             Dancer2::Template::Implementation::ForkedTiny - Template Toolkit reimplemented in as little code as possible, forked from Template::Tiny
296              
297             =head1 METHODS
298              
299             =head2 new
300              
301             my $template = Dancer2::Template::Implementation::ForkedTiny->new(
302             TRIM => 1,
303             );
304              
305             The C<new> constructor is provided for compatibility with Template Toolkit.
306              
307             The only parameter it currently supports is C<TRIM> (which removes leading
308             and trailing whitespace from processed templates).
309              
310             Additional parameters can be provided without error, but will be ignored.
311              
312             =head2 process
313              
314             # DEPRECATED: Return template results (emits a warning)
315             my $text = $template->process( \$input, $vars );
316              
317             # Print template results to STDOUT
318             $template->process( \$input, $vars );
319              
320             # Generate template results into a variable
321             my $output = '';
322             $template->process( \$input, $vars, \$output );
323              
324             The C<process> method is called to process a template.
325              
326             The first parameter is a reference to a text string containing the template
327             text. A reference to a hash may be passed as the second parameter containing
328             definitions of template variables.
329              
330             If a third parameter is provided, it must be a scalar reference to be
331             populated with the output of the template.
332              
333             For a limited amount of time, the old deprecated interface will continue to
334             be supported. If C<process> is called without a third parameter, and in
335             scalar or list contest, the template results will be returned to the caller.
336              
337             If C<process> is called without a third parameter, and in void context, the
338             template results will be C<print()>ed to the currently selected file handle
339             (probably C<STDOUT>) for compatibility with L<Template>.
340              
341             =head1 SUPPORT
342              
343             Bugs should be reported via the CPAN bug tracker at
344              
345             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Template-Tiny>
346              
347             For other issues, or commercial enhancement or support, contact the author.
348              
349             =head1 AUTHOR
350              
351             Adam Kennedy E<lt>adamk@cpan.orgE<gt>
352              
353             Forked and improved by Damien Krotkine E<lt>dams@cpan.orgE<gt>
354              
355             =head1 SEE ALSO
356              
357             L<Config::Tiny>, L<CSS::Tiny>, L<YAML::Tiny>
358              
359             =head1 COPYRIGHT
360              
361             Copyright 2009 - 2011 Adam Kennedy.
362             Copyright 2012 Damien Krotkine.
363              
364             This program is free software; you can redistribute
365             it and/or modify it under the same terms as Perl itself.
366              
367             The full text of the license can be found in the
368             LICENSE file included with this module.
369              
370             =head1 AUTHOR
371              
372             Dancer Core Developers
373              
374             =head1 COPYRIGHT AND LICENSE
375              
376             This software is copyright (c) 2022 by Alexis Sukrieh.
377              
378             This is free software; you can redistribute it and/or modify it under
379             the same terms as the Perl 5 programming language system itself.
380              
381             =cut