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
|
|
|
|
|
|
|
|