| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Mustache::Simple; |
|
2
|
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
150135
|
use strict; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
76
|
|
|
4
|
3
|
|
|
3
|
|
14
|
use warnings; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
67
|
|
|
5
|
3
|
|
|
3
|
|
34
|
use 5.10.1; |
|
|
3
|
|
|
|
|
11
|
|
|
6
|
3
|
|
|
3
|
|
1030
|
use utf8; |
|
|
3
|
|
|
|
|
35
|
|
|
|
3
|
|
|
|
|
13
|
|
|
7
|
3
|
|
|
3
|
|
563
|
use experimental qw(switch); |
|
|
3
|
|
|
|
|
4696
|
|
|
|
3
|
|
|
|
|
16
|
|
|
8
|
3
|
|
|
3
|
|
412
|
use version; |
|
|
3
|
|
|
|
|
8
|
|
|
|
3
|
|
|
|
|
15
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# Don't forget to change the version in the pod |
|
11
|
|
|
|
|
|
|
our $VERSION = version->declare('v1.3.6'); |
|
12
|
|
|
|
|
|
|
|
|
13
|
3
|
|
|
3
|
|
263
|
use File::Spec; |
|
|
3
|
|
|
|
|
9
|
|
|
|
3
|
|
|
|
|
82
|
|
|
14
|
3
|
|
|
3
|
|
835
|
use Mustache::Simple::ContextStack v1.3.6; |
|
|
3
|
|
|
|
|
39
|
|
|
|
3
|
|
|
|
|
84
|
|
|
15
|
3
|
|
|
3
|
|
20
|
use Scalar::Util qw( reftype ); |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
110
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
3
|
|
|
3
|
|
14
|
use Carp; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
955
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
#use Data::Dumper; |
|
20
|
|
|
|
|
|
|
#$Data::Dumper::Useqq = 1; |
|
21
|
|
|
|
|
|
|
#$Data::Dumper::Deparse = 1; |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=encoding utf8 |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 NAME |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Mustache::Simple - A simple Mustache Renderer |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
See L. |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 VERSION |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
This document describes Mustache::Simple version 1.3.6 |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
A typical Mustache template: |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my $template = <
|
|
40
|
|
|
|
|
|
|
Hello {{name}} |
|
41
|
|
|
|
|
|
|
You have just won ${{value}}! |
|
42
|
|
|
|
|
|
|
{{#in_ca}} |
|
43
|
|
|
|
|
|
|
Well, ${{taxed_value}}, after taxes. |
|
44
|
|
|
|
|
|
|
{{/in_ca}} |
|
45
|
|
|
|
|
|
|
EOT |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Given the following hashref: |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
my $context = { |
|
50
|
|
|
|
|
|
|
name => "Chris", |
|
51
|
|
|
|
|
|
|
value => 10000, |
|
52
|
|
|
|
|
|
|
taxed_value => 10000 - (10000 * 0.4), |
|
53
|
|
|
|
|
|
|
in_ca => 1 |
|
54
|
|
|
|
|
|
|
}; |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
Will produce the following: |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Hello Chris |
|
59
|
|
|
|
|
|
|
You have just won $10000! |
|
60
|
|
|
|
|
|
|
Well, $6000, after taxes. |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
using the following code: |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
my $tache = new Mustache::Simple( |
|
65
|
|
|
|
|
|
|
throw => 1 |
|
66
|
|
|
|
|
|
|
); |
|
67
|
|
|
|
|
|
|
my $output = $tache->render($template, $context); |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=cut |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Mustache can be used for HTML, config files, source code - anything. It works |
|
74
|
|
|
|
|
|
|
by expanding tags in a template using values provided in a hash or object. |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
There are no if statements, else clauses, or |
|
77
|
|
|
|
|
|
|
for loops. Instead there are only tags. Some tags are replaced with a value, |
|
78
|
|
|
|
|
|
|
some nothing, and others a series of values. |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
This is a simple perl implementation of the Mustache rendering. It has |
|
81
|
|
|
|
|
|
|
a single class method, new() to obtain an object and a single instance |
|
82
|
|
|
|
|
|
|
method render() to convert the template and the hashref into the final |
|
83
|
|
|
|
|
|
|
output. |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
As of version 1.2.0, it has support for nested contexts, for the dot notation |
|
86
|
|
|
|
|
|
|
and for the implicit iterator. |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
As of version 1.3.0, it will accept a blessed object. For any C<{{item}}> |
|
89
|
|
|
|
|
|
|
where the object has a method called item (as returned by C<< $object->can >>), |
|
90
|
|
|
|
|
|
|
the value will be the return from the method call (with no parameters). |
|
91
|
|
|
|
|
|
|
If C<< $object->can(item) >> returns C, the object will be treated |
|
92
|
|
|
|
|
|
|
as a hash and the value looked up directly. See L below. |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
As of version 1.3.6, if a method call on a blessed object returns an array, |
|
95
|
|
|
|
|
|
|
a C<{{#item}}> section will iterate over the array. This also works |
|
96
|
|
|
|
|
|
|
recursively, so a method can return an array of objects. |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head2 Rationale |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
I wanted a simple rendering tool for Mustache that did not require any |
|
101
|
|
|
|
|
|
|
subclassing. |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=cut |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
############################################################# |
|
107
|
|
|
|
|
|
|
## |
|
108
|
|
|
|
|
|
|
## Helper Functions |
|
109
|
|
|
|
|
|
|
## |
|
110
|
|
|
|
|
|
|
## |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub dottags($;$) |
|
113
|
|
|
|
|
|
|
{ |
|
114
|
27
|
|
|
27
|
0
|
47
|
my $tag = shift; |
|
115
|
27
|
|
100
|
|
|
66
|
my $type = shift // ''; |
|
116
|
27
|
|
|
|
|
111
|
my @dots = $tag =~ /(.*?)\.(.*)/; |
|
117
|
27
|
|
|
|
|
144
|
my @tags = ( |
|
118
|
|
|
|
|
|
|
{ pre => '', type => '#', txt => $dots[0] }, |
|
119
|
|
|
|
|
|
|
{ pre => '', type => $type, txt => $dots[1] }, |
|
120
|
|
|
|
|
|
|
{ pre => '', type => '/', txt => $dots[0] }, |
|
121
|
|
|
|
|
|
|
); |
|
122
|
27
|
|
|
|
|
73
|
return @tags; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# Generate a regular expression for iteration |
|
126
|
|
|
|
|
|
|
# Passed the open and close tags |
|
127
|
|
|
|
|
|
|
# Returns the regular expression |
|
128
|
|
|
|
|
|
|
sub tag_match(@) |
|
129
|
|
|
|
|
|
|
{ |
|
130
|
135
|
|
|
135
|
0
|
218
|
my ($open, $close) = @_; |
|
131
|
|
|
|
|
|
|
# Much of this regular expression stolen from Template::Mustache |
|
132
|
135
|
|
|
|
|
2055
|
qr/ |
|
133
|
|
|
|
|
|
|
(? .*?) # Text up to opening tag |
|
134
|
|
|
|
|
|
|
(? ^ \s*)? # Indent white space |
|
135
|
|
|
|
|
|
|
(?: \Q$open\E \s*) # Start of tag |
|
136
|
|
|
|
|
|
|
(?: |
|
137
|
|
|
|
|
|
|
(? =) \s* (?.+?) \s* = | # Change delimiters |
|
138
|
|
|
|
|
|
|
(? {) \s* (?.+?) \s* } | # Unescaped |
|
139
|
|
|
|
|
|
|
(? &) \s* (?.+?) | # Unescaped |
|
140
|
|
|
|
|
|
|
(? [#^>\/!]?) \s* (?.+?) # Normal tags |
|
141
|
|
|
|
|
|
|
) |
|
142
|
|
|
|
|
|
|
(?: \s* \Q$close\E) # End of tag |
|
143
|
|
|
|
|
|
|
/xsm; |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# Escape HTML entities |
|
147
|
|
|
|
|
|
|
# Passed a string |
|
148
|
|
|
|
|
|
|
# Returns an escaped string |
|
149
|
|
|
|
|
|
|
sub escape($) |
|
150
|
|
|
|
|
|
|
{ |
|
151
|
114
|
|
|
114
|
0
|
155
|
local $_ = shift; |
|
152
|
114
|
|
|
|
|
181
|
s/&/&/g; |
|
153
|
114
|
|
|
|
|
145
|
s/"/"/g; |
|
154
|
114
|
|
|
|
|
128
|
s/</g; |
|
155
|
114
|
|
|
|
|
128
|
s/>/>/g; |
|
156
|
114
|
|
|
|
|
342
|
return $_; |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# Reassemble the source code for an array of tags |
|
160
|
|
|
|
|
|
|
# Passed an array of tags |
|
161
|
|
|
|
|
|
|
# Returns the original source (roughly) |
|
162
|
|
|
|
|
|
|
sub reassemble(@) |
|
163
|
|
|
|
|
|
|
{ |
|
164
|
5
|
|
|
5
|
0
|
8
|
my @tags = @_; |
|
165
|
5
|
|
|
|
|
9
|
my $last = pop @tags; |
|
166
|
5
|
|
|
|
|
6
|
my $ans = ''; |
|
167
|
5
|
|
|
|
|
7
|
local $_; |
|
168
|
3
|
|
|
3
|
|
17
|
no warnings 'uninitialized'; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
725
|
|
|
169
|
5
|
|
|
|
|
13
|
$ans .= "$_->{pre}$_->{tab}\{\{$_->{type}$_->{txt}\}\}" foreach (@tags); |
|
170
|
5
|
|
|
|
|
21
|
return $ans . $last->{pre}; |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
############################################################# |
|
174
|
|
|
|
|
|
|
## |
|
175
|
|
|
|
|
|
|
## Class Functions |
|
176
|
|
|
|
|
|
|
## |
|
177
|
|
|
|
|
|
|
## |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head1 METHODS |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=head2 Creating a new Mustache::Simple object |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=over |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item new |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
my $tache = new Mustache::Simple(%options) |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=back |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=head3 Parameters: |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=over |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=item path |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
The path from which to load templates and partials. This may be |
|
198
|
|
|
|
|
|
|
a string or a reference to an array of strings. If it is a reference, |
|
199
|
|
|
|
|
|
|
each string will be searched in order. |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
Default: '.' |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=item extension |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
The extension to add to filenames when reading them off disk. The |
|
206
|
|
|
|
|
|
|
'.' should not be included as this will be added automatically. |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Default: 'mustache' |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=item throw |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
If set to a true value, Mustache::Simple will croak when there |
|
213
|
|
|
|
|
|
|
is no key in the context hash for a given tag. |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Default: undef |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=item partial |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
This may be set to a subroutine to be called to generate the |
|
220
|
|
|
|
|
|
|
filename or the template for a partial. If it is not set, partials |
|
221
|
|
|
|
|
|
|
will be loaded using the same parameters as render(). |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
Default: undef |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=back |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=cut |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub new |
|
230
|
|
|
|
|
|
|
{ |
|
231
|
98
|
|
|
98
|
1
|
60651
|
my $class = shift; |
|
232
|
98
|
50
|
|
|
|
369
|
my %options = @_ == 1 ? %{$_[0]} : @_; # Allow a hash to be passed, in case |
|
|
0
|
|
|
|
|
0
|
|
|
233
|
98
|
|
|
|
|
355
|
my %defaults = ( |
|
234
|
|
|
|
|
|
|
path => '.', |
|
235
|
|
|
|
|
|
|
extension => 'mustache', |
|
236
|
|
|
|
|
|
|
delimiters => [qw({{ }})], |
|
237
|
|
|
|
|
|
|
stack => new Mustache::Simple::ContextStack, |
|
238
|
|
|
|
|
|
|
); |
|
239
|
98
|
|
|
|
|
450
|
%options = (%defaults, %options); |
|
240
|
98
|
|
|
|
|
188
|
my $self = \%options; |
|
241
|
98
|
|
|
|
|
289
|
bless $self, $class; |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
############################################################# |
|
245
|
|
|
|
|
|
|
## |
|
246
|
|
|
|
|
|
|
## Private Instance Functions |
|
247
|
|
|
|
|
|
|
## |
|
248
|
|
|
|
|
|
|
## |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# Breaks the template into separate tags, preserving the text |
|
251
|
|
|
|
|
|
|
# Returns an array ref of the tags and the trailing text |
|
252
|
|
|
|
|
|
|
sub match_template |
|
253
|
|
|
|
|
|
|
{ |
|
254
|
122
|
|
|
122
|
0
|
152
|
my $self = shift; |
|
255
|
122
|
|
|
|
|
137
|
my $template = shift; |
|
256
|
122
|
|
|
|
|
141
|
my $match = tag_match(@{$self->{delimiters}}); # start with standard delimiters |
|
|
122
|
|
|
|
|
265
|
|
|
257
|
122
|
|
|
|
|
232
|
my @tags; |
|
258
|
|
|
|
|
|
|
my $afters; |
|
259
|
122
|
|
|
|
|
1001
|
while ($template =~ /$match/g) |
|
260
|
|
|
|
|
|
|
{ |
|
261
|
3
|
|
|
3
|
|
719
|
my %tag = %+; # pick up named parts from the regex |
|
|
3
|
|
|
|
|
956
|
|
|
|
3
|
|
|
|
|
5044
|
|
|
|
263
|
|
|
|
|
2625
|
|
|
262
|
263
|
100
|
|
|
|
842
|
if ($tag{type} eq '=') # change delimiters |
|
263
|
|
|
|
|
|
|
{ |
|
264
|
13
|
|
|
|
|
78
|
my @delimiters = split /\s/, $tag{txt}; |
|
265
|
13
|
|
|
|
|
31
|
$self->{delimiters} = \@delimiters; |
|
266
|
13
|
|
|
|
|
24
|
$match = tag_match(@delimiters); |
|
267
|
|
|
|
|
|
|
} |
|
268
|
263
|
|
|
|
|
449
|
$afters = $'; # save off the rest in case it's done |
|
269
|
263
|
|
|
|
|
1496
|
push @tags, \%tag; # put the tag into the array |
|
270
|
|
|
|
|
|
|
} |
|
271
|
122
|
100
|
|
|
|
285
|
return \@tags, $template if (@tags == 0); # no tags, it's all afters |
|
272
|
107
|
|
|
|
|
261
|
for (1 .. $#tags) |
|
273
|
|
|
|
|
|
|
{ # lose a leading LF after sections |
|
274
|
156
|
100
|
|
|
|
492
|
$tags[$_]->{pre} =~ s/^\r?\n// if $tags[$_ - 1]->{type} =~ m{^[#/^]$}; |
|
275
|
|
|
|
|
|
|
} |
|
276
|
107
|
100
|
|
|
|
206
|
if (@tags > 1) |
|
277
|
|
|
|
|
|
|
{ |
|
278
|
57
|
100
|
100
|
|
|
152
|
$tags[1]->{pre} =~ s/^\r?\n// if $tags[0]->{type} eq '=' and $tags[0]->{pre} =~ /^\s*$/; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
107
|
|
|
|
|
182
|
foreach(0 .. $#tags) |
|
281
|
|
|
|
|
|
|
{ |
|
282
|
263
|
100
|
|
|
|
469
|
$tags[$_]->{pre} =~ s/^\r?\n// if $tags[$_]->{type} =~ m{^[!]$}; |
|
283
|
263
|
100
|
|
|
|
501
|
$tags[$_]->{pre} =~ s/\r?\n$// if $tags[$_]->{type} =~ m{^[=]$}; |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
# and from the trailing text |
|
286
|
107
|
100
|
|
|
|
297
|
$afters =~ s/^\r?\n// if $tags[$#tags]->{type} =~ m{^[/!]$}; |
|
287
|
107
|
|
|
|
|
407
|
return \@tags, $afters; |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# Performs partial includes |
|
291
|
|
|
|
|
|
|
# Passed the current context, it calls the user code if any |
|
292
|
|
|
|
|
|
|
# Returns the partial rendered in the current context |
|
293
|
|
|
|
|
|
|
sub include_partial |
|
294
|
|
|
|
|
|
|
{ |
|
295
|
11
|
|
|
11
|
0
|
17
|
my $self = shift; |
|
296
|
11
|
|
|
|
|
16
|
my $tag = shift; |
|
297
|
11
|
|
|
|
|
13
|
my $result; |
|
298
|
11
|
50
|
|
|
|
57
|
$tag = $self->partial->($tag) if (ref $self->partial eq 'CODE'); |
|
299
|
11
|
|
|
|
|
65
|
$self->render($tag); |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# This is the main worker function. It builds up the result from the tags. |
|
303
|
|
|
|
|
|
|
# Passed the current context and the array of tags |
|
304
|
|
|
|
|
|
|
# Returns the final text |
|
305
|
|
|
|
|
|
|
# Note, this is called recursively, directly for sections and |
|
306
|
|
|
|
|
|
|
# indirectly via render() for partials |
|
307
|
|
|
|
|
|
|
sub resolve |
|
308
|
|
|
|
|
|
|
{ |
|
309
|
222
|
|
|
222
|
0
|
312
|
my $self = shift; |
|
310
|
222
|
|
100
|
|
|
462
|
my $context = shift // {}; |
|
311
|
222
|
|
|
|
|
462
|
$self->push($context); |
|
312
|
222
|
|
|
|
|
370
|
my @tags = @_; |
|
313
|
222
|
|
|
|
|
295
|
my $result = ''; |
|
314
|
222
|
|
|
|
|
436
|
for (my $i = 0; $i < @tags; $i++) |
|
315
|
|
|
|
|
|
|
{ |
|
316
|
327
|
|
|
|
|
436
|
my $tag = $tags[$i]; # the current tag |
|
317
|
327
|
|
|
|
|
480
|
$result .= $tag->{pre}; # add in the intervening text |
|
318
|
327
|
|
|
|
|
412
|
given ($tag->{type}) |
|
319
|
|
|
|
|
|
|
{ |
|
320
|
327
|
|
|
|
|
524
|
when('!') { # it's a comment |
|
321
|
|
|
|
|
|
|
# $result .= $tag->{tab} if $tag->{tab}; |
|
322
|
|
|
|
|
|
|
} |
|
323
|
320
|
|
|
|
|
451
|
when('/') { break; } # it's a section end - skip |
|
|
73
|
|
|
|
|
148
|
|
|
324
|
247
|
|
|
|
|
306
|
when('=') { break; } # delimiter change |
|
|
13
|
|
|
|
|
29
|
|
|
325
|
234
|
|
|
|
|
603
|
when(/^([{&])?$/) { # it's a variable |
|
326
|
137
|
|
|
|
|
169
|
my $txt; |
|
327
|
137
|
100
|
|
|
|
343
|
if ($tag->{txt} eq '.') |
|
|
|
100
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
{ |
|
329
|
10
|
|
|
|
|
22
|
$txt = $self->{stack}->top; |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
elsif ($tag->{txt} =~ /\./) |
|
332
|
|
|
|
|
|
|
{ |
|
333
|
21
|
|
|
|
|
55
|
my @dots = dottags $tag->{txt}, $tag->{type}; |
|
334
|
21
|
|
|
|
|
85
|
$txt = $self->resolve(undef, @dots); |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
else { |
|
337
|
106
|
|
|
|
|
192
|
$txt = $self->find($tag->{txt}); # get the entry from the context |
|
338
|
106
|
100
|
|
|
|
202
|
if (defined $txt) |
|
339
|
|
|
|
|
|
|
{ |
|
340
|
103
|
100
|
|
|
|
179
|
if (ref $txt eq 'CODE') |
|
341
|
|
|
|
|
|
|
{ |
|
342
|
8
|
|
|
|
|
13
|
my $saved = $self->{delimiters}; |
|
343
|
8
|
|
|
|
|
15
|
$self->{delimiters} = [qw({{ }})]; |
|
344
|
8
|
|
|
|
|
17
|
$txt = $self->render($txt->()); |
|
345
|
8
|
|
|
|
|
17
|
$self->{delimiters} = $saved; |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
else { |
|
349
|
3
|
50
|
|
|
|
23
|
croak qq(No context for "$tag->{txt}") if $self->throw; |
|
350
|
3
|
|
|
|
|
7
|
$txt = ''; |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
} |
|
353
|
137
|
100
|
|
|
|
231
|
$txt = "$tag->{tab}$txt" if $tag->{tab}; # replace the indent |
|
354
|
137
|
100
|
|
|
|
287
|
$result .= $tag->{type} ? $txt : escape $txt; |
|
355
|
|
|
|
|
|
|
} |
|
356
|
97
|
|
|
|
|
164
|
when('#') { # it's a section start |
|
357
|
66
|
|
|
|
|
91
|
my $j; |
|
358
|
66
|
|
|
|
|
79
|
my $nested = 0; |
|
359
|
66
|
|
|
|
|
139
|
for ($j = $i + 1; $j < @tags; $j++) # find the end |
|
360
|
|
|
|
|
|
|
{ |
|
361
|
276
|
100
|
|
|
|
516
|
if ($tag->{txt} eq $tags[$j]->{txt}) |
|
362
|
|
|
|
|
|
|
{ |
|
363
|
70
|
100
|
|
|
|
129
|
$nested++, next if $tags[$j]->{type} eq '#'; # nested sections with the |
|
364
|
68
|
50
|
|
|
|
109
|
if ($tags[$j]->{type} eq '/') # same name |
|
365
|
|
|
|
|
|
|
{ |
|
366
|
68
|
100
|
|
|
|
108
|
next if $nested--; |
|
367
|
66
|
|
|
|
|
90
|
last; |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
} |
|
371
|
66
|
50
|
|
|
|
120
|
croak 'No end tag found for {{#'.$tag->{txt}.'}}' if $j == @tags; |
|
372
|
66
|
|
|
|
|
169
|
my @subtags = @tags[$i + 1 .. $j]; # get the tags for the section |
|
373
|
66
|
|
|
|
|
84
|
my $txt; |
|
374
|
66
|
100
|
|
|
|
139
|
if ($tag->{txt} =~ /\./) |
|
375
|
|
|
|
|
|
|
{ |
|
376
|
3
|
|
|
|
|
10
|
my @dots = dottags($tag->{txt}); |
|
377
|
3
|
|
|
|
|
11
|
$txt = $self->resolve(undef, @dots); |
|
378
|
|
|
|
|
|
|
} |
|
379
|
|
|
|
|
|
|
else { |
|
380
|
|
|
|
|
|
|
# wantarray!!! |
|
381
|
63
|
|
|
|
|
117
|
my @ret = $self->find($tag->{txt}); # get the entry from the context |
|
382
|
63
|
50
|
|
|
|
150
|
if (scalar @ret == 0) { |
|
|
|
50
|
|
|
|
|
|
|
383
|
0
|
|
|
|
|
0
|
$txt = undef; |
|
384
|
|
|
|
|
|
|
} elsif (scalar @ret == 1) { |
|
385
|
63
|
|
|
|
|
117
|
$txt = $ret[0]; |
|
386
|
|
|
|
|
|
|
} else { |
|
387
|
0
|
|
|
|
|
0
|
$txt = \@ret; |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
} |
|
390
|
66
|
|
|
|
|
122
|
given (reftype $txt) |
|
391
|
|
|
|
|
|
|
{ |
|
392
|
66
|
|
|
|
|
119
|
when ('ARRAY') { # an array of hashes (hopefully) |
|
393
|
6
|
|
|
|
|
22
|
$result .= $self->resolve($_, @subtags) foreach @$txt; |
|
394
|
|
|
|
|
|
|
} |
|
395
|
60
|
|
|
|
|
82
|
when ('CODE') { # call user code which may call render() |
|
396
|
5
|
|
|
|
|
11
|
$result .= $self->render($txt->(reassemble @subtags)); |
|
397
|
|
|
|
|
|
|
} |
|
398
|
55
|
|
|
|
|
75
|
when ('HASH') { # use the hash as context |
|
399
|
38
|
100
|
|
|
|
88
|
break unless scalar %$txt; |
|
400
|
33
|
|
|
|
|
101
|
$result .= $self->resolve($txt, @subtags); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
17
|
|
|
|
|
20
|
default { # resolve the tags in current context |
|
403
|
17
|
100
|
|
|
|
54
|
$result .= $self->resolve(undef, @subtags) if $txt; |
|
404
|
|
|
|
|
|
|
} |
|
405
|
|
|
|
|
|
|
} |
|
406
|
66
|
|
|
|
|
196
|
$i = $j; |
|
407
|
|
|
|
|
|
|
} |
|
408
|
31
|
|
|
|
|
45
|
when ('^') { # inverse section |
|
409
|
20
|
|
|
|
|
25
|
my $j; |
|
410
|
20
|
|
|
|
|
22
|
my $nested = 0; |
|
411
|
20
|
|
|
|
|
46
|
for ($j = $i + 1; $j < @tags; $j++) |
|
412
|
|
|
|
|
|
|
{ |
|
413
|
29
|
100
|
|
|
|
63
|
if ($tag->{txt} eq $tags[$j]->{txt}) |
|
414
|
|
|
|
|
|
|
{ |
|
415
|
24
|
100
|
|
|
|
49
|
$nested++, next if $tags[$j]->{type} eq '^'; # nested sections with the |
|
416
|
22
|
50
|
|
|
|
36
|
if ($tags[$j]->{type} eq '/') # same name |
|
417
|
|
|
|
|
|
|
{ |
|
418
|
22
|
100
|
|
|
|
53
|
next if $nested--; |
|
419
|
20
|
|
|
|
|
41
|
last; |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
} |
|
422
|
|
|
|
|
|
|
} |
|
423
|
20
|
50
|
|
|
|
39
|
croak 'No end tag found for {{#'.$tag->{txt}.'}}' if $j == @tags; |
|
424
|
20
|
|
|
|
|
52
|
my @subtags = @tags[$i + 1 .. $j]; |
|
425
|
20
|
|
|
|
|
29
|
my $txt; |
|
426
|
20
|
100
|
|
|
|
48
|
if ($tag->{txt} =~ /\./) |
|
427
|
|
|
|
|
|
|
{ |
|
428
|
3
|
|
|
|
|
8
|
my @dots = dottags($tag->{txt}); |
|
429
|
3
|
|
|
|
|
10
|
$txt = $self->resolve(undef, @dots); |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
else { |
|
432
|
17
|
|
|
|
|
31
|
$txt = $self->find($tag->{txt}); # get the entry from the context |
|
433
|
|
|
|
|
|
|
} |
|
434
|
20
|
|
|
|
|
33
|
my $ans = ''; |
|
435
|
20
|
|
|
|
|
35
|
given (reftype $txt) |
|
436
|
|
|
|
|
|
|
{ |
|
437
|
20
|
|
|
|
|
30
|
when ('ARRAY') { |
|
438
|
2
|
100
|
|
|
|
9
|
$ans = $self->resolve(undef, @subtags) if @$txt == 0; |
|
439
|
|
|
|
|
|
|
} |
|
440
|
18
|
|
|
|
|
26
|
when ('HASH') { |
|
441
|
1
|
50
|
|
|
|
6
|
$ans = $self->resolve(undef, @subtags) if keys %$txt == 0; |
|
442
|
|
|
|
|
|
|
} |
|
443
|
17
|
|
|
|
|
22
|
when ('CODE') { |
|
444
|
|
|
|
|
|
|
# $ans = $self->resolve(undef, @subtags) unless &$txt; |
|
445
|
|
|
|
|
|
|
# The above line is rem'd out to comply with the test: |
|
446
|
|
|
|
|
|
|
# 'Lambdas used for inverted sections should be considered truthy.' |
|
447
|
|
|
|
|
|
|
# although I'm not sure I agree with it. |
|
448
|
|
|
|
|
|
|
} |
|
449
|
16
|
|
|
|
|
20
|
default { |
|
450
|
16
|
100
|
|
|
|
47
|
$ans = $self->resolve(undef, @subtags) unless $txt; |
|
451
|
|
|
|
|
|
|
} |
|
452
|
|
|
|
|
|
|
} |
|
453
|
20
|
50
|
|
|
|
41
|
$ans = "$tag->{tab}$ans" if $tag->{tab}; # replace the indent |
|
454
|
20
|
|
|
|
|
32
|
$result .= $ans; |
|
455
|
20
|
|
|
|
|
47
|
$i = $j; |
|
456
|
|
|
|
|
|
|
} |
|
457
|
11
|
|
|
|
|
20
|
when ('>') { # partial - see include_partial() |
|
458
|
11
|
|
|
|
|
18
|
my $saved = $self->{delimiters}; |
|
459
|
11
|
|
|
|
|
27
|
$self->{delimiters} = [qw({{ }})]; |
|
460
|
11
|
|
|
|
|
28
|
$result .= $self->include_partial($tag->{txt}); |
|
461
|
11
|
|
|
|
|
35
|
$self->{delimiters} = $saved; |
|
462
|
|
|
|
|
|
|
} |
|
463
|
0
|
|
|
|
|
0
|
default { # allow for future expansion |
|
464
|
0
|
|
|
|
|
0
|
croak "Unknown tag type in \{\{$_$tag->{txt}}}"; |
|
465
|
|
|
|
|
|
|
} |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
} |
|
468
|
222
|
|
|
|
|
484
|
$self->pop; |
|
469
|
222
|
|
|
|
|
537
|
return $result; |
|
470
|
|
|
|
|
|
|
} |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# Push something a context onto the stack |
|
473
|
|
|
|
|
|
|
sub push |
|
474
|
|
|
|
|
|
|
{ |
|
475
|
222
|
|
|
222
|
0
|
288
|
my $self = shift; |
|
476
|
222
|
|
|
|
|
276
|
my $value = shift; |
|
477
|
222
|
|
|
|
|
517
|
$self->{stack}->push($value); |
|
478
|
|
|
|
|
|
|
} |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# Pop the context back off the stack |
|
481
|
|
|
|
|
|
|
sub pop |
|
482
|
|
|
|
|
|
|
{ |
|
483
|
222
|
|
|
222
|
0
|
265
|
my $self = shift; |
|
484
|
222
|
|
|
|
|
407
|
my $value = $self->{stack}->pop; |
|
485
|
222
|
|
|
|
|
278
|
return $value; |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# Find a value on the stack |
|
489
|
|
|
|
|
|
|
sub find |
|
490
|
|
|
|
|
|
|
{ |
|
491
|
186
|
|
|
186
|
0
|
232
|
my $self = shift; |
|
492
|
186
|
|
|
|
|
237
|
my $value = shift; |
|
493
|
186
|
|
|
|
|
408
|
return $self->{stack}->search($value); |
|
494
|
|
|
|
|
|
|
} |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
# Given a path and a filename |
|
497
|
|
|
|
|
|
|
# returns the first match that exists |
|
498
|
|
|
|
|
|
|
sub getfile($$); |
|
499
|
|
|
|
|
|
|
sub getfile($$) |
|
500
|
|
|
|
|
|
|
{ |
|
501
|
14
|
|
|
14
|
0
|
31
|
my ($path, $filename) = @_; |
|
502
|
14
|
|
|
|
|
25
|
$filename =~ s/\r?\n$//; # not chomp $filename because of the possibility of \r\n |
|
503
|
14
|
100
|
|
|
|
37
|
return if $filename =~ /\r?\n/; |
|
504
|
13
|
|
|
|
|
19
|
my $fullfile; |
|
505
|
13
|
50
|
33
|
|
|
37
|
if (ref $path && ref $path eq 'ARRAY') |
|
506
|
|
|
|
|
|
|
{ |
|
507
|
0
|
|
|
|
|
0
|
foreach (@$path) |
|
508
|
|
|
|
|
|
|
{ |
|
509
|
0
|
|
|
|
|
0
|
$fullfile = getfile $_, $filename; |
|
510
|
0
|
0
|
|
|
|
0
|
last if $fullfile; |
|
511
|
|
|
|
|
|
|
} |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
else { |
|
514
|
13
|
|
|
|
|
119
|
$fullfile = File::Spec->catfile($path, $filename); |
|
515
|
13
|
50
|
|
|
|
232
|
undef $fullfile unless -e $fullfile; |
|
516
|
|
|
|
|
|
|
} |
|
517
|
13
|
|
|
|
|
35
|
return $fullfile; |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
############################################################# |
|
522
|
|
|
|
|
|
|
## |
|
523
|
|
|
|
|
|
|
## Public Instance Functions |
|
524
|
|
|
|
|
|
|
## |
|
525
|
|
|
|
|
|
|
## |
|
526
|
|
|
|
|
|
|
|
|
527
|
3
|
|
|
3
|
|
22
|
use constant functions => qw(path extension throw partial); |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
1356
|
|
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=head2 Configuration Methods |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
The configuration methods match the %options array thay may be passed |
|
532
|
|
|
|
|
|
|
to new(). |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
Each option may be called with a non-false value to set the option |
|
535
|
|
|
|
|
|
|
and will return the new value. If called without a value, it will return |
|
536
|
|
|
|
|
|
|
the current value. |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=over |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=item path() |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
$tache->path('/some/new/template/path'); |
|
543
|
|
|
|
|
|
|
or |
|
544
|
|
|
|
|
|
|
$tache->path([ qw{/some/new/template/path .} ]); |
|
545
|
|
|
|
|
|
|
my $path = $tache->path; # defaults to '.' |
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=item extension() |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
$tache->extension('html'); |
|
550
|
|
|
|
|
|
|
my $extension = $tache->extension; # defaults to 'mustache' |
|
551
|
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
=item throw() |
|
553
|
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
$tache->throw(1); |
|
555
|
|
|
|
|
|
|
my $throwing = $tache->throw; # defaults to undef |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=item partial() |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
$tache->partial(\&resolve_partials) |
|
560
|
|
|
|
|
|
|
my $partial = $tache->partial # defaults to undef |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=back |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=cut |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
sub AUTOLOAD |
|
567
|
|
|
|
|
|
|
{ |
|
568
|
53
|
|
|
53
|
|
74
|
my $self = shift; |
|
569
|
53
|
|
|
|
|
79
|
my $class = ref $self; |
|
570
|
53
|
|
|
|
|
63
|
my $value = shift; |
|
571
|
53
|
|
|
|
|
197
|
(my $name = our $AUTOLOAD) =~ s/.*:://; |
|
572
|
53
|
|
|
|
|
113
|
my %ok = map { ($_, 1) } functions; |
|
|
212
|
|
|
|
|
388
|
|
|
573
|
53
|
50
|
|
|
|
122
|
croak "Unknown function $class->$name()" unless $ok{$name}; |
|
574
|
53
|
50
|
|
|
|
80
|
$self->{$name} = $value if $value; |
|
575
|
53
|
|
|
|
|
160
|
return $self->{$name}; |
|
576
|
|
|
|
|
|
|
} |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
# Prevent it being caught by AUTOLOAD |
|
579
|
|
|
|
|
|
|
sub DESTROY |
|
580
|
|
|
|
0
|
|
|
{ |
|
581
|
|
|
|
|
|
|
} |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
=head2 Instance methods |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=over |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
=item read_file() |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
my $template = read_file('templatefile'); |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
You will not usually need to call this directly as it's called by |
|
592
|
|
|
|
|
|
|
L to load the file. If it is passed a string that looks like |
|
593
|
|
|
|
|
|
|
a template (i.e. has {{ in it) it simply returns it. Similarly, if, |
|
594
|
|
|
|
|
|
|
after prepending the path and adding the suffix, it cannot load the file, |
|
595
|
|
|
|
|
|
|
it simply returns the original string. |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
=back |
|
598
|
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=cut |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
sub read_file($) |
|
602
|
|
|
|
|
|
|
{ |
|
603
|
122
|
|
|
122
|
1
|
155
|
my $self = shift; |
|
604
|
122
|
|
|
|
|
153
|
my $file = shift; |
|
605
|
122
|
100
|
|
|
|
208
|
return '' unless $file; |
|
606
|
121
|
100
|
|
|
|
412
|
return $file if $file =~ /\{\{/; |
|
607
|
14
|
|
|
|
|
59
|
my $extension = $self->extension; |
|
608
|
14
|
|
|
|
|
122
|
(my $fullfile = $file) =~ s/(\.$extension)?$/.$extension/; |
|
609
|
14
|
|
|
|
|
58
|
my $filepath = getfile $self->path, $fullfile; |
|
610
|
14
|
50
|
|
|
|
45
|
return $file unless $filepath; |
|
611
|
0
|
|
|
|
|
0
|
local $/; |
|
612
|
0
|
0
|
|
|
|
0
|
open my $hand, "<:utf8", $filepath or croak "Can't open $filepath: $!"; |
|
613
|
0
|
|
|
|
|
0
|
<$hand>; |
|
614
|
|
|
|
|
|
|
} |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=over |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=item render() |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
my $context = { |
|
621
|
|
|
|
|
|
|
"name" => "Chris", |
|
622
|
|
|
|
|
|
|
"value" => 10000, |
|
623
|
|
|
|
|
|
|
"taxed_value" => 10000 - (10000 * 0.4), |
|
624
|
|
|
|
|
|
|
"in_ca" => true |
|
625
|
|
|
|
|
|
|
} |
|
626
|
|
|
|
|
|
|
my $html = $tache->render('templatefile', $context); |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
This is the main entry-point for rendering templates. It can be passed |
|
629
|
|
|
|
|
|
|
either a full template or path to a template file. See L |
|
630
|
|
|
|
|
|
|
for details of how the file is loaded. It must also be passed a hashref |
|
631
|
|
|
|
|
|
|
containing the main context. |
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
In callbacks (sections like C< {{#this}} > with a subroutine in the context), |
|
634
|
|
|
|
|
|
|
you may call render on the passed string and the current context will be |
|
635
|
|
|
|
|
|
|
remembered. For example: |
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
{ |
|
638
|
|
|
|
|
|
|
name => "Willy", |
|
639
|
|
|
|
|
|
|
wrapped => sub { |
|
640
|
|
|
|
|
|
|
my $text = shift; |
|
641
|
|
|
|
|
|
|
chomp $text; |
|
642
|
|
|
|
|
|
|
return "" . $tache->render($text) . "\n"; |
|
643
|
|
|
|
|
|
|
} |
|
644
|
|
|
|
|
|
|
} |
|
645
|
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
Alternatively, you may pass in an entirely new context when calling |
|
647
|
|
|
|
|
|
|
render() from a callback. |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=back |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=cut |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
sub render |
|
654
|
|
|
|
|
|
|
{ |
|
655
|
122
|
|
|
122
|
1
|
1764
|
my $self = shift; |
|
656
|
122
|
|
|
|
|
241
|
my ($template, $context) = @_; |
|
657
|
122
|
100
|
|
|
|
269
|
$context = {} unless $context; |
|
658
|
|
|
|
|
|
|
# say "\$template = $template, ref \$context = ", ref $context; |
|
659
|
|
|
|
|
|
|
# print Dumper $context; |
|
660
|
122
|
|
|
|
|
236
|
$template = $self->read_file($template); |
|
661
|
122
|
|
|
|
|
269
|
my ($tags, $tail) = $self->match_template($template); |
|
662
|
|
|
|
|
|
|
# print reassemble(@$tags), $tail; exit; |
|
663
|
122
|
|
|
|
|
307
|
my $result = $self->resolve($context, @$tags) . $tail; |
|
664
|
122
|
|
|
|
|
421
|
return $result; |
|
665
|
|
|
|
|
|
|
} |
|
666
|
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=head1 COMPLIANCE WITH THE STANDARD |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
The original standard for Mustache was defined at the |
|
670
|
|
|
|
|
|
|
L |
|
671
|
|
|
|
|
|
|
and this version of L was designed to comply |
|
672
|
|
|
|
|
|
|
with just that. Since then, the standard for Mustache seems to be |
|
673
|
|
|
|
|
|
|
defined by the L. |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
The test suite on this version skips a number of tests |
|
676
|
|
|
|
|
|
|
in the Spec, all of which relate to Decimals or White Space. |
|
677
|
|
|
|
|
|
|
It passes all the other tests. The YAML from the Spec is built |
|
678
|
|
|
|
|
|
|
into the test suite. |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
=head1 MANAGING OBJECTS |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
If a blessed object is passed in (at any level) as the context for |
|
683
|
|
|
|
|
|
|
rendering a template, L will check each tag to |
|
684
|
|
|
|
|
|
|
see if it can be called as a method on the object. To achieve this, it |
|
685
|
|
|
|
|
|
|
calls C from L |
|
686
|
|
|
|
|
|
|
on the object. If C<< $object->can(tag) >> |
|
687
|
|
|
|
|
|
|
returns code, this code will be called (with no parameters). Otherwise, |
|
688
|
|
|
|
|
|
|
if the object is based on an underlying HASH, it will be treated as that |
|
689
|
|
|
|
|
|
|
HASH. This works well for objects with AUTOLOADed "getters". |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
For example: |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
package Test::Mustache; |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
sub new |
|
696
|
|
|
|
|
|
|
{ |
|
697
|
|
|
|
|
|
|
my $class = shift; |
|
698
|
|
|
|
|
|
|
my %params = @_; |
|
699
|
|
|
|
|
|
|
bless \%params, $class; |
|
700
|
|
|
|
|
|
|
} |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
sub name # Ensure the name starts with a capital |
|
703
|
|
|
|
|
|
|
{ |
|
704
|
|
|
|
|
|
|
my $self = shift; |
|
705
|
|
|
|
|
|
|
(my $name = $self->{name}) =~ s/.*/\L\u$&/; |
|
706
|
|
|
|
|
|
|
return $name; |
|
707
|
|
|
|
|
|
|
} |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
sub AUTOLOAD # generic getter / setter |
|
710
|
|
|
|
|
|
|
{ |
|
711
|
|
|
|
|
|
|
my $self = shift; |
|
712
|
|
|
|
|
|
|
my $value = shift; |
|
713
|
|
|
|
|
|
|
(my $method = our $AUTOLOAD) =~ s/.*:://; |
|
714
|
|
|
|
|
|
|
$self->{$method} = $value if defined $value; |
|
715
|
|
|
|
|
|
|
return $self->{$method}; |
|
716
|
|
|
|
|
|
|
} |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
sub DESTROY { } |
|
719
|
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
Using the above object as C<$object>, C<{{name}}> would call |
|
721
|
|
|
|
|
|
|
C<< $object->can('name') >> which would return a reference to |
|
722
|
|
|
|
|
|
|
the C method and thus that method would be called as a |
|
723
|
|
|
|
|
|
|
"getter". On a call to C<{{address}}>, C<< $object->can >> would |
|
724
|
|
|
|
|
|
|
return undef and therefore C<< $object->{address} >> would be |
|
725
|
|
|
|
|
|
|
used. |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
This is usually what you want as it avoids the call to C<< $object->AUTOLOAD >> |
|
728
|
|
|
|
|
|
|
for each simple lookup. If, however, you want something different to |
|
729
|
|
|
|
|
|
|
happen, you either need to declare a "Forward Declaration" |
|
730
|
|
|
|
|
|
|
(see L) |
|
731
|
|
|
|
|
|
|
or you need to override the object's C |
|
732
|
|
|
|
|
|
|
(see L). |
|
733
|
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=head1 BUGS |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=over |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=item White Space |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
Much of the more esoteric white-space handling specified in |
|
741
|
|
|
|
|
|
|
L is not strictly adhered to |
|
742
|
|
|
|
|
|
|
in this version. Most of this will be addressed in a future version. |
|
743
|
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
Because of this, the following tests from the Mustache Spec are skipped: |
|
745
|
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
=over |
|
747
|
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
=item * Indented Inline |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
=item * Indented Inline Sections |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=item * Internal Whitespace |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=item * Standalone Indentation |
|
755
|
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
=item * Standalone Indented Lines |
|
757
|
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
=item * Standalone Line Endings |
|
759
|
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
=item * Standalone Without Newline |
|
761
|
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
=item * Standalone Without Previous Line |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
=back |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
=item Decimal Interpolation |
|
767
|
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
The spec implies that the template C<"{{power}} jiggawatts!"> when passed |
|
769
|
|
|
|
|
|
|
C<{ power: "1.210" }> should return C<"1.21 jiggawatts!">. I believe this to |
|
770
|
|
|
|
|
|
|
be wrong and simply a mistake in the YAML of the relevant tests or possibly |
|
771
|
|
|
|
|
|
|
in L. I am far from being a YAML expert. |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
Clearly C<{ power : 1.210 }> would have the desired effect. |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
Because of this, all tests matching C have been skipped. We can just |
|
776
|
|
|
|
|
|
|
assume that Perl will do the right thing. |
|
777
|
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
=back |
|
779
|
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=head1 EXPORTS |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
Nothing. |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
L - a much more complex module that is |
|
787
|
|
|
|
|
|
|
designed to be subclassed for each template. |
|
788
|
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
=head1 AUTHOR INFORMATION |
|
790
|
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
Cliff Stanford C<< >> |
|
792
|
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
=head1 SOURCE REPOSITORY |
|
794
|
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
The source is maintained at a public Github repository at |
|
796
|
|
|
|
|
|
|
L. Feel free to fork |
|
797
|
|
|
|
|
|
|
it and to help me fix some of the above issues. Please leave any |
|
798
|
|
|
|
|
|
|
bugs or issues on the L |
|
799
|
|
|
|
|
|
|
page and I will be notified. |
|
800
|
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
=head1 LICENCE AND COPYRIGHT |
|
802
|
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
Copyright © 2014, Cliff Stanford C<< >>. All rights reserved. |
|
804
|
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or |
|
806
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
=cut |
|
809
|
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
1; |
|
811
|
|
|
|
|
|
|
|