line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojo::Template; |
2
|
51
|
|
|
51
|
|
886
|
use Mojo::Base -base; |
|
51
|
|
|
|
|
110
|
|
|
51
|
|
|
|
|
551
|
|
3
|
|
|
|
|
|
|
|
4
|
51
|
|
|
51
|
|
698
|
use Carp qw(croak); |
|
51
|
|
|
|
|
148
|
|
|
51
|
|
|
|
|
2365
|
|
5
|
51
|
|
|
51
|
|
411
|
use Mojo::ByteStream; |
|
51
|
|
|
|
|
142
|
|
|
51
|
|
|
|
|
2114
|
|
6
|
51
|
|
|
51
|
|
806
|
use Mojo::Exception; |
|
51
|
|
|
|
|
149
|
|
|
51
|
|
|
|
|
2219
|
|
7
|
51
|
|
|
51
|
|
405
|
use Mojo::File qw(path); |
|
51
|
|
|
|
|
143
|
|
|
51
|
|
|
|
|
2477
|
|
8
|
51
|
|
|
51
|
|
371
|
use Mojo::Util qw(decode encode monkey_patch); |
|
51
|
|
|
|
|
167
|
|
|
51
|
|
|
|
|
3596
|
|
9
|
|
|
|
|
|
|
|
10
|
51
|
|
50
|
51
|
|
418
|
use constant DEBUG => $ENV{MOJO_TEMPLATE_DEBUG} || 0; |
|
51
|
|
|
|
|
164
|
|
|
51
|
|
|
|
|
152428
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
has [qw(append code prepend unparsed)] => ''; |
13
|
|
|
|
|
|
|
has [qw(auto_escape compiled vars)]; |
14
|
|
|
|
|
|
|
has capture_end => 'end'; |
15
|
|
|
|
|
|
|
has capture_start => 'begin'; |
16
|
|
|
|
|
|
|
has comment_mark => '#'; |
17
|
|
|
|
|
|
|
has encoding => 'UTF-8'; |
18
|
|
|
|
|
|
|
has escape => sub { \&Mojo::Util::xml_escape }; |
19
|
|
|
|
|
|
|
has [qw(escape_mark expression_mark trim_mark)] => '='; |
20
|
|
|
|
|
|
|
has [qw(line_start replace_mark)] => '%'; |
21
|
|
|
|
|
|
|
has name => 'template'; |
22
|
|
|
|
|
|
|
has namespace => 'Mojo::Template::Sandbox'; |
23
|
|
|
|
|
|
|
has tag_start => '<%'; |
24
|
|
|
|
|
|
|
has tag_end => '%>'; |
25
|
|
|
|
|
|
|
has tree => sub { [] }; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub parse { |
28
|
373
|
|
|
373
|
1
|
877
|
my ($self, $template) = @_; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Clean start |
31
|
373
|
|
|
|
|
1218
|
$self->unparsed($template)->tree(\my @tree)->compiled(undef); |
32
|
|
|
|
|
|
|
|
33
|
373
|
|
|
|
|
1188
|
my $tag = $self->tag_start; |
34
|
373
|
|
|
|
|
1107
|
my $replace = $self->replace_mark; |
35
|
373
|
|
|
|
|
1038
|
my $expr = $self->expression_mark; |
36
|
373
|
|
|
|
|
1042
|
my $escp = $self->escape_mark; |
37
|
373
|
|
|
|
|
1098
|
my $cpen = $self->capture_end; |
38
|
373
|
|
|
|
|
1091
|
my $cmnt = $self->comment_mark; |
39
|
373
|
|
|
|
|
1032
|
my $cpst = $self->capture_start; |
40
|
373
|
|
|
|
|
1052
|
my $trim = $self->trim_mark; |
41
|
373
|
|
|
|
|
1007
|
my $end = $self->tag_end; |
42
|
373
|
|
|
|
|
940
|
my $start = $self->line_start; |
43
|
|
|
|
|
|
|
|
44
|
373
|
|
|
|
|
4595
|
my $line_re = qr/^(\s*)\Q$start\E(?:(\Q$replace\E)|(\Q$cmnt\E)|(\Q$expr\E))?(.*)$/; |
45
|
373
|
|
|
|
|
5011
|
my $token_re = qr/ |
46
|
|
|
|
|
|
|
( |
47
|
|
|
|
|
|
|
\Q$tag\E(?:\Q$replace\E|\Q$cmnt\E) # Replace |
48
|
|
|
|
|
|
|
| |
49
|
|
|
|
|
|
|
\Q$tag$expr\E(?:\Q$escp\E)?(?:\s*\Q$cpen\E(?!\w))? # Expression |
50
|
|
|
|
|
|
|
| |
51
|
|
|
|
|
|
|
\Q$tag\E(?:\s*\Q$cpen\E(?!\w))? # Code |
52
|
|
|
|
|
|
|
| |
53
|
|
|
|
|
|
|
(?:(?
|
54
|
|
|
|
|
|
|
) |
55
|
|
|
|
|
|
|
/x; |
56
|
373
|
|
|
|
|
2398
|
my $cpen_re = qr/^\Q$tag\E(?:\Q$expr\E)?(?:\Q$escp\E)?\s*\Q$cpen\E(.*)$/; |
57
|
373
|
|
|
|
|
2232
|
my $end_re = qr/^(?:(\Q$cpst\E)\s*)?(\Q$trim\E)?\Q$end\E$/; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# Split lines |
60
|
373
|
|
|
|
|
746
|
my $op = 'text'; |
61
|
373
|
|
|
|
|
646
|
my ($trimming, $capture); |
62
|
373
|
|
|
|
|
3647
|
for my $line (split /\n/, $template) { |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# Turn Perl line into mixed line |
65
|
3846
|
100
|
100
|
|
|
29689
|
if ($op eq 'text' && $line =~ $line_re) { |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Escaped start |
68
|
1272
|
100
|
|
|
|
3978
|
if ($2) { $line = "$1$start$5" } |
|
9
|
100
|
|
|
|
37
|
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# Comment |
71
|
7
|
|
|
|
|
32
|
elsif ($3) { $line = "$tag$3 $trim$end" } |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Expression or code |
74
|
1256
|
100
|
|
|
|
5477
|
else { $line = $4 ? "$1$tag$4$5 $end" : "$tag$5 $trim$end" } |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Escaped line ending |
78
|
3846
|
100
|
100
|
|
|
18235
|
$line .= "\n" if $line !~ s/\\\\$/\\\n/ && $line !~ s/\\$//; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# Mixed line |
81
|
3846
|
|
|
|
|
64128
|
for my $token (split $token_re, $line) { |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Capture end |
84
|
11303
|
100
|
|
|
|
38698
|
($token, $capture) = ("$tag$1", 1) if $token =~ $cpen_re; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# End |
87
|
11303
|
100
|
100
|
|
|
56308
|
if ($op ne 'text' && $token =~ $end_re) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# Capture start |
90
|
1854
|
100
|
|
|
|
5093
|
splice @tree, -1, 0, ['cpst'] if $1; |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# Trim left side |
93
|
1854
|
100
|
100
|
|
|
6560
|
_trim(\@tree) if ($trimming = $2) && @tree > 1; |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# Hint at end |
96
|
1854
|
|
|
|
|
5415
|
push @tree, [$op = 'text', '']; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Code |
100
|
788
|
|
|
|
|
1351
|
elsif ($token eq $tag) { $op = 'code' } |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Expression |
103
|
997
|
|
|
|
|
1691
|
elsif ($token eq "$tag$expr") { $op = 'expr' } |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# Expression that needs to be escaped |
106
|
59
|
|
|
|
|
117
|
elsif ($token eq "$tag$expr$escp") { $op = 'escp' } |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# Comment |
109
|
10
|
|
|
|
|
21
|
elsif ($token eq "$tag$cmnt") { $op = 'cmnt' } |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# Text (comments are just ignored) |
112
|
|
|
|
|
|
|
elsif ($op ne 'cmnt') { |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# Replace |
115
|
7583
|
100
|
|
|
|
13944
|
$token = $tag if $token eq "$tag$replace"; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# Trim right side (convert whitespace to line noise) |
118
|
7583
|
100
|
100
|
|
|
16234
|
if ($trimming && $token =~ s/^(\s+)//) { |
119
|
733
|
|
|
|
|
2383
|
push @tree, ['code', $1]; |
120
|
733
|
|
|
|
|
1211
|
$trimming = 0; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# Token (with optional capture end) |
124
|
7583
|
100
|
|
|
|
20410
|
push @tree, $capture ? ['cpen'] : (), [$op, $token]; |
125
|
7583
|
|
|
|
|
13358
|
$capture = 0; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# Optimize successive text lines separated by a newline |
130
|
3846
|
100
|
50
|
|
|
27931
|
push @tree, ['line'] and next |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
131
|
|
|
|
|
|
|
if $tree[-4] && $tree[-4][0] ne 'line' |
132
|
|
|
|
|
|
|
|| (!$tree[-3] || $tree[-3][0] ne 'text' || $tree[-3][1] !~ /\n$/) |
133
|
|
|
|
|
|
|
|| ($tree[-2][0] ne 'line' || $tree[-1][0] ne 'text'); |
134
|
1535
|
|
|
|
|
5304
|
$tree[-3][1] .= pop(@tree)->[1]; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
373
|
|
|
|
|
3232
|
return $self; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub process { |
141
|
538
|
|
|
538
|
1
|
1785
|
my $self = shift; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# Use a local stack trace for compile exceptions |
144
|
538
|
|
|
|
|
1569
|
my $compiled = $self->compiled; |
145
|
538
|
100
|
|
|
|
1423
|
unless ($compiled) { |
146
|
373
|
|
|
|
|
1083
|
my $code = $self->_compile->code; |
147
|
373
|
|
|
|
|
1256
|
monkey_patch $self->namespace, '_escape', $self->escape; |
148
|
373
|
100
|
|
|
|
1485
|
return Mojo::Exception->new($@)->inspect($self->unparsed, $code)->trace->verbose(1) |
149
|
|
|
|
|
|
|
unless $compiled = eval $self->_wrap($code, @_); |
150
|
366
|
|
|
|
|
106464
|
$self->compiled($compiled); |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# Use a real stack trace for normal exceptions |
154
|
|
|
|
|
|
|
local $SIG{__DIE__} = sub { |
155
|
46
|
100
|
|
46
|
|
516
|
CORE::die $_[0] if ref $_[0]; |
156
|
43
|
|
|
|
|
354
|
CORE::die Mojo::Exception->new(shift)->trace->inspect($self->unparsed, $self->code)->verbose(1); |
157
|
531
|
|
|
|
|
4280
|
}; |
158
|
|
|
|
|
|
|
|
159
|
531
|
|
|
|
|
1189
|
my $output; |
160
|
531
|
100
|
|
|
|
927
|
return eval { $output = $compiled->(@_); 1 } ? $output : $@; |
|
531
|
|
|
|
|
1658
|
|
|
509
|
|
|
|
|
9888
|
|
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
372
|
|
|
372
|
1
|
3418
|
sub render { shift->parse(shift)->process(@_) } |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub render_file { |
166
|
56
|
|
|
56
|
1
|
238
|
my ($self, $path) = (shift, shift); |
167
|
|
|
|
|
|
|
|
168
|
56
|
100
|
|
|
|
243
|
$self->name($path) unless defined $self->{name}; |
169
|
56
|
|
|
|
|
233
|
my $template = path($path)->slurp; |
170
|
56
|
|
|
|
|
861
|
my $encoding = $self->encoding; |
171
|
56
|
100
|
66
|
|
|
435
|
croak qq{Template "$path" has invalid encoding} if $encoding && !defined($template = decode $encoding, $template); |
172
|
|
|
|
|
|
|
|
173
|
55
|
|
|
|
|
352
|
return $self->render($template, @_); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub _compile { |
177
|
373
|
|
|
373
|
|
620
|
my $self = shift; |
178
|
|
|
|
|
|
|
|
179
|
373
|
|
|
|
|
883
|
my $tree = $self->tree; |
180
|
373
|
|
|
|
|
1229
|
my $escape = $self->auto_escape; |
181
|
|
|
|
|
|
|
|
182
|
373
|
|
|
|
|
969
|
my @blocks = (''); |
183
|
373
|
|
|
|
|
770
|
my ($i, $capture, $multi); |
184
|
373
|
|
100
|
|
|
1987
|
while (++$i <= @$tree && (my $next = $tree->[$i])) { |
185
|
10833
|
|
|
|
|
13800
|
my ($op, $value) = @{$tree->[$i - 1]}; |
|
10833
|
|
|
|
|
20178
|
|
186
|
10833
|
100
|
50
|
|
|
24918
|
push @blocks, '' and next if $op eq 'line'; |
187
|
8893
|
|
100
|
|
|
17037
|
my $newline = chomp($value //= ''); |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# Text (quote and fix line ending) |
190
|
8893
|
100
|
100
|
|
|
17361
|
if ($op eq 'text') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
191
|
6034
|
|
|
|
|
12455
|
$value = join "\n", map { quotemeta $_ } split(/\n/, $value, -1); |
|
3024
|
|
|
|
|
7861
|
|
192
|
6034
|
100
|
|
|
|
11403
|
$value .= '\n' if $newline; |
193
|
6034
|
100
|
|
|
|
13947
|
$blocks[-1] .= "\$_O .= \"" . $value . "\";" if length $value; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Code or multi-line expression |
197
|
1567
|
|
|
|
|
2653
|
elsif ($op eq 'code' || $multi) { $blocks[-1] .= $value } |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# Capture end |
200
|
|
|
|
|
|
|
elsif ($op eq 'cpen') { |
201
|
118
|
|
|
|
|
320
|
$blocks[-1] .= 'return Mojo::ByteStream->new($_O) }'; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
# No following code |
204
|
118
|
100
|
50
|
|
|
965
|
$blocks[-1] .= ';' if $next->[0] ne 'cpst' && ($next->[1] // '') =~ /^\s*$/; |
|
|
|
100
|
|
|
|
|
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# Expression |
208
|
8893
|
100
|
100
|
|
|
23719
|
if ($op eq 'expr' || $op eq 'escp') { |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# Escaped |
211
|
1070
|
100
|
100
|
|
|
5731
|
if (!$multi && ($op eq 'escp' && !$escape || $op eq 'expr' && $escape)) { |
|
|
100
|
100
|
|
|
|
|
212
|
864
|
|
|
|
|
1970
|
$blocks[-1] .= "\$_O .= _escape scalar + $value"; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# Raw |
216
|
192
|
|
|
|
|
453
|
elsif (!$multi) { $blocks[-1] .= "\$_O .= scalar + $value" } |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Multi-line |
219
|
1070
|
|
66
|
|
|
3460
|
$multi = !$next || $next->[0] ne 'text'; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# Append semicolon |
222
|
1070
|
100
|
100
|
|
|
3498
|
$blocks[-1] .= ';' unless $multi || $capture; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# Capture start |
226
|
8893
|
100
|
|
|
|
33378
|
if ($op eq 'cpst') { $capture = 1 } |
|
118
|
100
|
|
|
|
405
|
|
227
|
|
|
|
|
|
|
elsif ($capture) { |
228
|
118
|
|
|
|
|
295
|
$blocks[-1] .= "sub { my \$_O = ''; "; |
229
|
118
|
|
|
|
|
444
|
$capture = 0; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
373
|
|
|
|
|
2889
|
return $self->code(join "\n", @blocks)->tree([]); |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub _line { |
237
|
746
|
|
|
746
|
|
1700
|
my $name = shift->name; |
238
|
746
|
|
|
|
|
2142
|
$name =~ y/"//d; |
239
|
746
|
|
|
|
|
1155
|
return qq{#line @{[shift]} "$name"}; |
|
746
|
|
|
|
|
3176
|
|
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
sub _trim { |
243
|
732
|
|
|
732
|
|
1150
|
my $tree = shift; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# Skip captures |
246
|
732
|
100
|
100
|
|
|
2722
|
my $i = $tree->[-2][0] eq 'cpst' || $tree->[-2][0] eq 'cpen' ? -3 : -2; |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# Only trim text |
249
|
732
|
100
|
|
|
|
1540
|
return unless $tree->[$i][0] eq 'text'; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# Convert whitespace text to line noise |
252
|
721
|
100
|
|
|
|
2031
|
splice @$tree, $i, 0, ['code', $1] if $tree->[$i][1] =~ s/(\s+)$//; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub _wrap { |
256
|
373
|
|
|
373
|
|
977
|
my ($self, $body, $vars) = @_; |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# Variables |
259
|
373
|
|
|
|
|
702
|
my $args = ''; |
260
|
373
|
100
|
100
|
|
|
1003
|
if ($self->vars && (my @vars = grep {/^\w+$/} keys %$vars)) { |
|
949
|
|
|
|
|
3244
|
|
261
|
133
|
|
|
|
|
784
|
$args = 'my (' . join(',', map {"\$$_"} @vars) . ')'; |
|
384
|
|
|
|
|
1064
|
|
262
|
133
|
|
|
|
|
512
|
$args .= '= @{shift()}{qw(' . join(' ', @vars) . ')};'; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# Wrap lines |
266
|
373
|
|
|
|
|
2306
|
my $num = () = $body =~ /\n/g; |
267
|
373
|
|
|
|
|
1288
|
my $code = $self->_line(1) . "\npackage @{[$self->namespace]};"; |
|
373
|
|
|
|
|
997
|
|
268
|
373
|
|
|
|
|
1020
|
$code .= "use Mojo::Base -strict; no warnings 'ambiguous';"; |
269
|
373
|
|
|
|
|
1129
|
$code .= "sub { my \$_O = ''; @{[$self->prepend]};{ $args { $body\n"; |
|
373
|
|
|
|
|
1068
|
|
270
|
373
|
|
|
|
|
1197
|
$code .= $self->_line($num + 1) . "\n;}@{[$self->append]}; } \$_O };"; |
|
373
|
|
|
|
|
1056
|
|
271
|
|
|
|
|
|
|
|
272
|
373
|
|
|
|
|
781
|
warn "-- Code for @{[$self->name]}\n@{[encode 'UTF-8', $code]}\n\n" if DEBUG; |
273
|
373
|
|
|
|
|
119456
|
return $code; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
1; |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=encoding utf8 |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
=head1 NAME |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
Mojo::Template - Perl-ish templates |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=head1 SYNOPSIS |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
use Mojo::Template; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# Use Perl modules |
289
|
|
|
|
|
|
|
my $mt = Mojo::Template->new; |
290
|
|
|
|
|
|
|
say $mt->render(<<'EOF'); |
291
|
|
|
|
|
|
|
% use Time::Piece; |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
% my $now = localtime; |
294
|
|
|
|
|
|
|
Time: <%= $now->hms %> |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
EOF |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# Render with arguments |
299
|
|
|
|
|
|
|
say $mt->render(<<'EOF', [1 .. 13], 'Hello World!'); |
300
|
|
|
|
|
|
|
% my ($numbers, $title) = @_; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
<%= $title %> |
303
|
|
|
|
|
|
|
% for my $i (@$numbers) { |
304
|
|
|
|
|
|
|
Test <%= $i %> |
305
|
|
|
|
|
|
|
% } |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
EOF |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# Render with named variables |
310
|
|
|
|
|
|
|
say $mt->vars(1)->render(<<'EOF', {title => 'Hello World!'}); |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
<%= $title %> |
313
|
|
|
|
|
|
|
%= 5 + 5 |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
EOF |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=head1 DESCRIPTION |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
L is a minimalistic, fast, and very Perl-ish template engine, designed specifically for all those small |
320
|
|
|
|
|
|
|
tasks that come up during big projects. Like preprocessing a configuration file, generating text from heredocs and |
321
|
|
|
|
|
|
|
stuff like that. |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
See L for information on how to generate content with the L renderer. |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=head1 SYNTAX |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
For all templates L, L, L and Perl 5.16 L are automatically enabled. |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
<% Perl code %> |
330
|
|
|
|
|
|
|
<%= Perl expression, replaced with result %> |
331
|
|
|
|
|
|
|
<%== Perl expression, replaced with XML escaped result %> |
332
|
|
|
|
|
|
|
<%# Comment, useful for debugging %> |
333
|
|
|
|
|
|
|
<%% Replaced with "<%", useful for generating templates %> |
334
|
|
|
|
|
|
|
% Perl code line, treated as "<% line =%>" (explained later) |
335
|
|
|
|
|
|
|
%= Perl expression line, treated as "<%= line %>" |
336
|
|
|
|
|
|
|
%== Perl expression line, treated as "<%== line %>" |
337
|
|
|
|
|
|
|
%# Comment line, useful for debugging |
338
|
|
|
|
|
|
|
%% Replaced with "%", useful for generating templates |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
Escaping behavior can be reversed with the L"auto_escape"> attribute, this is the default in L C<.ep> |
341
|
|
|
|
|
|
|
templates, for example. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
<%= Perl expression, replaced with XML escaped result %> |
344
|
|
|
|
|
|
|
<%== Perl expression, replaced with result %> |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
L objects are always excluded from automatic escaping. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
% use Mojo::ByteStream qw(b); |
349
|
|
|
|
|
|
|
<%= b(' excluded! ') %> |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
Whitespace characters around tags can be trimmed by adding an additional equal sign to the end of a tag. |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
<% for (1 .. 3) { %> |
354
|
|
|
|
|
|
|
<%= 'Trim all whitespace characters around this expression' =%> |
355
|
|
|
|
|
|
|
<% } %> |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Newline characters can be escaped with a backslash. |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
This is <%= 1 + 1 %> a\ |
360
|
|
|
|
|
|
|
single line |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
And a backslash in front of a newline character can be escaped with another backslash. |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
This will <%= 1 + 1 %> result\\ |
365
|
|
|
|
|
|
|
in multiple\\ |
366
|
|
|
|
|
|
|
lines |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
A newline character gets appended automatically to every template, unless the last character is a backslash. And empty |
369
|
|
|
|
|
|
|
lines at the end of a template are ignored. |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
There is <%= 1 + 1 %> no newline at the end here\ |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
You can capture whole template blocks for reuse later with the C and C keywords. Just be aware that both |
374
|
|
|
|
|
|
|
keywords are part of the surrounding tag and not actual Perl code, so there can only be whitespace after C and |
375
|
|
|
|
|
|
|
before C. |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
<% my $block = begin %> |
378
|
|
|
|
|
|
|
<% my $name = shift; =%> |
379
|
|
|
|
|
|
|
Hello <%= $name %>. |
380
|
|
|
|
|
|
|
<% end %> |
381
|
|
|
|
|
|
|
<%= $block->('Baerbel') %> |
382
|
|
|
|
|
|
|
<%= $block->('Wolfgang') %> |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Perl lines can also be indented freely. |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
% my $block = begin |
387
|
|
|
|
|
|
|
% my $name = shift; |
388
|
|
|
|
|
|
|
Hello <%= $name %>. |
389
|
|
|
|
|
|
|
% end |
390
|
|
|
|
|
|
|
%= $block->('Baerbel') |
391
|
|
|
|
|
|
|
%= $block->('Wolfgang') |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
L templates get compiled to a Perl subroutine, that means you can access arguments simply via C<@_>. |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
% my ($foo, $bar) = @_; |
396
|
|
|
|
|
|
|
% my $x = shift; |
397
|
|
|
|
|
|
|
test 123 <%= $foo %> |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
The compilation of templates to Perl code can make debugging a bit tricky, but L will return |
400
|
|
|
|
|
|
|
L objects that stringify to error messages with context. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
Bareword "xx" not allowed while "strict subs" in use at template line 4. |
403
|
|
|
|
|
|
|
Context: |
404
|
|
|
|
|
|
|
2: |
405
|
|
|
|
|
|
|
3: |
406
|
|
|
|
|
|
|
4: % my $i = 2; xx |
407
|
|
|
|
|
|
|
5: %= $i * 2 |
408
|
|
|
|
|
|
|
6: |
409
|
|
|
|
|
|
|
Traceback (most recent call first): |
410
|
|
|
|
|
|
|
File "template", line 4, in "Mojo::Template::Sandbox" |
411
|
|
|
|
|
|
|
File "path/to/Mojo/Template.pm", line 123, in "Mojo::Template" |
412
|
|
|
|
|
|
|
File "path/to/myapp.pl", line 123, in "main" |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
L implements the following attributes. |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=head2 auto_escape |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
my $bool = $mt->auto_escape; |
421
|
|
|
|
|
|
|
$mt = $mt->auto_escape($bool); |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
Activate automatic escaping. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
# "<html>" |
426
|
|
|
|
|
|
|
Mojo::Template->new(auto_escape => 1)->render("<%= '' %>"); |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=head2 append |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
my $code = $mt->append; |
431
|
|
|
|
|
|
|
$mt = $mt->append('warn "Processed template"'); |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
Append Perl code to compiled template. Note that this code should not contain newline characters, or line numbers in |
434
|
|
|
|
|
|
|
error messages might end up being wrong. |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=head2 capture_end |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
my $end = $mt->capture_end; |
439
|
|
|
|
|
|
|
$mt = $mt->capture_end('end'); |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
Keyword indicating the end of a capture block, defaults to C. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
<% my $block = begin %> |
444
|
|
|
|
|
|
|
Some data! |
445
|
|
|
|
|
|
|
<% end %> |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=head2 capture_start |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
my $start = $mt->capture_start; |
450
|
|
|
|
|
|
|
$mt = $mt->capture_start('begin'); |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Keyword indicating the start of a capture block, defaults to C. |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
<% my $block = begin %> |
455
|
|
|
|
|
|
|
Some data! |
456
|
|
|
|
|
|
|
<% end %> |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=head2 code |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
my $code = $mt->code; |
461
|
|
|
|
|
|
|
$mt = $mt->code($code); |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
Perl code for template if available. |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=head2 comment_mark |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
my $mark = $mt->comment_mark; |
468
|
|
|
|
|
|
|
$mt = $mt->comment_mark('#'); |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
Character indicating the start of a comment, defaults to C<#>. |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
<%# This is a comment %> |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=head2 compiled |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
my $compiled = $mt->compiled; |
477
|
|
|
|
|
|
|
$mt = $mt->compiled($compiled); |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
Compiled template code if available. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=head2 encoding |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
my $encoding = $mt->encoding; |
484
|
|
|
|
|
|
|
$mt = $mt->encoding('UTF-8'); |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
Encoding used for template files, defaults to C. |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=head2 escape |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
my $cb = $mt->escape; |
491
|
|
|
|
|
|
|
$mt = $mt->escape(sub {...}); |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
A callback used to escape the results of escaped expressions, defaults to L. |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
$mt->escape(sub ($str) { return reverse $str }); |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=head2 escape_mark |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
my $mark = $mt->escape_mark; |
500
|
|
|
|
|
|
|
$mt = $mt->escape_mark('='); |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
Character indicating the start of an escaped expression, defaults to C<=>. |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
<%== $foo %> |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=head2 expression_mark |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
my $mark = $mt->expression_mark; |
509
|
|
|
|
|
|
|
$mt = $mt->expression_mark('='); |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
Character indicating the start of an expression, defaults to C<=>. |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
<%= $foo %> |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=head2 line_start |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
my $start = $mt->line_start; |
518
|
|
|
|
|
|
|
$mt = $mt->line_start('%'); |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
Character indicating the start of a code line, defaults to C<%>. |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
% $foo = 23; |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head2 name |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
my $name = $mt->name; |
527
|
|
|
|
|
|
|
$mt = $mt->name('foo.mt'); |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
Name of template currently being processed, defaults to C. Note that this value should not contain quotes or |
530
|
|
|
|
|
|
|
newline characters, or error messages might end up being wrong. |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=head2 namespace |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
my $namespace = $mt->namespace; |
535
|
|
|
|
|
|
|
$mt = $mt->namespace('main'); |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
Namespace used to compile templates, defaults to C. Note that namespaces should only be shared |
538
|
|
|
|
|
|
|
very carefully between templates, since functions and global variables will not be cleared automatically. |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=head2 prepend |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
my $code = $mt->prepend; |
543
|
|
|
|
|
|
|
$mt = $mt->prepend('my $self = shift;'); |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
Prepend Perl code to compiled template. Note that this code should not contain newline characters, or line numbers in |
546
|
|
|
|
|
|
|
error messages might end up being wrong. |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=head2 replace_mark |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
my $mark = $mt->replace_mark; |
551
|
|
|
|
|
|
|
$mt = $mt->replace_mark('%'); |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
Character used for escaping the start of a tag or line, defaults to C<%>. |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
<%% my $foo = 23; %> |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=head2 tag_start |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
my $start = $mt->tag_start; |
560
|
|
|
|
|
|
|
$mt = $mt->tag_start('<%'); |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
Characters indicating the start of a tag, defaults to C%>. |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
<% $foo = 23; %> |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=head2 tag_end |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
my $end = $mt->tag_end; |
569
|
|
|
|
|
|
|
$mt = $mt->tag_end('%>'); |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
Characters indicating the end of a tag, defaults to C<%E>. |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
<%= $foo %> |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=head2 tree |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
my $tree = $mt->tree; |
578
|
|
|
|
|
|
|
$mt = $mt->tree([['text', 'foo'], ['line']]); |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
Template in parsed form if available. Note that this structure should only be used very carefully since it is very |
581
|
|
|
|
|
|
|
dynamic. |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
=head2 trim_mark |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
my $mark = $mt->trim_mark; |
586
|
|
|
|
|
|
|
$mt = $mt->trim_mark('-'); |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
Character activating automatic whitespace trimming, defaults to C<=>. |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
<%= $foo =%> |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=head2 unparsed |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
my $unparsed = $mt->unparsed; |
595
|
|
|
|
|
|
|
$mt = $mt->unparsed('<%= 1 + 1 %>'); |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
Raw unparsed template if available. |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=head2 vars |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
my $bool = $mt->vars; |
602
|
|
|
|
|
|
|
$mt = $mt->vars($bool); |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
Instead of a list of values, use a hash reference with named variables to pass data to templates. |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# "works!" |
607
|
|
|
|
|
|
|
Mojo::Template->new(vars => 1)->render('<%= $test %>!', {test => 'works'}); |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=head1 METHODS |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
L inherits all methods from L and implements the following new ones. |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=head2 parse |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
$mt = $mt->parse('<%= 1 + 1 %>'); |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
Parse template into L"tree">. |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
=head2 process |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
my $output = $mt->process; |
622
|
|
|
|
|
|
|
my $output = $mt->process(@args); |
623
|
|
|
|
|
|
|
my $output = $mt->process({foo => 'bar'}); |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
Process previously parsed template and return the result, or a L object if rendering failed. |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
# Parse and process |
628
|
|
|
|
|
|
|
say Mojo::Template->new->parse('Hello <%= $_[0] %>')->process('Bender'); |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
# Reuse template (for much better performance) |
631
|
|
|
|
|
|
|
my $mt = Mojo::Template->new; |
632
|
|
|
|
|
|
|
say $mt->render('Hello <%= $_[0] %>!', 'Bender'); |
633
|
|
|
|
|
|
|
say $mt->process('Fry'); |
634
|
|
|
|
|
|
|
say $mt->process('Leela'); |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=head2 render |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
my $output = $mt->render('<%= 1 + 1 %>'); |
639
|
|
|
|
|
|
|
my $output = $mt->render('<%= shift() + shift() %>', @args); |
640
|
|
|
|
|
|
|
my $output = $mt->render('<%= $foo %>', {foo => 'bar'}); |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
Render template and return the result, or a L object if rendering failed. |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
# Longer version |
645
|
|
|
|
|
|
|
my $output = $mt->parse('<%= 1 + 1 %>')->process; |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
# Render with arguments |
648
|
|
|
|
|
|
|
say Mojo::Template->new->render('<%= $_[0] %>', 'bar'); |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
# Render with named variables |
651
|
|
|
|
|
|
|
say Mojo::Template->new(vars => 1)->render('<%= $foo %>', {foo => 'bar'}); |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
=head2 render_file |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
my $output = $mt->render_file('/tmp/foo.mt'); |
656
|
|
|
|
|
|
|
my $output = $mt->render_file('/tmp/foo.mt', @args); |
657
|
|
|
|
|
|
|
my $output = $mt->render_file('/tmp/bar.mt', {foo => 'bar'}); |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
Same as L"render">, but renders a template file. |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
=head1 DEBUGGING |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
You can set the C environment variable to get some advanced diagnostics information printed to |
664
|
|
|
|
|
|
|
C. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
MOJO_TEMPLATE_DEBUG=1 |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
=head1 SEE ALSO |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
L, L, L. |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=cut |