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