line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Tenjin::Template; |
2
|
|
|
|
|
|
|
|
3
|
8
|
|
|
8
|
|
29
|
use strict; |
|
8
|
|
|
|
|
11
|
|
|
8
|
|
|
|
|
193
|
|
4
|
8
|
|
|
8
|
|
28
|
use warnings; |
|
8
|
|
|
|
|
9
|
|
|
8
|
|
|
|
|
239
|
|
5
|
8
|
|
|
8
|
|
37
|
use Fcntl qw/:flock/; |
|
8
|
|
|
|
|
6
|
|
|
8
|
|
|
|
|
1037
|
|
6
|
8
|
|
|
8
|
|
35
|
use Carp; |
|
8
|
|
|
|
|
15
|
|
|
8
|
|
|
|
|
15039
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = "1.000000"; |
9
|
|
|
|
|
|
|
$VERSION = eval $VERSION; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Tenjin::Template - A Tenjin template object, either built from a file or from memory. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# mostly used internally, but you can manipulate |
18
|
|
|
|
|
|
|
# templates like so |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my $template = Tenjin::Template->new('/path/to/templates/template.html'); |
21
|
|
|
|
|
|
|
my $context = { scalar => 'scalar', arrayref => ['one', 2, "3"] }; |
22
|
|
|
|
|
|
|
$template->render($context); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 DESCRIPTION |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
This module is in charge of the task of compiling Tenjin templates. |
27
|
|
|
|
|
|
|
Templates in Tenjin are compiled into standard Perl code (combined with |
28
|
|
|
|
|
|
|
any Perl code used inside the templates themselves). Rendering a template |
29
|
|
|
|
|
|
|
means Cuating that Perl code and returning its output. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
The Tenjin engine reads a template file or a template string, and creates |
32
|
|
|
|
|
|
|
a Template object from it. Then the object compiles itself by traversing |
33
|
|
|
|
|
|
|
the template, parsing Tenjin macros like 'include' and 'start_capture', |
34
|
|
|
|
|
|
|
replaces Tenjin expressions (i.e. C<[== $expr =]> or C<[= $expr =]>) with the |
35
|
|
|
|
|
|
|
appropriate Perl code, etc. This module ties a template object with |
36
|
|
|
|
|
|
|
a context object, but all context manipulation (and the actual Cuation |
37
|
|
|
|
|
|
|
of the Perl code) is done by L. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
If you're planning on using this module by itself (i.e. without the L |
40
|
|
|
|
|
|
|
engine), keep in mind that template caching and layout templates are not |
41
|
|
|
|
|
|
|
handled by this module. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
our $MACRO_HANDLER_TABLE = { |
46
|
|
|
|
|
|
|
'include' => sub { my $arg = shift; |
47
|
|
|
|
|
|
|
" \$_buf .= \$_context->{'_engine'}->render($arg, \$_context, 0);"; |
48
|
|
|
|
|
|
|
}, |
49
|
|
|
|
|
|
|
'start_capture' => sub { my $arg = shift; |
50
|
|
|
|
|
|
|
" my \$_buf_bkup=\$_buf; \$_buf=''; my \$_capture_varname=$arg;"; |
51
|
|
|
|
|
|
|
}, |
52
|
|
|
|
|
|
|
'stop_capture' => sub { my $arg = shift; |
53
|
|
|
|
|
|
|
" \$_context->{\$_capture_varname}=\$_buf; \$_buf=\$_buf_bkup;"; |
54
|
|
|
|
|
|
|
}, |
55
|
|
|
|
|
|
|
'start_placeholder' => sub { my $arg = shift; |
56
|
|
|
|
|
|
|
" if (\$_context->{$arg}) { \$_buf .= \$_context->{$arg}; } else {"; |
57
|
|
|
|
|
|
|
}, |
58
|
|
|
|
|
|
|
'stop_placeholder' => sub { my $arg = shift; |
59
|
|
|
|
|
|
|
" }"; |
60
|
|
|
|
|
|
|
}, |
61
|
|
|
|
|
|
|
'echo' => sub { my $arg = shift; |
62
|
|
|
|
|
|
|
" \$_buf .= $arg;"; |
63
|
|
|
|
|
|
|
}, |
64
|
|
|
|
|
|
|
}; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head1 METHODS |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=head2 new( [$filename, \%opts] ) |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Creates a new Tenjin::Template object, possibly from a file on the file |
71
|
|
|
|
|
|
|
system (in which case C<$filename> must be provided and be an absolute |
72
|
|
|
|
|
|
|
path to a template file). Optionally, a hash-ref of options can be |
73
|
|
|
|
|
|
|
passed to set some customizations. Available options are 'escapefunc', |
74
|
|
|
|
|
|
|
which will be in charge of escaping expressions (from C<[= $expr =]>) instead |
75
|
|
|
|
|
|
|
of the internal method (which uses L); and 'rawclass', |
76
|
|
|
|
|
|
|
which can be used to prevent variables and objects of a certain class |
77
|
|
|
|
|
|
|
from being escaped, in which case the variable must be a hash-ref |
78
|
|
|
|
|
|
|
that has a key named 'str', which will be used instead. So, for example, |
79
|
|
|
|
|
|
|
if you have a variable named C<$var> which is a hash-ref, and 'rawclass' |
80
|
|
|
|
|
|
|
is set as 'HASH', then writing C<[= $var =]> on your templates will replace |
81
|
|
|
|
|
|
|
C<$var> with C<< $var->{str} >>. |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=cut |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub new { |
86
|
18
|
|
|
18
|
1
|
32
|
my ($class, $filename, $template_name, $opts) = @_; |
87
|
|
|
|
|
|
|
|
88
|
18
|
50
|
33
|
|
|
86
|
my $escapefunc = defined($opts) && exists($opts->{escapefunc}) ? $opts->{escapefunc} : undef; |
89
|
18
|
50
|
33
|
|
|
69
|
my $rawclass = defined($opts) && exists($opts->{rawclass}) ? $opts->{rawclass} : undef; |
90
|
|
|
|
|
|
|
|
91
|
18
|
|
|
|
|
91
|
my $self = bless { |
92
|
|
|
|
|
|
|
'filename' => $filename, |
93
|
|
|
|
|
|
|
'name' => $template_name, |
94
|
|
|
|
|
|
|
'script' => undef, |
95
|
|
|
|
|
|
|
'escapefunc' => $escapefunc, |
96
|
|
|
|
|
|
|
'rawclass' => $rawclass, |
97
|
|
|
|
|
|
|
'timestamp' => undef, |
98
|
|
|
|
|
|
|
'args' => undef, |
99
|
|
|
|
|
|
|
}, $class; |
100
|
|
|
|
|
|
|
|
101
|
18
|
50
|
|
|
|
44
|
$self->convert_file($filename) if $filename; |
102
|
|
|
|
|
|
|
|
103
|
18
|
|
|
|
|
43
|
return $self; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head2 render( [$_context] ) |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Renders the template, possibly with a context hash-ref, and returns the |
109
|
|
|
|
|
|
|
rendered output. If errors have occured when rendering the template (which |
110
|
|
|
|
|
|
|
might happen since templates have and are Perl code), then this method |
111
|
|
|
|
|
|
|
will croak. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=cut |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub render { |
116
|
23
|
|
|
23
|
1
|
27
|
my ($self, $_context) = @_; |
117
|
|
|
|
|
|
|
|
118
|
23
|
|
50
|
|
|
46
|
$_context ||= {}; |
119
|
|
|
|
|
|
|
|
120
|
23
|
50
|
|
|
|
55
|
if ($self->{func}) { |
121
|
0
|
|
|
|
|
0
|
return $self->{func}->($_context); |
122
|
|
|
|
|
|
|
} else { |
123
|
23
|
100
|
|
|
|
141
|
$_context = $Tenjin::CONTEXT_CLASS->new($_context) if ref $_context eq 'HASH'; |
124
|
|
|
|
|
|
|
|
125
|
23
|
|
|
|
|
40
|
my $script = $self->{script}; |
126
|
23
|
50
|
|
|
|
93
|
$script = $_context->_build_decl() . $script unless $self->{args}; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# rendering is actually done inside the context object |
129
|
|
|
|
|
|
|
# with the evaluate method. We pass either the name of |
130
|
|
|
|
|
|
|
# the template or the filename of the template for debug |
131
|
|
|
|
|
|
|
# purposes |
132
|
|
|
|
|
|
|
|
133
|
23
|
|
33
|
|
|
105
|
return $_context->evaluate($script, $self->{filename} || $self->{name}); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=head1 INTERNAL METHODS |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=head2 convert_file( $filename ) |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
Receives an absolute path to a template file, converts that file |
142
|
|
|
|
|
|
|
to Perl code by calling L and |
143
|
|
|
|
|
|
|
returns that code. |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=cut |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub convert_file { |
148
|
0
|
|
|
0
|
1
|
0
|
my ($self, $filename) = @_; |
149
|
|
|
|
|
|
|
|
150
|
0
|
|
|
|
|
0
|
return $self->convert($self->_read_file($filename, 1), $filename); |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head2 convert( $input, [$filename] ) |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
Receives a text of a template (i.e. the template itself) and possibly |
156
|
|
|
|
|
|
|
an absolute path to the template file (if the template comes from a file), |
157
|
|
|
|
|
|
|
and converts the template into Perl code, which is later Cuated |
158
|
|
|
|
|
|
|
for rendering. Conversion is done by parsing the statements in the |
159
|
|
|
|
|
|
|
template (see L). |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=cut |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub convert { |
164
|
18
|
|
|
18
|
1
|
54
|
my ($self, $input, $filename) = @_; |
165
|
|
|
|
|
|
|
|
166
|
18
|
|
|
|
|
55
|
$self->{filename} = $filename; |
167
|
18
|
|
|
|
|
36
|
my @buf = ('my $_buf = ""; my $_V; ', ); |
168
|
18
|
|
|
|
|
48
|
$self->parse_stmt(\@buf, $input); |
169
|
|
|
|
|
|
|
|
170
|
18
|
|
|
|
|
209
|
return $self->{script} = $buf[0] . " \$_buf;\n"; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=head2 compile_stmt_pattern( $pl ) |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Receives a string which denotes the Perl code delimiter which is used |
176
|
|
|
|
|
|
|
inside templates. Tenjin uses 'C<< >>' and 'C<< >>' |
177
|
|
|
|
|
|
|
(the latter for preprocessing), so C<$pl> will be 'pl'. This method |
178
|
|
|
|
|
|
|
returns a tranlsation regular expression which will be used for reading |
179
|
|
|
|
|
|
|
embedded Perl code. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub compile_stmt_pattern { |
184
|
18
|
|
|
18
|
1
|
23
|
my $pl = shift; |
185
|
|
|
|
|
|
|
|
186
|
18
|
|
|
|
|
36
|
my $pat = '((^[ \t]*)?<\?'.$pl.'( |\t|\r?\n)(.*?) ?\?>([ \t]*\r?\n)?)'; |
187
|
18
|
|
|
|
|
321
|
return qr/$pat/sm; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head2 stmt_pattern |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Returns the default pattern (which uses 'pl') with the |
193
|
|
|
|
|
|
|
L. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=cut |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub stmt_pattern { |
198
|
18
|
|
|
18
|
1
|
39
|
return compile_stmt_pattern('pl'); |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head2 expr_pattern() |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
Defines how expressions are written in Tenjin templates (C<[== $expr =]> |
204
|
|
|
|
|
|
|
and C<[= $expr =]>). |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=cut |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub expr_pattern { |
209
|
23
|
|
|
23
|
1
|
52
|
return qr/\[=(=?)(.*?)(=?)=\]/s; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=head2 parse_stmt( $bufref, $input ) |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Receives a buffer which is used for saving a template's expressions |
215
|
|
|
|
|
|
|
and the template's text, parses all expressions in the templates and |
216
|
|
|
|
|
|
|
pushes them to the buffer. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=cut |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
sub parse_stmt { |
221
|
18
|
|
|
18
|
1
|
35
|
my ($self, $bufref, $input) = @_; |
222
|
|
|
|
|
|
|
|
223
|
18
|
|
|
|
|
32
|
my $pos = 0; |
224
|
18
|
|
|
|
|
40
|
my $pat = $self->stmt_pattern(); |
225
|
18
|
|
|
|
|
159
|
while ($input =~ /$pat/g) { |
226
|
14
|
|
|
|
|
42
|
my ($pi, $lspace, $mspace, $stmt, $rspace) = ($1, $2, $3, $4, $5); |
227
|
14
|
|
|
|
|
37
|
my $start = $-[0]; |
228
|
14
|
|
|
|
|
50
|
my $text = substr($input, $pos, $start - $pos); |
229
|
14
|
|
|
|
|
21
|
$pos = $start + length($pi); |
230
|
14
|
100
|
|
|
|
34
|
$self->parse_expr($bufref, $text) if $text; |
231
|
14
|
100
|
|
|
|
35
|
$mspace = '' if $mspace eq ' '; |
232
|
14
|
|
|
|
|
25
|
$stmt = $self->hook_stmt($stmt); |
233
|
14
|
100
|
|
|
|
31
|
$stmt .= $rspace if $rspace; |
234
|
14
|
100
|
|
|
|
25
|
$stmt = $mspace . $stmt if $mspace; |
235
|
14
|
50
|
|
|
|
20
|
$stmt = $lspace . $stmt if $lspace; |
236
|
14
|
|
|
|
|
23
|
$self->add_stmt($bufref, $stmt); |
237
|
|
|
|
|
|
|
} |
238
|
18
|
100
|
|
|
|
63
|
my $rest = $pos == 0 ? $input : substr($input, $pos); |
239
|
18
|
100
|
|
|
|
85
|
$self->parse_expr($bufref, $rest) if $rest; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head2 hook_stmt( $stmt ) |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=cut |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub hook_stmt { |
247
|
14
|
|
|
14
|
1
|
17
|
my ($self, $stmt) = @_; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
## macro expantion |
250
|
14
|
100
|
|
|
|
45
|
if ($stmt =~ /\A(\s*)(\w+)\((.*?)\);?(\s*)\Z/) { |
251
|
7
|
|
|
|
|
16
|
my ($lspace, $funcname, $arg, $rspace) = ($1, $2, $3, $4); |
252
|
7
|
|
|
|
|
14
|
my $s = $self->expand_macro($funcname, $arg); |
253
|
7
|
100
|
|
|
|
24
|
return $lspace . $s . $rspace if defined($s); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
## template arguments |
257
|
8
|
50
|
|
|
|
23
|
unless ($self->{args}) { |
258
|
8
|
50
|
|
|
|
24
|
if ($stmt =~ m/\A(\s*)\#\@ARGS\s+(.*)(\s*)\Z/) { |
259
|
0
|
|
|
|
|
0
|
my ($lspace, $argstr, $rspace) = ($1, $2, $3); |
260
|
0
|
|
|
|
|
0
|
my @args = (); |
261
|
0
|
|
|
|
|
0
|
my @declares = (); |
262
|
0
|
|
|
|
|
0
|
foreach my $arg (split(/,/, $argstr)) { |
263
|
0
|
|
|
|
|
0
|
$arg =~ s/(^\s+|\s+$)//g; |
264
|
0
|
0
|
|
|
|
0
|
next unless $arg; |
265
|
0
|
0
|
|
|
|
0
|
$arg =~ m/\A([\$\@\%])?([a-zA-Z_]\w*)\Z/ or croak "[Tenjin] $arg: invalid template argument."; |
266
|
0
|
0
|
0
|
|
|
0
|
croak "[Tenjin] $arg: only '\$var' is available for template argument." unless (!$1 || $1 eq '$'); |
267
|
0
|
|
|
|
|
0
|
my $name = $2; |
268
|
0
|
|
|
|
|
0
|
push(@args, $name); |
269
|
0
|
|
|
|
|
0
|
push(@declares, "my \$$name = \$_context->{$name}; "); |
270
|
|
|
|
|
|
|
} |
271
|
0
|
|
|
|
|
0
|
$self->{args} = \@args; |
272
|
0
|
|
|
|
|
0
|
return $lspace . join('', @declares) . $rspace; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
8
|
|
|
|
|
13
|
return $stmt; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=head2 expand_macro( $funcname, $arg ) |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
This method is in charge of invoking macro functions which might be used |
282
|
|
|
|
|
|
|
inside templates. The following macros are available: |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=over |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=item * C |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
Includes another template, whose name is C<$filename>, inside the current |
289
|
|
|
|
|
|
|
template. The included template will be placed inside the template as if |
290
|
|
|
|
|
|
|
they were one unit, so the context variable applies to both. |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
=item * C and C |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Tells Tenjin to capture the output of the rendered template from the point |
295
|
|
|
|
|
|
|
where C was called to the point where C |
296
|
|
|
|
|
|
|
was called. You must provide a name for the captured portion, which will be |
297
|
|
|
|
|
|
|
made available in the context as C<< $_context->{$name} >> for immediate |
298
|
|
|
|
|
|
|
usage. Note that the captured portion will not be printed unless you do |
299
|
|
|
|
|
|
|
so explicilty with C<< $_context->{$name} >>. |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=item * C and C |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
This is a special method which can be used for making your templates a bit |
304
|
|
|
|
|
|
|
cleaner. Suppose your context might have a variable whose name is defined |
305
|
|
|
|
|
|
|
in C<$var>. If that variable exists in the context, you simply want to print |
306
|
|
|
|
|
|
|
it, but if it's not, you want to print and/or perform other things. In that |
307
|
|
|
|
|
|
|
case you can call C with the name of the context |
308
|
|
|
|
|
|
|
variable you want printed, and if it's not, anything you do between |
309
|
|
|
|
|
|
|
C and C will be printed instead. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=item * echo( $exr ) |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
Just prints the provided expression. You might want to use it if you're |
314
|
|
|
|
|
|
|
a little too comfortable with PHP. |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=back |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=cut |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub expand_macro { |
321
|
7
|
|
|
7
|
1
|
9
|
my ($self, $funcname, $arg) = @_; |
322
|
|
|
|
|
|
|
|
323
|
7
|
|
|
|
|
13
|
my $handler = $MACRO_HANDLER_TABLE->{$funcname}; |
324
|
7
|
100
|
|
|
|
16
|
return $handler ? $handler->($arg) : undef; |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=head2 get_expr_and_escapeflag( $not_escape, $expr, $delete_newline ) |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=cut |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
## ex. get_expr_and_escapeflag('=', '$item->{name}', '') => 1, '$item->{name}', 0 |
332
|
|
|
|
|
|
|
sub get_expr_and_escapeflag { |
333
|
17
|
|
|
17
|
1
|
60
|
my ($self, $not_escape, $expr, $delete_newline) = @_; |
334
|
|
|
|
|
|
|
|
335
|
17
|
|
|
|
|
48
|
return $expr, $not_escape eq '', $delete_newline eq '='; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=head2 parse_expr( $bufref, $input ) |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=cut |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
sub parse_expr { |
343
|
23
|
|
|
23
|
1
|
42
|
my ($self, $bufref, $input) = @_; |
344
|
|
|
|
|
|
|
|
345
|
23
|
|
|
|
|
28
|
my $pos = 0; |
346
|
23
|
|
|
|
|
47
|
$self->start_text_part($bufref); |
347
|
23
|
|
|
|
|
59
|
my $pat = $self->expr_pattern(); |
348
|
23
|
|
|
|
|
158
|
while ($input =~ /$pat/g) { |
349
|
17
|
|
|
|
|
32
|
my $start = $-[0]; |
350
|
17
|
|
|
|
|
51
|
my $text = substr($input, $pos, $start - $pos); |
351
|
17
|
|
|
|
|
35
|
my ($expr, $flag_escape, $delete_newline) = $self->get_expr_and_escapeflag($1, $2, $3); |
352
|
17
|
|
|
|
|
35
|
$pos = $+[0]; |
353
|
17
|
100
|
|
|
|
52
|
$self->add_text($bufref, $text) if $text; |
354
|
17
|
50
|
|
|
|
51
|
$self->add_expr($bufref, $expr, $flag_escape) if $expr; |
355
|
17
|
100
|
|
|
|
95
|
if ($delete_newline) { |
356
|
4
|
|
|
|
|
7
|
my $end = $+[0]; |
357
|
4
|
100
|
|
|
|
28
|
if (substr($input, $end, 1) eq "\n") { |
358
|
1
|
|
|
|
|
2
|
$bufref->[0] .= "\n"; |
359
|
1
|
|
|
|
|
6
|
$pos++; |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
} |
363
|
23
|
100
|
|
|
|
93
|
my $rest = $pos == 0 ? $input : substr($input, $pos); |
364
|
23
|
|
|
|
|
45
|
$self->add_text($bufref, $rest); |
365
|
23
|
|
|
|
|
40
|
$self->stop_text_part($bufref); |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=head2 start_text_part( $bufref ) |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=cut |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub start_text_part { |
373
|
23
|
|
|
23
|
1
|
25
|
my ($self, $bufref) = @_; |
374
|
|
|
|
|
|
|
|
375
|
23
|
|
|
|
|
50
|
$bufref->[0] .= ' $_buf .= '; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=head2 stop_text_part( $bufref ) |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=cut |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub stop_text_part { |
383
|
23
|
|
|
23
|
1
|
28
|
my ($self, $bufref) = @_; |
384
|
|
|
|
|
|
|
|
385
|
23
|
|
|
|
|
100
|
$bufref->[0] .= '; '; |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=head2 add_text( $bufref, $text ) |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=cut |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub add_text { |
393
|
34
|
|
|
34
|
1
|
53
|
my ($self, $bufref, $text) = @_; |
394
|
|
|
|
|
|
|
|
395
|
34
|
100
|
|
|
|
78
|
return unless $text; |
396
|
30
|
|
|
|
|
54
|
$text =~ s/([`\\])/\\$1/g; |
397
|
30
|
|
|
|
|
77
|
my $is_start = $bufref->[0] =~ / \$_buf \.= \Z/; |
398
|
30
|
100
|
|
|
|
153
|
$bufref->[0] .= $is_start ? "q`$text`" : " . q`$text`"; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=head2 add_stmt( $bufref, $stmt ) |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
=cut |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
sub add_stmt { |
406
|
14
|
|
|
14
|
1
|
17
|
my ($self, $bufref, $stmt) = @_; |
407
|
|
|
|
|
|
|
|
408
|
14
|
|
|
|
|
120
|
$bufref->[0] .= $stmt; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
=head2 add_expr( $bufref, $expr, $flag_escape ) |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=cut |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
sub add_expr { |
416
|
17
|
|
|
17
|
1
|
21
|
my ($self, $bufref, $expr, $flag_escape) = @_; |
417
|
|
|
|
|
|
|
|
418
|
17
|
100
|
|
|
|
55
|
my $dot = $bufref->[0] =~ / \$_buf \.= \Z/ ? '' : ' . '; |
419
|
17
|
50
|
|
|
|
73
|
$bufref->[0] .= $dot . ($flag_escape ? $self->escaped_expr($expr) : "($expr)"); |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head2 defun( $funcname, @args ) |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=cut |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub defun { ## (experimental) |
427
|
0
|
|
|
0
|
1
|
0
|
my ($self, $funcname, @args) = @_; |
428
|
|
|
|
|
|
|
|
429
|
0
|
0
|
|
|
|
0
|
unless ($funcname) { |
430
|
0
|
|
|
|
|
0
|
my $funcname = $self->{filename}; |
431
|
0
|
0
|
|
|
|
0
|
if ($funcname) { |
432
|
0
|
|
|
|
|
0
|
$funcname =~ s/\.\w+$//; |
433
|
0
|
|
|
|
|
0
|
$funcname =~ s/[^\w]/_/g; |
434
|
|
|
|
|
|
|
} |
435
|
0
|
|
|
|
|
0
|
$funcname = 'render_' . $funcname; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
0
|
|
|
|
|
0
|
my $str = "sub $funcname { my (\$_context) = \@_; "; |
439
|
0
|
|
|
|
|
0
|
foreach (@args) { |
440
|
0
|
|
|
|
|
0
|
$str .= "my \$$_ = \$_context->{'$_'}; "; |
441
|
|
|
|
|
|
|
} |
442
|
0
|
|
|
|
|
0
|
$str .= $self->{script}; |
443
|
0
|
|
|
|
|
0
|
$str .= "}\n"; |
444
|
|
|
|
|
|
|
|
445
|
0
|
|
|
|
|
0
|
return $str; |
446
|
|
|
|
|
|
|
} |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=head2 compile() |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=cut |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
## compile $self->{script} into closure. |
453
|
|
|
|
|
|
|
sub compile { |
454
|
18
|
|
|
18
|
1
|
28
|
my $self = shift; |
455
|
|
|
|
|
|
|
|
456
|
18
|
50
|
|
|
|
50
|
if ($self->{args}) { |
457
|
0
|
|
|
|
|
0
|
$self->{func} = $Tenjin::CONTEXT_CLASS->to_func($self->{script}, $self->{name}); |
458
|
0
|
|
|
|
|
0
|
return $self->{func}; |
459
|
|
|
|
|
|
|
} |
460
|
18
|
|
|
|
|
31
|
return; |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=head2 escaped_expr( $expr ) |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
Receives a Perl expression (from C<[= $expr =]>) and escapes it. This will |
466
|
|
|
|
|
|
|
happen in one of three ways: with the escape function defined in |
467
|
|
|
|
|
|
|
C<< $opts->{escapefunc} >> (if defined), with a scalar string (if |
468
|
|
|
|
|
|
|
C<< $opts->{rawclass} >> is defined), or with C from |
469
|
|
|
|
|
|
|
L, which uses L. |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=cut |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
sub escaped_expr { |
474
|
0
|
|
|
0
|
1
|
0
|
my ($self, $expr) = @_; |
475
|
|
|
|
|
|
|
|
476
|
0
|
0
|
|
|
|
0
|
return "$self->{escapefunc}($expr)" if $self->{escapefunc}; |
477
|
|
|
|
|
|
|
|
478
|
0
|
0
|
|
|
|
0
|
return "(ref(\$_V = ($expr)) eq '$self->{rawclass}' ? \$_V->{str} : escape_xml($expr)" if $self->{rawclass}; |
479
|
|
|
|
|
|
|
|
480
|
0
|
|
|
|
|
0
|
return "escape_xml($expr)"; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=head2 _read_file( $filename, [$lock_required] ) |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
Receives an absolute path to a template file, reads its content and |
486
|
|
|
|
|
|
|
returns it. If C<$lock_required> is passed (and has a true value), the |
487
|
|
|
|
|
|
|
file will be locked for reading. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=cut |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
sub _read_file { |
492
|
18
|
|
|
18
|
|
27
|
my ($self, $filename, $lock_required) = @_; |
493
|
|
|
|
|
|
|
|
494
|
18
|
50
|
|
7
|
|
513
|
open(IN, "<:encoding($Tenjin::ENCODING)", $filename) |
|
7
|
|
|
|
|
52
|
|
|
7
|
|
|
|
|
11
|
|
|
7
|
|
|
|
|
42
|
|
495
|
|
|
|
|
|
|
or croak "[Tenjin] Can't open $filename for reading: $!"; |
496
|
18
|
50
|
|
|
|
62267
|
flock(IN, LOCK_SH) if $lock_required; |
497
|
|
|
|
|
|
|
|
498
|
18
|
|
|
|
|
442
|
read(IN, my $content, -s $filename); |
499
|
|
|
|
|
|
|
|
500
|
18
|
|
|
|
|
327
|
close(IN); |
501
|
|
|
|
|
|
|
|
502
|
18
|
|
|
|
|
115
|
return $content; |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=head2 _write_file( $filename, $content, [$lock_required] ) |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
Receives an absolute path to a template file and the templates contents, |
508
|
|
|
|
|
|
|
and creates the file (or truncates it, if existing) with that contents. |
509
|
|
|
|
|
|
|
If C<$lock_required> is passed (and has a true value), the file will be |
510
|
|
|
|
|
|
|
locked exclusively when writing. |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=cut |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
sub _write_file { |
515
|
16
|
|
|
16
|
|
27
|
my ($self, $filename, $content, $lock_required) = @_; |
516
|
|
|
|
|
|
|
|
517
|
16
|
50
|
|
|
|
43
|
my $enc = $Tenjin::ENCODING eq 'UTF-8' ? '>:utf8' : ">:encoding($Tenjin::ENCODING)"; |
518
|
|
|
|
|
|
|
|
519
|
16
|
50
|
|
|
|
1070
|
open(OUT, $enc, $filename) |
520
|
|
|
|
|
|
|
or croak "[Tenjin] Can't open $filename for writing: $!"; |
521
|
16
|
50
|
|
|
|
81
|
flock(OUT, LOCK_EX) if $lock_required; |
522
|
16
|
|
|
|
|
130
|
print OUT $content; |
523
|
16
|
|
|
|
|
548
|
close(OUT); |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
1; |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
=head1 SEE ALSO |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
L. |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=head1 AUTHOR, LICENSE AND COPYRIGHT |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
See L. |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=cut |