| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
7
|
|
|
7
|
|
424495
|
use 5.020; |
|
|
7
|
|
|
|
|
78
|
|
|
2
|
7
|
|
|
7
|
|
38
|
use warnings; |
|
|
7
|
|
|
|
|
11
|
|
|
|
7
|
|
|
|
|
307
|
|
|
3
|
|
|
|
|
|
|
package Plate 1.4; |
|
4
|
|
|
|
|
|
|
|
|
5
|
7
|
|
|
7
|
|
41
|
use Carp 'croak'; |
|
|
7
|
|
|
|
|
13
|
|
|
|
7
|
|
|
|
|
347
|
|
|
6
|
7
|
|
|
7
|
|
55
|
use File::Spec; |
|
|
7
|
|
|
|
|
11
|
|
|
|
7
|
|
|
|
|
202
|
|
|
7
|
7
|
|
|
7
|
|
46
|
use Scalar::Util; |
|
|
7
|
|
|
|
|
37
|
|
|
|
7
|
|
|
|
|
281
|
|
|
8
|
7
|
|
|
7
|
|
42
|
use XSLoader; |
|
|
7
|
|
|
|
|
10
|
|
|
|
7
|
|
|
|
|
262
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
7
|
|
|
7
|
|
50
|
use constant WINDOWS => $^O eq 'MSWin32'; |
|
|
7
|
|
|
|
|
13
|
|
|
|
7
|
|
|
|
|
758
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
BEGIN { |
|
13
|
7
|
|
|
7
|
|
31889
|
XSLoader::load __PACKAGE__, $Plate::VERSION; |
|
14
|
|
|
|
|
|
|
} |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=encoding UTF-8 |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
Plate - Fast templating engine with support for embedded Perl |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use Plate; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $plate = Plate->new( |
|
27
|
|
|
|
|
|
|
path => '/path/to/plate/files/', |
|
28
|
|
|
|
|
|
|
cache_path => '/tmp/cache/', |
|
29
|
|
|
|
|
|
|
auto_filter => 'trim', |
|
30
|
|
|
|
|
|
|
); |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
$plate->filter(html => \&HTML::Escape::escape_html); |
|
33
|
|
|
|
|
|
|
$plate->filter(trim => sub { $_[0] =~ s/^\s+|\s+$//gr }); |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# Render /path/to/plate/files/hello.plate cached as /tmp/cache/hello.pl |
|
36
|
|
|
|
|
|
|
my $output = $plate->serve('hello'); |
|
37
|
|
|
|
|
|
|
print $output; |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=cut |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
my $re_pre = qr'(.*?)(?: |
|
42
|
|
|
|
|
|
|
^%%\h*(\V*?)\h*(?:\R|\z)| |
|
43
|
|
|
|
|
|
|
^<%%(perl)>(?:\R|\z)(?:(.*?)^%%\g-2>(?:\R|\z))?| |
|
44
|
|
|
|
|
|
|
^<%%def\h+([\w/\.-]+)>(?:\R|\z)| |
|
45
|
|
|
|
|
|
|
<%%(\s*(?:\#.+?)?)%%>| |
|
46
|
|
|
|
|
|
|
<%%\s*(.+?)\s*(?:\|\h*(|\w+(?:\s*\|\h*\w+)*)\s*)?%%>| |
|
47
|
|
|
|
|
|
|
<&&(\|)?\s*(.+?)\s*(?:\|\h*(|\w+(?:\s*\|\h*\w+)*)\s*)?&&>| |
|
48
|
|
|
|
|
|
|
(%%def|%%perl)>(?:\R|\z)| |
|
49
|
|
|
|
|
|
|
(&&)>|\z |
|
50
|
|
|
|
|
|
|
)'mosx; |
|
51
|
|
|
|
|
|
|
my $re_run = qr'(.*?)(?: |
|
52
|
|
|
|
|
|
|
^%\h*(\V*?)\h*(?:\R|\z)| |
|
53
|
|
|
|
|
|
|
^<%(perl)>(?:\R|\z)(?:(.*?)^%\g-2>(?:\R|\z))?| |
|
54
|
|
|
|
|
|
|
^<%def\h+([\w/\.-]+)>(?:\R|\z)| |
|
55
|
|
|
|
|
|
|
<%(\s*(?:\#.+?)?)%>| |
|
56
|
|
|
|
|
|
|
<%\s*(.+?)\s*(?:\|\h*(|\w+(?:\s*\|\h*\w+)*)\s*)?%>| |
|
57
|
|
|
|
|
|
|
<&(\|)?\s*(.+?)\s*(?:\|\h*(|\w+(?:\s*\|\h*\w+)*)\s*)?&>| |
|
58
|
|
|
|
|
|
|
(%def|%perl)>(?:\R|\z)| |
|
59
|
|
|
|
|
|
|
(&)>|\z |
|
60
|
|
|
|
|
|
|
)'mosx; |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub _parse_text { |
|
63
|
318
|
|
|
318
|
|
558
|
my $text = $_[0]; |
|
64
|
318
|
100
|
|
|
|
823
|
$_[2] = $text =~ s/\\\R//g unless $_[1]; |
|
65
|
318
|
|
|
|
|
1099
|
$text =~ s/(\\|')/\\$1/g; |
|
66
|
318
|
100
|
|
|
|
1071
|
length $text ? "'$text'" : (); |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
sub _parse_cmnt { |
|
69
|
75
|
|
|
75
|
|
138
|
my $cmnt = $_[0]; |
|
70
|
75
|
100
|
|
|
|
261
|
$cmnt =~ /^#(?:\s*line\s+(\d+)\s*(?:\s("?)([^"]+)\g2)?\s*|.*)$/ |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
? defined $1 |
|
72
|
|
|
|
|
|
|
? defined $3 |
|
73
|
|
|
|
|
|
|
? "\n#line $1 $3" |
|
74
|
|
|
|
|
|
|
: "\n#line $1" |
|
75
|
|
|
|
|
|
|
: '' |
|
76
|
|
|
|
|
|
|
: $cmnt; |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
sub _parse_defn { |
|
79
|
7
|
|
|
7
|
|
14
|
my $defn = $_[0]; |
|
80
|
7
|
100
|
|
|
|
56
|
$defn =~ /\W/ ? "'".($defn =~ s/(\\|')/\\$1/gr)."'" : $defn; |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
sub _parse_fltr { |
|
83
|
168
|
|
|
168
|
|
272
|
my $expr = $_[0]; |
|
84
|
168
|
100
|
|
|
|
511
|
$expr .= "//''" unless $$Plate::_s{keep_undef}; |
|
85
|
168
|
100
|
|
|
|
428
|
if (length $_[1]) { |
|
|
|
100
|
|
|
|
|
|
|
86
|
77
|
|
|
|
|
294
|
for (split /\s*\|\s*/, $_[1]) { |
|
87
|
81
|
100
|
|
|
|
574
|
exists $$Plate::_s{filters}{$_} or croak "No '$_' filter defined"; |
|
88
|
78
|
|
|
|
|
213
|
$expr = "Plate::_f($_=>$expr)"; |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
} elsif (not $$Plate::_s{keep_undef}) { |
|
91
|
84
|
|
|
|
|
161
|
$expr = "($expr)"; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
165
|
|
|
|
|
351
|
$expr; |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
sub _parse { |
|
96
|
258
|
|
|
258
|
|
420
|
my @expr; |
|
97
|
|
|
|
|
|
|
my $stmt; |
|
98
|
258
|
|
|
|
|
507
|
my $pre = $_[1] == $re_pre; |
|
99
|
258
|
|
|
|
|
373
|
my $fix_line_num; |
|
100
|
|
|
|
|
|
|
my $expr2stmt = sub { |
|
101
|
188
|
100
|
|
188
|
|
321
|
if (@expr) { |
|
102
|
121
|
100
|
|
|
|
202
|
if (defined $stmt) { |
|
103
|
81
|
100
|
|
|
|
149
|
$stmt .= ';push@Plate::_l,length$Plate::_b,__LINE__' if $pre; |
|
104
|
81
|
|
|
|
|
127
|
$stmt .= ';$Plate::_b.='; |
|
105
|
|
|
|
|
|
|
} else { |
|
106
|
40
|
|
|
|
|
61
|
$stmt = 'local$Plate::_b='; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
121
|
100
|
|
|
|
403
|
$stmt .= join('.', $_[0] ? splice @expr, 0, $fix_line_num : splice @expr).';'; |
|
109
|
|
|
|
|
|
|
} else { |
|
110
|
67
|
|
100
|
|
|
197
|
$stmt //= q"local$Plate::_b='';"; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
188
|
100
|
|
|
|
443
|
undef $fix_line_num unless $_[0]; |
|
113
|
258
|
|
|
|
|
987
|
}; |
|
114
|
258
|
|
|
|
|
3397
|
while ($_[0] =~ /$_[1]/g) { |
|
115
|
|
|
|
|
|
|
|
|
116
|
514
|
100
|
|
|
|
1569
|
if (length $1) { |
|
117
|
318
|
|
|
|
|
610
|
push @expr, _parse_text $1, $pre, my $add_lines; |
|
118
|
318
|
100
|
|
|
|
653
|
(@expr ? $expr[-1] : defined $stmt ? $stmt : ($expr[0] = "''")) .= "\n" x $add_lines if $add_lines; |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
119
|
318
|
100
|
|
|
|
646
|
$fix_line_num = @expr if $fix_line_num; |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
|
|
122
|
514
|
100
|
100
|
|
|
1623
|
if (!$pre and @Plate::_l and $Plate::_l[0] <= $+[1]) { |
|
|
|
|
100
|
|
|
|
|
|
123
|
24
|
|
|
|
|
75
|
my($pos, $line) = splice @Plate::_l, 0, 2; |
|
124
|
24
|
|
100
|
|
|
137
|
($pos, $line) = splice @Plate::_l, 0, 2 while @Plate::_l and $Plate::_l[0] <= $+[1]; |
|
125
|
24
|
|
|
|
|
65
|
my $rem = $+[1] - $pos; |
|
126
|
24
|
100
|
|
|
|
77
|
$line += substr($_[0], $pos, $rem) =~ tr/\n// if $rem; |
|
127
|
24
|
|
|
|
|
58
|
$expr2stmt->(); |
|
128
|
24
|
|
|
|
|
52
|
$stmt .= "\n#line $line\n"; |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
|
|
131
|
514
|
100
|
|
|
|
2072
|
if (defined $2) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# % ... |
|
133
|
75
|
|
|
|
|
146
|
$expr2stmt->(); |
|
134
|
75
|
|
|
|
|
136
|
$stmt .= _parse_cmnt $2; |
|
135
|
75
|
|
|
|
|
156
|
$stmt .= "\n"; |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
} elsif (defined $3) { |
|
138
|
|
|
|
|
|
|
# <%perl> |
|
139
|
8
|
|
|
|
|
17
|
$expr2stmt->(); |
|
140
|
8
|
100
|
|
|
|
19
|
unless (defined $4) { |
|
141
|
2
|
|
|
|
|
6
|
my $line = 1 + $stmt =~ y/\n//; |
|
142
|
2
|
|
100
|
|
|
12
|
$line = "$_[2] line $line.\nPlate ".($pre && 'pre').'compilation failed'; |
|
143
|
2
|
|
100
|
|
|
9
|
my $tag = ($pre && '%').'%'.$3; |
|
144
|
2
|
|
|
|
|
197
|
croak "Opening <$tag...> tag without closing $tag> tag at $line"; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
6
|
|
|
|
|
15
|
$stmt .= "\n$4\n"; |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
} elsif (defined $5) { |
|
149
|
|
|
|
|
|
|
# <%def ...> |
|
150
|
7
|
|
|
|
|
17
|
$expr2stmt->(); |
|
151
|
7
|
|
100
|
|
|
38
|
local $_[3] = ($pre && '%').'%def'; |
|
152
|
7
|
|
|
|
|
22
|
$stmt .= 'local$$Plate::_s{mem}{'._parse_defn($5)."}=\nsub{".&_parse.'};'; |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
} elsif (defined $6) { |
|
155
|
|
|
|
|
|
|
# <%# ... %> |
|
156
|
4
|
|
|
|
|
10
|
my $add_lines = $6 =~ tr/\n//; |
|
157
|
4
|
100
|
|
|
|
22
|
(@expr ? $expr[-1] : defined $stmt ? $stmt : ($expr[0] = "''")) .= "\n" x $add_lines if $add_lines; |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
158
|
4
|
100
|
|
|
|
10
|
$fix_line_num = @expr if $fix_line_num; |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
} elsif (defined $7) { |
|
161
|
|
|
|
|
|
|
# <% ... %> |
|
162
|
100
|
|
|
|
|
500
|
my $nl1 = "\n" x substr($_[0], $+[1], $-[7] - $+[1]) =~ tr/\n//; |
|
163
|
100
|
|
|
|
|
390
|
my $nl2 = "\n" x substr($_[0], $+[7], $+[0] - $+[7]) =~ tr/\n//; |
|
164
|
100
|
100
|
|
|
|
289
|
$expr2stmt->(1) if $fix_line_num; |
|
165
|
|
|
|
|
|
|
$fix_line_num = push @expr, |
|
166
|
100
|
|
100
|
|
|
968
|
_parse_fltr "do{$nl1$7}$nl2", $8 // $$Plate::_s{auto_filter}; |
|
167
|
98
|
100
|
|
|
|
231
|
$expr2stmt->() if $pre; |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
} elsif (defined $10) { |
|
170
|
|
|
|
|
|
|
# <& ... &> or <&| ... &> |
|
171
|
68
|
|
|
|
|
379
|
my $nl = "\n" x (substr($_[0], $+[1], $+[0] - $+[1]) =~ tr/\n// - $10 =~ tr/\n//); |
|
172
|
68
|
|
|
|
|
142
|
my($tmpl, $args) = do { $10 =~ /^([\w\/\.-]+)\s*(?:,\s*(.*))?$/s }; |
|
|
68
|
|
|
|
|
394
|
|
|
173
|
68
|
100
|
100
|
|
|
284
|
$expr2stmt->(!$pre) if $pre or $fix_line_num; |
|
174
|
68
|
100
|
|
|
|
126
|
if (defined $tmpl) { |
|
175
|
57
|
100
|
|
|
|
125
|
if ($tmpl eq '_') { |
|
176
|
|
|
|
|
|
|
$fix_line_num = push @expr, _parse_fltr defined $9 |
|
177
|
25
|
100
|
|
|
|
112
|
? do { |
|
|
|
100
|
|
|
|
|
|
|
178
|
9
|
100
|
|
|
|
26
|
$args = defined $args ? "($args)" : ''; |
|
179
|
9
|
100
|
|
|
|
27
|
local $_[3] = $pre ? '&&' : '&'; |
|
180
|
9
|
|
|
|
|
40
|
'(@Plate::_c?do{local@Plate::_c=@Plate::_c;&{splice@Plate::_c,-1,1,sub{'.&_parse."}}$args}:undef)$nl" |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
: defined $args ? "do{Plate::content($args)}$nl" : "do{&Plate::content}$nl", $11; |
|
183
|
25
|
100
|
100
|
|
|
79
|
$expr2stmt->() if $pre and $nl; |
|
184
|
25
|
|
|
|
|
212
|
next; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
32
|
100
|
|
|
|
112
|
$tmpl = defined $args ? "Plate::_r('$tmpl',($args)," : "Plate::_r('$tmpl',"; |
|
187
|
|
|
|
|
|
|
} else { |
|
188
|
11
|
|
|
|
|
33
|
$tmpl = "Plate::_r($10,"; |
|
189
|
|
|
|
|
|
|
} |
|
190
|
43
|
100
|
|
|
|
257
|
$fix_line_num = push @expr, |
|
|
|
100
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
_parse_fltr "do{$tmpl".(defined $9 ? (local $_[3] = $pre ? '&&' : '&', 'sub{'.&_parse.'}') : 'undef').")}$nl", $11; |
|
192
|
42
|
100
|
100
|
|
|
135
|
$expr2stmt->() if $pre and $nl; |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
} else { |
|
195
|
|
|
|
|
|
|
# %...> or &> or \z |
|
196
|
252
|
|
100
|
|
|
1358
|
my $tag = $12 // $13 // ''; |
|
|
|
|
100
|
|
|
|
|
|
197
|
252
|
100
|
|
|
|
534
|
if ($tag ne $_[3]) { |
|
198
|
3
|
|
100
|
|
|
22
|
my $line = 1 + join('', $stmt // '', @expr) =~ y/\n//; |
|
199
|
3
|
|
100
|
|
|
23
|
$line = "$_[2] line $line.\nPlate ".($pre && 'pre').'compilation failed'; |
|
200
|
3
|
100
|
|
|
|
348
|
croak $tag |
|
201
|
|
|
|
|
|
|
? "Closing $tag> tag without opening <$tag...> tag at $line" |
|
202
|
|
|
|
|
|
|
: "Opening <$_[3]...> tag without closing $_[3]> tag at $line"; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
my $pl = defined $stmt |
|
206
|
249
|
100
|
|
|
|
666
|
? do { |
|
|
|
100
|
|
|
|
|
|
|
207
|
81
|
100
|
100
|
|
|
219
|
$stmt .= ';push@Plate::_l,length$Plate::_b,__LINE__' if $pre and @expr; |
|
208
|
81
|
|
|
|
|
297
|
$stmt.join('.', ';$Plate::_b', @expr); |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
: @expr ? join('.', @expr) : "''"; |
|
211
|
249
|
100
|
100
|
|
|
756
|
$pl .= '=~s/\R\z//r' if !$pre and $$Plate::_s{chomp}; |
|
212
|
249
|
100
|
|
|
|
488
|
$pl .= "\n" if defined $12; |
|
213
|
249
|
|
|
|
|
1624
|
return $pl; |
|
214
|
|
|
|
|
|
|
} |
|
215
|
|
|
|
|
|
|
|
|
216
|
231
|
100
|
100
|
|
|
2319
|
if (!$pre and @Plate::_l and $Plate::_l[0] <= $+[0]) { |
|
|
|
|
100
|
|
|
|
|
|
217
|
5
|
|
|
|
|
14
|
my($pos, $line) = splice @Plate::_l, 0, 2; |
|
218
|
5
|
|
66
|
|
|
35
|
($pos, $line) = splice @Plate::_l, 0, 2 while @Plate::_l and $Plate::_l[0] <= $+[0]; |
|
219
|
5
|
|
|
|
|
13
|
my $rem = $+[0] - $pos; |
|
220
|
5
|
100
|
|
|
|
27
|
$line += substr($_[0], $pos, $rem) =~ tr/\n// if $rem; |
|
221
|
5
|
|
|
|
|
14
|
$expr2stmt->(); |
|
222
|
5
|
|
|
|
|
42
|
$stmt .= "\n#line $line\n"; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub _read { |
|
228
|
20
|
100
|
|
20
|
|
1074
|
open my $fh, '<'.$_[0]{io_layers}, $_[1] |
|
229
|
|
|
|
|
|
|
or croak "Can't read $_[1]: $!"; |
|
230
|
16
|
|
|
|
|
23053
|
local $/; |
|
231
|
16
|
|
|
|
|
593
|
scalar <$fh>; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
sub _write { |
|
234
|
8
|
|
|
8
|
|
60
|
my $umask = umask $$Plate::_s{umask}; |
|
235
|
8
|
100
|
|
|
|
637
|
(open(my $fh, '>:utf8', $_[0]), umask $umask)[0] |
|
236
|
|
|
|
|
|
|
or croak "Can't write $_[0]: $!"; |
|
237
|
7
|
|
|
|
|
451
|
print $fh $_[1]; |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
sub _eval { |
|
240
|
215
|
|
|
215
|
|
25345
|
eval "package $$Plate::_s{package};$_[0]"; |
|
|
3
|
|
|
3
|
|
44
|
|
|
|
3
|
|
|
1
|
|
7
|
|
|
|
3
|
|
|
|
|
107
|
|
|
|
1
|
|
|
|
|
7
|
|
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
126
|
|
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
sub _compile { |
|
243
|
114
|
|
|
114
|
|
727
|
my($pl, $file) = @_; |
|
244
|
114
|
|
|
|
|
154
|
my($line, $sub); |
|
245
|
114
|
100
|
|
|
|
223
|
if (length $file) { |
|
246
|
46
|
|
|
|
|
113
|
$line = "\n#line 1 $_[1]\n"; |
|
247
|
|
|
|
|
|
|
} else { |
|
248
|
68
|
|
|
|
|
100
|
$file = '-'; |
|
249
|
68
|
|
|
|
|
95
|
$line = ''; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
114
|
|
|
|
|
300
|
local @Plate::_l; |
|
252
|
|
|
|
|
|
|
# Precompile |
|
253
|
114
|
|
|
|
|
282
|
$pl = _parse $pl, $re_pre, $file, ''; |
|
254
|
111
|
|
|
|
|
333
|
$pl = "sub{$line$pl}"; |
|
255
|
111
|
100
|
|
|
|
240
|
$sub = _eval $pl |
|
256
|
|
|
|
|
|
|
or croak $@.'Plate precompilation failed'; |
|
257
|
110
|
100
|
|
|
|
771
|
defined($pl = eval { $sub->() }) |
|
|
110
|
|
|
|
|
1518
|
|
|
258
|
|
|
|
|
|
|
or croak $@.'Plate precompilation failed'; |
|
259
|
|
|
|
|
|
|
# Compile |
|
260
|
109
|
|
|
|
|
451
|
$pl = _parse $pl, $re_run, $file, ''; |
|
261
|
104
|
|
|
|
|
373
|
$pl = "$$Plate::_s{once}sub{$$Plate::_s{init}$line$pl}"; |
|
262
|
104
|
100
|
|
|
|
223
|
$sub = _eval $pl |
|
263
|
|
|
|
|
|
|
or croak $@.'Plate compilation failed'; |
|
264
|
|
|
|
|
|
|
# Cache |
|
265
|
99
|
100
|
|
|
|
995
|
_write $_[2], "use 5.020;use warnings;use utf8;package $$Plate::_s{package};$pl" if defined $_[2]; |
|
266
|
98
|
100
|
|
|
|
230
|
$$Plate::_s{mod}{$_[3]} = $_[4] if defined $_[4]; |
|
267
|
98
|
|
|
|
|
648
|
return $sub; |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
sub _make_cache_dir { |
|
270
|
11
|
|
|
11
|
|
27
|
my($dir, @mkdir) = $_[1]; |
|
271
|
11
|
|
100
|
|
|
113
|
unshift @mkdir, $_[0]{cache_path}.$dir until $dir !~ s|/[^/]*$|| or -d $_[0]{cache_path}.$dir; |
|
272
|
11
|
100
|
|
|
|
37
|
return unless @mkdir; |
|
273
|
2
|
|
|
|
|
13
|
my $umask = umask $_[0]{umask}; |
|
274
|
2
|
|
66
|
|
|
207
|
mkdir $_ or umask $umask, croak "Can't create cache directory $_: $!" for @mkdir; |
|
275
|
1
|
|
|
|
|
7
|
umask $umask; |
|
276
|
1
|
|
|
|
|
4
|
return; |
|
277
|
|
|
|
|
|
|
} |
|
278
|
|
|
|
|
|
|
sub _plate_file { |
|
279
|
49
|
100
|
|
49
|
|
577
|
defined $_[0]{path} ? $_[0]{path}.$_[1].$_[0]{suffix} : undef; |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
sub _cache_file { |
|
282
|
38
|
100
|
|
38
|
|
124
|
defined $_[0]{cache_path} ? $_[0]{cache_path}.$_[1].$_[0]{cache_suffix} : undef; |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
sub _load { |
|
285
|
37
|
|
|
37
|
|
96
|
my $plate = $_[0]->_plate_file($_[1]); |
|
286
|
37
|
|
|
|
|
83
|
my $cache = $_[0]->_cache_file($_[1]); |
|
287
|
37
|
|
|
|
|
57
|
my $_n; |
|
288
|
37
|
100
|
|
|
|
71
|
if (defined $cache) { |
|
|
|
100
|
|
|
|
|
|
|
289
|
26
|
100
|
|
|
|
48
|
if ($_[0]{static}) { |
|
290
|
8
|
50
|
66
|
|
|
541
|
return do $cache // croak $@ ? $@.'Plate compilation failed' : "Couldn't load $cache: $!" if -f $cache; |
|
|
|
100
|
|
|
|
|
|
|
291
|
4
|
|
66
|
|
|
121
|
$plate // croak "Plate template '$_[1]' does not exist"; |
|
292
|
|
|
|
|
|
|
} else { |
|
293
|
18
|
|
100
|
|
|
747
|
$_n = $_[2] // (stat $plate)[9] // croak "Can't read $plate: $!"; |
|
|
|
|
66
|
|
|
|
|
|
294
|
15
|
100
|
66
|
|
|
261
|
if (-f $cache and ($_[0]{mod}{$_[1]} // (stat _)[9]) >= $_n) { |
|
|
|
|
100
|
|
|
|
|
|
295
|
7
|
50
|
66
|
|
|
1224
|
my $sub = do $cache // croak $@ ? $@.'Plate compilation failed' : "Couldn't load $cache: $!"; |
|
296
|
6
|
|
66
|
|
|
1539
|
$_[0]{mod}{$_[1]} //= $_n; |
|
297
|
6
|
|
|
|
|
36
|
return $sub; |
|
298
|
|
|
|
|
|
|
} |
|
299
|
|
|
|
|
|
|
} |
|
300
|
11
|
|
|
|
|
39
|
$_[0]->_make_cache_dir($_[1]); |
|
301
|
|
|
|
|
|
|
} elsif (defined $plate) { |
|
302
|
10
|
100
|
66
|
|
|
102
|
$_n = (stat $plate)[9] unless $_[0]{static} or exists $_[0]{mod}{$_[1]}; |
|
303
|
|
|
|
|
|
|
} else { |
|
304
|
1
|
|
|
|
|
95
|
croak "Plate template '$_[1]' does not exist"; |
|
305
|
|
|
|
|
|
|
} |
|
306
|
20
|
|
|
|
|
68
|
_compile $_[0]->_read($plate), $plate, $cache, $_[1], $_n; |
|
307
|
|
|
|
|
|
|
} |
|
308
|
|
|
|
|
|
|
sub _cached_sub { |
|
309
|
151
|
100
|
100
|
151
|
|
903
|
return $_[0]{mem}{$_[1]} //= $_[0]->_load($_[1]) if $_[0]{static} or not exists $_[0]{mod}{$_[1]}; |
|
|
|
|
100
|
|
|
|
|
|
310
|
5
|
100
|
|
|
|
14
|
my $mod = (stat $_[0]->_plate_file($_[1]))[9] |
|
311
|
|
|
|
|
|
|
or croak "Plate template '$_[1]' does not exist"; |
|
312
|
4
|
100
|
66
|
|
|
31
|
return $_[0]{mem}{$_[1]} //= $_[0]->_load($_[1], $mod) if $_[0]{mod}{$_[1]} == $mod; |
|
313
|
1
|
|
|
|
|
5
|
$_[0]{mem}{$_[1]} = $_[0]->_load($_[1], $mod); |
|
314
|
|
|
|
|
|
|
} |
|
315
|
|
|
|
|
|
|
sub _sub { |
|
316
|
|
|
|
|
|
|
$$Plate::_s{cache_code} |
|
317
|
|
|
|
|
|
|
? $Plate::_s->_cached_sub($_[0]) |
|
318
|
180
|
100
|
66
|
180
|
|
554
|
: $$Plate::_s{mem}{$_[0]} // $Plate::_s->_load($_[0]); |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
3
|
|
|
sub _empty {} |
|
322
|
|
|
|
|
|
|
sub _r { |
|
323
|
144
|
|
|
144
|
|
964
|
my $tmpl = shift; |
|
324
|
144
|
100
|
|
|
|
280
|
if ($tmpl eq '_') { |
|
325
|
6
|
100
|
|
|
|
61
|
return undef unless @Plate::_c; |
|
326
|
3
|
100
|
|
|
|
9
|
if (defined(my $c = pop)) { |
|
327
|
2
|
|
|
|
|
6
|
local @Plate::_c = @Plate::_c; |
|
328
|
2
|
|
|
|
|
3
|
return &{splice @Plate::_c, -1, 1, $c}; |
|
|
2
|
|
|
|
|
7
|
|
|
329
|
|
|
|
|
|
|
} else { |
|
330
|
1
|
|
|
|
|
4
|
$tmpl = pop @Plate::_c; |
|
331
|
1
|
|
|
|
|
3
|
local @Plate::_c = @Plate::_c; |
|
332
|
1
|
|
|
|
|
1
|
return &{$tmpl}; |
|
|
1
|
|
|
|
|
22
|
|
|
333
|
|
|
|
|
|
|
} |
|
334
|
|
|
|
|
|
|
} |
|
335
|
138
|
100
|
|
|
|
282
|
if (@Plate::_c >= $$Plate::_s{max_call_depth}) { |
|
336
|
1
|
|
|
|
|
10
|
my($f, $l) = (caller 0)[1, 2]; |
|
337
|
1
|
|
|
|
|
100
|
die "Call depth limit exceeded while calling \"$tmpl\" at $f line $l.\n"; |
|
338
|
|
|
|
|
|
|
} |
|
339
|
137
|
|
|
|
|
570
|
local @Plate::_c = @Plate::_c; |
|
340
|
137
|
|
100
|
|
|
460
|
push @Plate::_c, pop // \&_empty; |
|
341
|
137
|
|
|
|
|
218
|
&{_sub $tmpl}; |
|
|
137
|
|
|
|
|
296
|
|
|
342
|
|
|
|
|
|
|
} |
|
343
|
|
|
|
|
|
|
sub _f { |
|
344
|
120
|
|
|
120
|
|
988
|
my $f = shift; |
|
345
|
120
|
|
66
|
|
|
148
|
goto &{$$Plate::_s{filters}{$f} // croak "No '$f' filter defined"}; |
|
|
120
|
|
|
|
|
527
|
|
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
sub _path { |
|
349
|
21
|
|
|
21
|
|
504
|
my $path = $_[0]; |
|
350
|
21
|
|
|
|
|
28
|
my $vol = WINDOWS ? $path =~ s'^[\\/]{2}(?=[^\\/])'' ? '//' : $path =~ s'^([a-zA-Z]:)'' ? ucfirst $1 : '' : ''; |
|
351
|
21
|
100
|
66
|
|
|
62
|
length $path or return (length $vol or not $_[1]) ? $vol : './'; |
|
|
|
100
|
|
|
|
|
|
|
352
|
19
|
|
|
|
|
280
|
my @dir = grep $_ ne '.', split WINDOWS ? qr'[\\/]+' : qr'/+', $path.'/', -1; |
|
353
|
19
|
100
|
66
|
|
|
125
|
$vol = './' if $_[1] and not length $vol and (length $dir[0] or @dir == 1); |
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
354
|
19
|
|
|
|
|
83
|
$vol.join('/', @dir); |
|
355
|
|
|
|
|
|
|
} |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
{ |
|
358
|
|
|
|
|
|
|
my %esc_html = ('"' => '"', '&' => '&', "'" => ''', '<' => '<', '>' => '>'); |
|
359
|
7
|
|
|
7
|
|
105
|
no warnings 'uninitialized'; |
|
|
7
|
|
|
|
|
17
|
|
|
|
7
|
|
|
|
|
12558
|
|
|
360
|
115
|
|
|
115
|
|
985
|
sub _basic_html_filter { $_[0] =~ s/(["&'<>])/$esc_html{$1}/egr } |
|
|
45
|
|
|
|
|
344
|
|
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
Plate is a very fast, efficient and full-featured templating engine. |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Inspired by L and L, the goal of this templating engine is speed and functionality. |
|
368
|
|
|
|
|
|
|
It has no non-core dependencies, is a compact size and supports embedded Perl. |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
Features include preprocessing templates, |
|
371
|
|
|
|
|
|
|
caching compiled templates, |
|
372
|
|
|
|
|
|
|
variable escaping/filtering, |
|
373
|
|
|
|
|
|
|
localised global variables. |
|
374
|
|
|
|
|
|
|
Templates can also include other templates, with optional content |
|
375
|
|
|
|
|
|
|
and even define or override templates locally. |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
All templates have strict, warnings, utf8 and Perl 5.20 features enabled. |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head2 Example |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Here is an example template for a letter stored in the file: C |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
% my($title, $surname) = @_; |
|
384
|
|
|
|
|
|
|
Dear <% $title %> <% $surname %>, |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
<& _ &> |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
Kind Regards, |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
E. X. Ample |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
Another template could I this template, Eg: C |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
<&| letter, 'Dr.', 'No' &>\ |
|
395
|
|
|
|
|
|
|
In response to the recently advertised position, please |
|
396
|
|
|
|
|
|
|
consider my résumé in your search for a professional sidekick. |
|
397
|
|
|
|
|
|
|
&> |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
Serving the C template will result in the following output: |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
Dear Dr. No, |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
In response to the recent advertised position, please |
|
404
|
|
|
|
|
|
|
consider my résumé in your search for a professional sidekick. |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
Kind Regards, |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
E. X. Ample |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
Here is the code to render this output: |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
use Plate; |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
my $plate = new Plate; |
|
415
|
|
|
|
|
|
|
my $output = $plate->serve('job'); |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
=head2 Markup |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=head3 Variables |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
<% $var %> |
|
422
|
|
|
|
|
|
|
<% $unescaped |%> |
|
423
|
|
|
|
|
|
|
<% $filtered |trim |html %> |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
Variables are interpolated into the output and optionally filtered (escaped). |
|
426
|
|
|
|
|
|
|
Filters are listed in the order to be applied preceded by a C<|> character. |
|
427
|
|
|
|
|
|
|
If no filter is given as in the first example, then the default filter is applied. |
|
428
|
|
|
|
|
|
|
To explicitly avoid the default filter use the empty string as a filter. |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=head3 Statements |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
% my $user = db_lookup(user => 'bob'); |
|
433
|
|
|
|
|
|
|
% for my $var (@list) { |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Lines that start with a C<%> character are treated as Perl statements. |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=head3 Comments |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
%# Comment line |
|
440
|
|
|
|
|
|
|
<% # inline comment %> |
|
441
|
|
|
|
|
|
|
<%# |
|
442
|
|
|
|
|
|
|
Multi-line |
|
443
|
|
|
|
|
|
|
comment |
|
444
|
|
|
|
|
|
|
%> |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head3 Perl blocks |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
<%perl> |
|
449
|
|
|
|
|
|
|
... |
|
450
|
|
|
|
|
|
|
%perl> |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Perl code can also be wrapped in a perl block. |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head3 Newlines |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Newline characters can be escaped with a backslash, Eg: |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
% for my $var ('a' .. 'c') { |
|
459
|
|
|
|
|
|
|
<% $var %>\ |
|
460
|
|
|
|
|
|
|
% } |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
This will result in the output C, all on one line. |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=head3 Content |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
<& _ &> |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
A template can be served with content. This markup will insert the content provided, if any. |
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=head3 Include other templates |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
<& header, 'My Title' &> |
|
473
|
|
|
|
|
|
|
... |
|
474
|
|
|
|
|
|
|
<& footer &> |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
A template can include other templates with optional arguments. |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=head3 Include other templates with provided content |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
<&| paragraph &> |
|
481
|
|
|
|
|
|
|
This content is passed to the "paragraph" template. |
|
482
|
|
|
|
|
|
|
&> |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
Plain text, <&| bold &>bold text&>, plain text. |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
An included template can have its own content passed in. |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=head3 Def blocks |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
<%def copyright> |
|
491
|
|
|
|
|
|
|
Copyright © <% $_[0] %> |
|
492
|
|
|
|
|
|
|
%def> |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
<& copyright, 2018 &> |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
Local templates can be defined in a template. |
|
497
|
|
|
|
|
|
|
They can even override existing templates. |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
=head1 SUBROUTINES/METHODS |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=head2 new |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
my $plate = Plate->new(%options); |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
Creates a new C engine with the options provided. |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
Options (with their defaults) are: |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=over |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=item C<< auto_filter => 'html' >> |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
The name of the default filter to use for template variables when no filter is specified, S >>>. |
|
514
|
|
|
|
|
|
|
The built-in default filter is a very basic HTML filter. |
|
515
|
|
|
|
|
|
|
Set this to C to disable the default filter. |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
To prevent the default filter being used for just a single variable, |
|
518
|
|
|
|
|
|
|
just set the filter to an empty string. Eg: S >>> |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=item C<< cache_code => 1 >> |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
If set to a true value, the engine will cache compiled template code in memory. |
|
523
|
|
|
|
|
|
|
This vastly improves performance at the expense of some memory. |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
=item C<< cache_path => undef >> |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
Set this to a directory to store compiled templates on the filesystem. |
|
528
|
|
|
|
|
|
|
If the directory does not exist, it will attempt to create it using the C setting. |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=item C<< cache_suffix => '.pl' >> |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
Compiled templates stored on the filesystem will have this suffix appended. |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=item C<< chomp => 1 >> |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
If set to a true value (the default), |
|
537
|
|
|
|
|
|
|
the final newline in every template will be removed. |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=item C<< encoding => 'UTF-8' >> |
|
540
|
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
Set this to the encoding of your template files. |
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=item C<< filters => { html => \&_basic_html_filter } >> |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
A hash of filters to set for use in templates. |
|
546
|
|
|
|
|
|
|
The key is the name of the filter, and the value is the CODE ref, subroutine name or C. |
|
547
|
|
|
|
|
|
|
The subroutine will be given one argument (the content to filter) as a string, |
|
548
|
|
|
|
|
|
|
and must return the filtered string. |
|
549
|
|
|
|
|
|
|
To remove a filter pass C as it's value. |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
To remove all filters pass C instead of a HASH ref. |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=item C<< keep_undef => undef >> |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
If set to a false value (the default), |
|
556
|
|
|
|
|
|
|
then variables and calls that return C are converted to an empty string. |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
=item C<< max_call_depth => 99 >> |
|
559
|
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
This sets the maximum call depth to prevent infinite recursion. |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=item C<< package => 'Plate::Template' >> |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
The package name that templates are compiled and run in. |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=item C<< path => '' >> |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
The path to the templates on the filesystem. |
|
569
|
|
|
|
|
|
|
An empty string (the default) refers to the current directory. |
|
570
|
|
|
|
|
|
|
If set to C then the filesystem will not be searched, |
|
571
|
|
|
|
|
|
|
only cached templates will be served. |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=item C<< static => undef >> |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
If set to a false value (the default), |
|
576
|
|
|
|
|
|
|
the engine will reload and recompile templates whenever files are modified. |
|
577
|
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
If set to a true value, |
|
579
|
|
|
|
|
|
|
file modification will not be checked nor will templates be reloaded. |
|
580
|
|
|
|
|
|
|
While this improves performance in production, it is not recommended in development. |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=item C<< suffix => '.plate' >> |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
The suffix appended to template names when searching on the filesystem. |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=item C<< umask => 077 >> |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
The C used when creating cache files and directories. |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=item C<< vars => {} >> |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
A hash of vars to set for use in templates. |
|
593
|
|
|
|
|
|
|
This will define new local variables to be imported into the templating package when compiling and running templates. |
|
594
|
|
|
|
|
|
|
If the value is not a reference it will be a constant in the templating package. |
|
595
|
|
|
|
|
|
|
To remove a var pass C as it's value. |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
To remove all vars pass C instead of a HASH ref. |
|
598
|
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
All templates will have access to these variables, subroutines and constants even under C |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=back |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=cut |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
sub new { |
|
606
|
19
|
|
|
19
|
1
|
7457
|
my $class = shift; |
|
607
|
19
|
|
|
|
|
275
|
my $self = bless { |
|
608
|
|
|
|
|
|
|
auto_filter => 'html', |
|
609
|
|
|
|
|
|
|
cache_code => 1, |
|
610
|
|
|
|
|
|
|
cache_path => undef, |
|
611
|
|
|
|
|
|
|
cache_suffix => '.pl', |
|
612
|
|
|
|
|
|
|
chomp => 1, |
|
613
|
|
|
|
|
|
|
filters => { |
|
614
|
|
|
|
|
|
|
html => \&_basic_html_filter, |
|
615
|
|
|
|
|
|
|
}, |
|
616
|
|
|
|
|
|
|
init => '', |
|
617
|
|
|
|
|
|
|
io_layers => ':encoding(UTF-8)', |
|
618
|
|
|
|
|
|
|
keep_undef => undef, |
|
619
|
|
|
|
|
|
|
max_call_depth => 99, |
|
620
|
|
|
|
|
|
|
mem => {}, |
|
621
|
|
|
|
|
|
|
once => '', |
|
622
|
|
|
|
|
|
|
package => 'Plate::Template', |
|
623
|
|
|
|
|
|
|
path => '', |
|
624
|
|
|
|
|
|
|
static => undef, |
|
625
|
|
|
|
|
|
|
suffix => '.plate', |
|
626
|
|
|
|
|
|
|
umask => 077, |
|
627
|
|
|
|
|
|
|
vars => {}, |
|
628
|
|
|
|
|
|
|
}, $class; |
|
629
|
19
|
100
|
|
|
|
107
|
$self->set(@_) if @_; |
|
630
|
12
|
|
|
|
|
161
|
$self; |
|
631
|
|
|
|
|
|
|
} |
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
=head2 serve |
|
634
|
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
my $output = $plate->serve($template_name, @arguments); |
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
Renders a template. |
|
638
|
|
|
|
|
|
|
The C<@arguments> will be passed to the template as C<@_>. |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=head2 serve_with |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
my $output = $plate->serve_with($content, $template_name, @arguments); |
|
643
|
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
Renders a template with the provided content. |
|
645
|
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
The content can be passed in one of three ways. |
|
647
|
|
|
|
|
|
|
If C<$content> is a string then it is the name of a template to serve. |
|
648
|
|
|
|
|
|
|
If C<$content> is a SCALAR ref then it is the contents of a template to be compiled and served. |
|
649
|
|
|
|
|
|
|
C<$content> may also be a CODE ref which should return the content directly. |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=cut |
|
652
|
|
|
|
|
|
|
|
|
653
|
92
|
|
|
92
|
1
|
14798
|
sub serve { shift->serve_with(undef, @_) } |
|
654
|
|
|
|
|
|
|
sub serve_with { |
|
655
|
98
|
|
|
98
|
1
|
197
|
local $Plate::_s = shift; |
|
656
|
98
|
|
100
|
|
|
516
|
my($_c, $tmpl) = (shift // \&_empty, shift); |
|
657
|
98
|
|
|
|
|
622
|
_local_vars $$Plate::_s{package}, $$Plate::_s{vars}; |
|
658
|
98
|
100
|
|
|
|
469
|
local @Plate::_c = ref $_c eq 'CODE' ? $_c : ref $_c eq 'SCALAR' ? _compile $$_c : _sub $_c; |
|
|
|
100
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
|
|
660
|
98
|
100
|
|
|
|
343
|
my $sub = ref $tmpl eq 'SCALAR' |
|
661
|
|
|
|
|
|
|
? _compile $$tmpl |
|
662
|
|
|
|
|
|
|
: _sub $tmpl; |
|
663
|
81
|
|
|
|
|
1413
|
&$sub; |
|
664
|
|
|
|
|
|
|
} |
|
665
|
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=head2 content |
|
667
|
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
% my $content = &Plate::content; |
|
669
|
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
Used from within a template to return the content passed to that template. |
|
671
|
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=head2 has_content |
|
673
|
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
% if (Plate::has_content) { ... |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
Used from within a template to determine if that template was called with content. |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=cut |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
sub content { |
|
681
|
51
|
100
|
|
51
|
1
|
403
|
@Plate::_c ? do { local @Plate::_c = @Plate::_c; &{pop @Plate::_c} } : undef; |
|
|
50
|
|
|
|
|
91
|
|
|
|
50
|
|
|
|
|
67
|
|
|
|
50
|
|
|
|
|
151
|
|
|
682
|
|
|
|
|
|
|
} |
|
683
|
|
|
|
|
|
|
sub has_content { |
|
684
|
4
|
100
|
|
4
|
1
|
68
|
@Plate::_c and $Plate::_c[-1] != \&_empty; |
|
685
|
|
|
|
|
|
|
} |
|
686
|
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=head2 define |
|
688
|
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
$plate->define($template_name => $content); |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
This will cache a template in memory. |
|
692
|
|
|
|
|
|
|
The C<$content> is the contents of a template (as a string) to be compiled or a CODE ref. |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
This is useful if you need to use templates that are not stored on the file system, |
|
695
|
|
|
|
|
|
|
for example from a database or a custom subroutine. |
|
696
|
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
=head2 undefine |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
$plate->undefine; |
|
700
|
|
|
|
|
|
|
$plate->undefine($template_name); |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
This will delete a previously cached template, |
|
703
|
|
|
|
|
|
|
or all templates if the name is C. |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
=cut |
|
706
|
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
sub define { |
|
708
|
31
|
100
|
|
31
|
1
|
26302
|
delete $_[0]{mod}{$_[1]} if $_[0]{mod}; |
|
709
|
31
|
100
|
|
|
|
106
|
$_[0]{mem}{$_[1]} = ref $_[2] eq 'CODE' ? $_[2] : do { |
|
710
|
30
|
|
|
|
|
105
|
local($Plate::_s, @Plate::_c) = $_[0]; |
|
711
|
30
|
|
|
|
|
188
|
_local_vars $$Plate::_s{package}, $$Plate::_s{vars}; |
|
712
|
30
|
|
|
|
|
96
|
_compile $_[2], $_[1]; |
|
713
|
|
|
|
|
|
|
}; |
|
714
|
|
|
|
|
|
|
} |
|
715
|
|
|
|
|
|
|
sub undefine { |
|
716
|
3
|
100
|
|
3
|
1
|
1813
|
if (defined $_[1]) { |
|
717
|
2
|
|
|
|
|
7
|
delete $_[0]{mod}{$_[1]}; |
|
718
|
2
|
|
|
|
|
15
|
delete $_[0]{mem}{$_[1]}; |
|
719
|
|
|
|
|
|
|
} else { |
|
720
|
1
|
|
|
|
|
5
|
delete $_[0]{mod}; |
|
721
|
1
|
|
|
|
|
2
|
undef %{$_[0]{mem}}; |
|
|
1
|
|
|
|
|
6
|
|
|
722
|
|
|
|
|
|
|
} |
|
723
|
|
|
|
|
|
|
} |
|
724
|
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=head2 does_exist |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
my $exists = $plate->does_exist($template_name); |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
Returns true if a template by that name is cached or exists on the filesystem. |
|
730
|
|
|
|
|
|
|
No attempt will be made to compile the template. |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
=head2 can_serve |
|
733
|
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
my $ok = $plate->can_serve($template); |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
Returns true if the template can be served (compiles successfully), |
|
737
|
|
|
|
|
|
|
otherwise it sets C<$@> to the reason for failure. |
|
738
|
|
|
|
|
|
|
If C<$template> is a string then it is the name of a template to compile. |
|
739
|
|
|
|
|
|
|
If C<$template> is a SCALAR ref then it is the contents of a template to be compiled. |
|
740
|
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
=cut |
|
742
|
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
sub does_exist { |
|
744
|
8
|
100
|
100
|
8
|
1
|
2042
|
$_[0]{cache_code} and not $_[0]{static} and exists $_[0]{mod}{$_[1]} |
|
|
|
|
100
|
|
|
|
|
|
745
|
|
|
|
|
|
|
and return -f $_[0]->_plate_file($_[1]); |
|
746
|
|
|
|
|
|
|
|
|
747
|
7
|
100
|
66
|
|
|
32
|
exists $_[0]{mem}{$_[1]} or -f($_[0]->_plate_file($_[1]) // $_[0]->_cache_file($_[1])); |
|
748
|
|
|
|
|
|
|
} |
|
749
|
|
|
|
|
|
|
sub can_serve { |
|
750
|
7
|
|
|
7
|
1
|
23
|
local($Plate::_s, @Plate::_c) = $_[0]; |
|
751
|
7
|
|
|
|
|
68
|
_local_vars $$Plate::_s{package}, $$Plate::_s{vars}; |
|
752
|
7
|
100
|
|
|
|
14
|
!!eval { ref $_[1] eq 'SCALAR' ? _compile ${$_[1]} : _sub $_[1] }; |
|
|
7
|
|
|
|
|
26
|
|
|
|
1
|
|
|
|
|
4
|
|
|
753
|
|
|
|
|
|
|
} |
|
754
|
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
=head2 set |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
$plate->set(%options); |
|
758
|
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
Set the options for this C engine. |
|
760
|
|
|
|
|
|
|
Options are the same as those for L. |
|
761
|
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
=cut |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
my %sigil = ( |
|
765
|
|
|
|
|
|
|
ARRAY => '@', |
|
766
|
|
|
|
|
|
|
CODE => '&', |
|
767
|
|
|
|
|
|
|
GLOB => '*', |
|
768
|
|
|
|
|
|
|
HASH => '%', |
|
769
|
|
|
|
|
|
|
); |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
sub set { |
|
772
|
51
|
|
|
51
|
1
|
13353
|
my($self, %opt) = @_; |
|
773
|
|
|
|
|
|
|
|
|
774
|
51
|
|
|
|
|
203
|
while (my($k, $v) = each %opt) { |
|
775
|
72
|
100
|
|
|
|
516
|
if ($k eq 'encoding') { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
776
|
4
|
|
|
|
|
6
|
$k = 'io_layers'; |
|
777
|
4
|
100
|
|
|
|
16
|
$v = length $v ? $v eq 'utf8' ? ':utf8' : ":encoding($v)" : ''; |
|
|
|
100
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
} elsif ($k eq 'path') { |
|
779
|
15
|
100
|
|
|
|
41
|
$v = _path $v if length $v; |
|
780
|
|
|
|
|
|
|
} elsif ($k eq 'cache_path') { |
|
781
|
|
|
|
|
|
|
# A relative cache_path must start with "./" to prevent searching @INC when sourcing the file |
|
782
|
11
|
100
|
|
|
|
36
|
$v = _path $v, 1 if defined $v; |
|
783
|
|
|
|
|
|
|
} elsif ($k =~ /^(?:(?:cache_)?suffix|init|io_layers|once)$/) { |
|
784
|
5
|
|
100
|
|
|
32
|
$v //= ''; |
|
785
|
|
|
|
|
|
|
} elsif ($k eq 'filters') { |
|
786
|
9
|
100
|
|
|
|
21
|
if (defined $v) { |
|
787
|
8
|
100
|
|
|
|
131
|
ref $v eq 'HASH' or croak "Invalid $k (not a hash reference)"; |
|
788
|
7
|
|
|
|
|
31
|
while (my($name, $code) = each %$v) { |
|
789
|
8
|
100
|
|
|
|
117
|
$name =~ /^\w+$/ |
|
790
|
|
|
|
|
|
|
or croak "Invalid filter name '$name'"; |
|
791
|
7
|
100
|
|
|
|
17
|
if (defined $code) { |
|
792
|
|
|
|
|
|
|
ref $code eq 'CODE' |
|
793
|
|
|
|
|
|
|
or $code = ($code =~ /(.*)::(.*)/ |
|
794
|
|
|
|
|
|
|
? $1->can($2) |
|
795
|
5
|
100
|
100
|
|
|
207
|
: do { |
|
|
|
100
|
|
|
|
|
|
|
796
|
1
|
|
|
|
|
2
|
my($i,$p) = 0; |
|
797
|
1
|
|
|
|
|
6
|
$i++ while __PACKAGE__ eq ($p = caller $i); |
|
798
|
1
|
|
|
|
|
14
|
$p->can($code) |
|
799
|
|
|
|
|
|
|
}) |
|
800
|
|
|
|
|
|
|
or croak "Invalid subroutine '$$v{$name}' for filter '$name'"; |
|
801
|
4
|
|
|
|
|
19
|
$$self{filters}{$name} = $code; |
|
802
|
|
|
|
|
|
|
} else { |
|
803
|
2
|
|
|
|
|
10
|
delete $$self{filters}{$name}; |
|
804
|
|
|
|
|
|
|
} |
|
805
|
|
|
|
|
|
|
} |
|
806
|
|
|
|
|
|
|
} else { |
|
807
|
1
|
|
|
|
|
2
|
undef %{$$self{$k}}; |
|
|
1
|
|
|
|
|
7
|
|
|
808
|
|
|
|
|
|
|
} |
|
809
|
6
|
|
|
|
|
21
|
next; |
|
810
|
|
|
|
|
|
|
} elsif ($k eq 'vars') { |
|
811
|
5
|
100
|
|
|
|
15
|
if (defined $v) { |
|
812
|
4
|
100
|
|
|
|
139
|
ref $v eq 'HASH' or croak "Invalid $k (not a hash reference)"; |
|
813
|
3
|
|
|
|
|
13
|
while (my($name, $ref) = each %$v) { |
|
814
|
7
|
100
|
|
|
|
14
|
if (defined $ref) { |
|
815
|
6
|
|
100
|
|
|
35
|
my $sigil = $sigil{Scalar::Util::reftype $ref // 'CODE'} // '$'; |
|
|
|
|
100
|
|
|
|
|
|
816
|
6
|
100
|
|
|
|
66
|
$name =~ s/^\Q$sigil\E?/$sigil ne '&' && $sigil/e; |
|
|
6
|
|
|
|
|
28
|
|
|
817
|
6
|
|
|
|
|
27
|
$$self{vars}{$name} = $ref; |
|
818
|
|
|
|
|
|
|
} else { |
|
819
|
1
|
|
|
|
|
5
|
delete $$self{vars}{$name}; |
|
820
|
|
|
|
|
|
|
} |
|
821
|
|
|
|
|
|
|
} |
|
822
|
|
|
|
|
|
|
} else { |
|
823
|
1
|
|
|
|
|
2
|
undef %{$$self{$k}}; |
|
|
1
|
|
|
|
|
7
|
|
|
824
|
|
|
|
|
|
|
} |
|
825
|
4
|
|
|
|
|
14
|
next; |
|
826
|
|
|
|
|
|
|
} elsif ($k eq 'package') { |
|
827
|
3
|
100
|
100
|
|
|
232
|
defined $v and $v =~ /^[A-Z_a-z][0-9A-Z_a-z]*(?:::[0-9A-Z_a-z]+)*$/ |
|
|
|
|
100
|
|
|
|
|
|
828
|
|
|
|
|
|
|
or croak "Invalid package name '".($v // '')."'"; |
|
829
|
|
|
|
|
|
|
} elsif ($k !~ /^(?:auto_filter|cache_code|chomp|keep_undef|max_call_depth|static|umask)$/) { |
|
830
|
1
|
|
|
|
|
203
|
croak "Invalid setting '$k'"; |
|
831
|
|
|
|
|
|
|
} |
|
832
|
55
|
|
|
|
|
249
|
$$self{$k} = $v; |
|
833
|
|
|
|
|
|
|
} |
|
834
|
|
|
|
|
|
|
|
|
835
|
44
|
100
|
|
|
|
99
|
if (defined $$self{path}) { |
|
836
|
40
|
|
|
|
|
87
|
undef $!; |
|
837
|
40
|
100
|
|
|
|
97
|
my $dir = length $$self{path} ? $$self{path} : '.'; |
|
838
|
40
|
100
|
50
|
|
|
1046
|
-d $dir and -r _ or croak "Can't set path to $dir: ".($! || 'Not accessable'); |
|
|
|
|
66
|
|
|
|
|
|
839
|
39
|
100
|
100
|
|
|
215
|
undef $$self{static} if $$self{static} and $$self{static} eq 'auto'; |
|
840
|
|
|
|
|
|
|
} else { |
|
841
|
4
|
|
100
|
|
|
18
|
$$self{static} ||= 'auto'; |
|
842
|
|
|
|
|
|
|
} |
|
843
|
|
|
|
|
|
|
|
|
844
|
43
|
100
|
|
|
|
177
|
if (defined $$self{cache_path}) { |
|
|
|
100
|
|
|
|
|
|
|
845
|
15
|
|
|
|
|
36
|
my $dir = $$self{cache_path}; |
|
846
|
15
|
100
|
|
|
|
248
|
if (-d $dir) { |
|
847
|
10
|
50
|
|
|
|
99
|
-w _ or croak "Cache directory $dir is not writeable"; |
|
848
|
|
|
|
|
|
|
} else { |
|
849
|
5
|
|
|
|
|
41
|
my $umask = umask $$self{umask}; |
|
850
|
5
|
100
|
|
|
|
470
|
(mkdir($dir), umask $umask)[0] |
|
851
|
|
|
|
|
|
|
or croak "Can't create cache directory $dir: $!"; |
|
852
|
|
|
|
|
|
|
} |
|
853
|
|
|
|
|
|
|
} elsif (not $$self{cache_code}) { |
|
854
|
9
|
|
100
|
|
|
52
|
$$self{static} ||= 'auto'; |
|
855
|
|
|
|
|
|
|
} |
|
856
|
|
|
|
|
|
|
} |
|
857
|
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
=head1 AUTHOR |
|
859
|
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
Vernon Lyon C<< >> |
|
861
|
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
=head1 BUGS |
|
863
|
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
Please report any bugs or feature requests on L. |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
=head1 SOURCE |
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
The source code is hosted on L. |
|
869
|
|
|
|
|
|
|
Feel free to fork the repository and submit pull requests! |
|
870
|
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=head1 SUPPORT |
|
872
|
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
|
874
|
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
perldoc Plate |
|
876
|
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
You can also read the documentation online on L. |
|
878
|
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
880
|
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
Copyright (C) 2018, Vernon Lyon. |
|
882
|
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
|
884
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
|
885
|
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
=cut |
|
887
|
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
1; |