File Coverage

blib/lib/Text/Haml.pm
Criterion Covered Total %
statement 797 820 97.2
branch 329 384 85.6
condition 119 152 78.2
subroutine 102 102 100.0
pod 27 30 90.0
total 1374 1488 92.3


line stmt bran cond sub pod time code
1             package Text::Haml;
2              
3 31     31   199061 use strict;
  31         50  
  31         788  
4 31     31   103 use warnings;
  31         34  
  31         1504  
5              
6 31     31   9516 use IO::File;
  31         155407  
  31         2428  
7 31     31   171 use Scalar::Util qw/weaken/;
  31         41  
  31         2399  
8 31     31   12147 use Encode qw/decode/;
  31         180169  
  31         1651  
9 31     31   171 use Carp ();
  31         42  
  31         1061  
10 31     31   108 use File::Spec;
  31         36  
  31         503  
11 31     31   103 use File::Basename ();
  31         45  
  31         974  
12 31     31   9238 use URI::Escape ();
  31         28421  
  31         737  
13 31     31   289 use Digest::MD5;
  31         36  
  31         1969  
14              
15             our $VERSION = '0.990118';
16              
17 31     31   124 use constant CHUNK_SIZE => 4096;
  31         41  
  31         1511  
18 31     31   112 use constant _DEFAULT_CACHE_DIR => '.text_haml_cache';
  31         30  
  31         2401  
19             my $cache_dir;
20              
21             BEGIN {
22 31     31   2082 for my $dir ($ENV{HOME}, File::Spec->tmpdir) {
23 31 50 33     430 if (defined($dir) && -d $dir && -w _) {
      33        
24 31         387 $cache_dir = File::Spec->catdir($dir, _DEFAULT_CACHE_DIR);
25 31         138435 last;
26             }
27             }
28             }
29              
30             my $ESCAPE = {
31             '\"' => "\x22",
32             "\'" => "\x27",
33             '\\' => "\x5c",
34             '\/' => "\x2f",
35             '\b' => "\x8",
36             '\f' => "\xC",
37             '\n' => "\xA",
38             '\r' => "\xD",
39             '\t' => "\x9",
40             '\\\\' => "\x5c\x5c"
41             };
42              
43             my $UNESCAPE_RE = qr/
44             \\[\"\'\/\\bfnrt]
45             /x;
46              
47             my $STRING_DOUBLE_QUOTES_RE = qr/
48             \"
49             (?:
50             $UNESCAPE_RE
51             |
52             [\x20-\x21\x23-\x5b\x5b-\x{10ffff}]
53             )*
54             \"
55             /x;
56              
57             my $STRING_SINGLE_QUOTES_RE = qr/
58             \'
59             (?:
60             $UNESCAPE_RE
61             |
62             [\x20-\x26\x28-\x5b\x5b-\x{10ffff}]
63             )*
64             \'
65             /x;
66              
67             my $STRING_RE = qr/
68             $STRING_SINGLE_QUOTES_RE
69             |
70             $STRING_DOUBLE_QUOTES_RE
71             /x;
72              
73             sub new {
74 29     29 1 5115 my $class = shift;
75              
76             # Default attributes
77 29         283 my $attrs = {};
78 29         76 $attrs->{vars_as_subs} = 0;
79 29         63 $attrs->{tape} = [];
80 29         105 $attrs->{encoding} = 'utf-8';
81 29         52 $attrs->{escape_html} = 1;
82 29         157 $attrs->{helpers} = {};
83 29         239 $attrs->{helpers_options} = {};
84 28         53 $attrs->{format} = 'xhtml';
85 28         444 $attrs->{prepend} = '';
86 28         97 $attrs->{append} = '';
87 28         52 $attrs->{namespace} = '';
88 28         58 $attrs->{path} = ['.'];
89 28         323 $attrs->{cache} = 1; # 0: not cached, 1: checks mtime, 2: always cached
90 28         57 $attrs->{cache_dir} = _DEFAULT_CACHE_DIR;
91              
92 28         45 $attrs->{escape} = <<'EOF';
93             my $s = shift;
94             return unless defined $s;
95             $s =~ s/&/&/g;
96             $s =~ s/
97             $s =~ s/>/>/g;
98             $s =~ s/"/"/g;
99             $s =~ s/'/'/g;
100             return $s;
101             EOF
102              
103             $attrs->{filters} = {
104 4     5   20 plain => sub { $_[0] =~ s/\n*$//; $_[0] },
  4         7  
105 2     3   86 escaped => sub { $_[0] },
106 3     3   10 preserve => sub { $_[0] =~ s/\n/ /g; $_[0] },
  3         3  
107             javascript => sub {
108 6     6   60 "";
113             },
114             css => sub {
115 3     3   10 "";
120             },
121 28         415 };
122              
123 28         231 my $self = {%$attrs, @_};
124 28         229 bless $self, $class;
125              
126             # Convert to template fullpath
127             $self->path([
128 28 100       1108 map { ref($_) ? $_ : File::Spec->rel2abs($_) }
129 28 100       118 ref($self->path) eq 'ARRAY' ? @{$self->path} : $self->path
  26         95  
130             ]);
131              
132 28   33     227 $self->{helpers_arg} ||= $self;
133 28         222 weaken $self->{helpers_arg};
134              
135 28         256 return $self;
136             }
137              
138             # Yes, i know!
139 21 50   21 1 67 sub vars_as_subs { @_ > 1 ? $_[0]->{vars_as_subs} = $_[1] : $_[0]->{vars_as_subs}; }
140 47 100   47 1 2452 sub format { @_ > 1 ? $_[0]->{format} = $_[1] : $_[0]->{format} }
141 16 50   16 1 130 sub encoding { @_ > 1 ? $_[0]->{encoding} = $_[1] : $_[0]->{encoding} }
142 555 100   555 1 4230 sub escape_html { @_ > 1 ? $_[0]->{escape_html} = $_[1] : $_[0]->{escape_html}; }
143 256 100   256 1 661 sub code { @_ > 1 ? $_[0]->{code} = $_[1] : $_[0]->{code} }
144 252 100   252 1 777 sub compiled { @_ > 1 ? $_[0]->{compiled} = $_[1] : $_[0]->{compiled} }
145 140 50   140 1 508 sub helpers { @_ > 1 ? $_[0]->{helpers} = $_[1] : $_[0]->{helpers} }
146 7 50   7 0 19 sub helpers_options { @_ > 1 ? $_[0]->{helpers_options} = $_[1] : $_[0]->{helpers_options} }
147 13 50   13 1 63 sub filters { @_ > 1 ? $_[0]->{filters} = $_[1] : $_[0]->{filters} }
148 127 50   127 1 248 sub prepend { @_ > 1 ? $_[0]->{prepend} = $_[1] : $_[0]->{prepend} }
149 127 50   127 1 251 sub append { @_ > 1 ? $_[0]->{append} = $_[1] : $_[0]->{append} }
150 127 50   127 1 388 sub escape { @_ > 1 ? $_[0]->{escape} = $_[1] : $_[0]->{escape} }
151 1820 100   1820 1 3561 sub tape { @_ > 1 ? $_[0]->{tape} = $_[1] : $_[0]->{tape} }
152 87 100   87 1 386 sub path { @_ > 1 ? $_[0]->{path} = $_[1] : $_[0]->{path} }
153 20 50   20 1 120 sub cache { @_ > 1 ? $_[0]->{cache} = $_[1] : $_[0]->{cache} }
154 45 100   45 0 383 sub fullpath { @_ > 1 ? $_[0]->{fullpath} = $_[1] : $_[0]->{fullpath}; }
155 8 50   8 1 45 sub cache_dir { @_ > 1 ? $_[0]->{cache_dir} = $_[1] : $_[0]->{cache_dir}; }
156 21 100   21 0 767 sub cache_path { @_ > 1 ? $_[0]->{cache_path} = $_[1] : $_[0]->{cache_path}; }
157 284 100   284 1 698 sub namespace { @_ > 1 ? $_[0]->{namespace} = $_[1] : $_[0]->{namespace}; }
158 9 100   9 1 1611 sub error { @_ > 1 ? $_[0]->{error} = $_[1] : $_[0]->{error} }
159              
160             sub helpers_arg {
161 6 50   6 1 40 if (@_ > 1) {
162 2         5 $_[0]->{helpers_arg} = $_[1];
163 2         2 weaken $_[0]->{helpers_arg};
164             }
165             else {
166 6         315 return $_[0]->{helpers_arg};
167             }
168             }
169              
170              
171             our @AUTOCLOSE = (qw/meta img link br hr input area param col base/);
172              
173             sub add_helper {
174 7     5 1 1114 my $self = shift;
175 7         15 my ($name, $code, %options) = @_;
176              
177 7         80 $self->helpers->{$name} = $code;
178 7         28 $self->helpers_options->{$name} = \%options;
179             }
180              
181             sub add_filter {
182 3     1 1 369 my $self = shift;
183 3         244 my ($name, $code) = @_;
184              
185 3         8 $self->filters->{$name} = $code;
186             }
187              
188             sub parse {
189 146     144 1 140 my $self = shift;
190 146         173 my $tmpl = shift;
191              
192 146 100       306 $tmpl = '' unless defined $tmpl;
193              
194 146         280 $self->tape([]);
195              
196 146         576 my $level_token = quotemeta ' ';
197 146         138 my $escape_token = quotemeta '&';
198 146         131 my $unescape_token = quotemeta '!';
199 146         142 my $expr_token = quotemeta '=';
200 146         122 my $tag_start = quotemeta '%';
201 146         120 my $class_start = quotemeta '.';
202 146         301 my $id_start = quotemeta '#';
203              
204 146         135 my $attributes_start = quotemeta '{';
205 146         115 my $attributes_end = quotemeta '}';
206 146         132 my $attribute_arrow = quotemeta '=>';
207 146         115 my $attributes_sep = quotemeta ',';
208 146         102 my $attribute_prefix = quotemeta ':';
209 146         2264 my $attribute_name = qr/(?:$STRING_RE|.*?(?= |$attribute_arrow))/;
210 144         1367 my $attribute_value =
211             qr/(?:$STRING_RE|[^ $attributes_sep$attributes_end]+)/x;
212              
213 144         186 my $attributes_start2 = quotemeta '(';
214 144         125 my $attributes_end2 = quotemeta ')';
215 144         110 my $attribute_arrow2 = quotemeta '=';
216 144         129 my $attributes_sep2 = ' ';
217 144         1208 my $attribute_name2 = qr/(?:$STRING_RE|.*?(?= |$attribute_arrow2))/;
218 144         1175 my $attribute_value2 =
219             qr/(?:$STRING_RE|[^ $attributes_sep2$attributes_end2]+)/;
220              
221 144         159 my $filter_token = quotemeta ':';
222 144         140 my $quote = "'";
223 144         112 my $comment_token = quotemeta '-#';
224 144         115 my $trim_in = quotemeta '<';
225 144         125 my $trim_out = quotemeta '>';
226 144         113 my $autoclose_token = quotemeta '/';
227 144         118 my $multiline_token = quotemeta '|';
228              
229 144         1616 my $tag_name = qr/([^
230             $level_token
231             $attributes_start
232             $attributes_start2
233             $class_start
234             $id_start
235             $trim_in
236             $trim_out
237             $unescape_token
238             $escape_token
239             $expr_token
240             $autoclose_token]+)/;
241              
242 144         263 my $tape = $self->tape;
243              
244 144         124 my $level;
245             my @multiline_el_queue;
246 144         147 my $multiline_code_el = undef;
247 144         457 my @lines = split /\n/, $tmpl;
248 144 100       622 push @lines, '' if $tmpl =~ m/\n$/;
249 144 100       273 @lines = ('') if $tmpl eq "\n";
250 144         316 for (my $i = 0; $i < @lines; $i++) {
251 675         662 my $line = $lines[$i];
252              
253 675 100       1919 if ($line =~ s/^($level_token+)//) {
254 298         361 $level = length $1;
255             }
256             else {
257 377         339 $level = 0;
258             }
259              
260 675         1542 my $el = {level => $level, type => 'text', line => $line, lineno => $i+1};
261              
262 675 100 100     1165 if (defined $multiline_code_el && $line =~ /^[-!=%#.:]/) {
263 6         6 push @$tape, $multiline_code_el;
264 6         4 undef $multiline_code_el;
265             }
266              
267             # Haml comment
268 675 100       1618 if ($line =~ m/^$comment_token(?: (.*))?/) {
269 10         10 $el->{type} = 'comment';
270 10 100       25 $el->{text} = $1 if $1;
271 10         9 push @$tape, $el;
272 10         22 next;
273             }
274              
275             # Inside a filter
276 665         561 my $prev = $tape->[-1];
277 665 100 100     1953 if ($prev && $prev->{type} eq 'filter') {
278 34 100 100     106 if ($prev->{level} < $el->{level}
      66        
279             || ($i + 1 < @lines && $line eq ''))
280             {
281 22 100       32 $prev->{text} .= "\n" if $prev->{text};
282 22         25 $prev->{text} .= $line;
283 22         43 $prev->{line} .= "\n" . (' ' x $el->{level}) . $el->{line};
284 22         33 _update_lineno($prev, $i);
285 22         54 next;
286             }
287             }
288              
289             # Filter
290 643 100       841 if ($line =~ m/^:(\w+)/) {
291 12         15 $el->{type} = 'filter';
292 12         23 $el->{name} = $1;
293 12         15 $el->{text} = '';
294 12         13 push @$tape, $el;
295 12         30 next;
296             }
297              
298             # Doctype
299 631 100       808 if ($line =~ m/^!!!(?: ([^ ]+)(?: (.*))?)?$/) {
300 16         22 $el->{type} = 'text';
301 16         18 $el->{escape} = 0;
302 16         34 $el->{text} = $self->_doctype($1, $2);
303 16         44 push @$tape, $el;
304 16         42 next;
305             }
306              
307             # HTML comment
308 615 100       785 if ($line =~ m/^\/(?:\[if (.*)?\])?(?: *(.*))?/) {
309 8         9 $el->{type} = 'html_comment';
310 8 100       21 $el->{if} = $1 if $1;
311 8 100       19 $el->{text} = $2 if $2;
312 8         8 push @$tape, $el;
313 8         19 next;
314             }
315              
316             # Escaping, everything after is a text
317 607 100       829 if ($line =~ s/^\\//) {
318 2         5 $el->{type} = 'text', $el->{text} = $line;
319 2         2 push @$tape, $el;
320 2         6 next;
321             }
322              
323             # Block (note even the final multiline block must end in |)
324 605 100 100     2595 if ($line =~ s/^- \s*(.*)(\s\|\s*)$// ||
      66        
      66        
325             $line =~ s/^- \s*(.*)// ||
326             (defined $multiline_code_el && $line =~ s/^(.*)(\s\|\s*)$//)) {
327              
328 54         104 $el->{type} = 'block';
329            
330 54 100       125 if ($2) {
331 18   66     31 $multiline_code_el ||= $el;
332 18   100     31 $multiline_code_el->{text} ||= '';
333 18         24 $multiline_code_el->{text} .= $1;
334              
335 18         44 next;
336             }
337            
338 36         68 $el->{text} = $1;
339 36         44 push @$tape, $el;
340 36         95 next;
341            
342             }
343              
344             # Preserve whitespace
345 551 100       742 if ($line =~ s/^~ \s*(.*)//) {
346 1         2 $el->{type} = 'text';
347 1         2 $el->{text} = $1;
348 1         2 $el->{expr} = 1;
349 1         1 $el->{preserve_whitespace} = 1;
350 1         2 push @$tape, $el;
351 1         3 next;
352             }
353              
354             # Tag
355 550 100       2039 if ($line =~ m/^(?:$tag_start
356             |$class_start
357             |$id_start
358             )/x
359             )
360             {
361 243         264 $el->{type} = 'tag';
362 243         252 $el->{name} = '';
363              
364 243 100       1803 if ($line =~ s/^$tag_start$tag_name//) {
365 221         390 $el->{name} = $1;
366             }
367              
368 243         208 while (1) {
369 287 100       2456 if ($line =~ s/^$class_start$tag_name//) {
    100          
370 31         82 my $class = join(' ', split(/\./, $1));
371              
372 31   100     83 $el->{name} ||= 'div';
373 31   100     96 $el->{class} ||= [];
374 31         26 push @{$el->{class}}, $class;
  31         67  
375             }
376             elsif ($line =~ s/^$id_start$tag_name//) {
377 13         21 my $id = $1;
378              
379 13   100     50 $el->{name} ||= 'div';
380 13         30 $el->{id} = $id;
381             }
382             else {
383 243         314 last;
384             }
385             }
386              
387 243 100       3666 if ($line =~ m/^
388             (?:
389             $attributes_start\s*
390             $attribute_prefix?
391             $attribute_name\s*
392             $attribute_arrow\s*
393             $attribute_value
394             |
395             $attributes_start2\s*
396             $attribute_name2\s*
397             $attribute_arrow2\s*
398             $attribute_value2
399             )
400             /x
401             )
402             {
403 40         47 my $attrs = [];
404              
405 40         52 my $type = 'html';
406 40 100       192 if ($line =~ s/^$attributes_start//) {
407 30         34 $type = 'perl';
408             }
409             else {
410 10         55 $line =~ s/^$attributes_start2//;
411             }
412              
413 40         43 while (1) {
414 104 100 100     661 if (!$line) {
    100 100        
    100          
415 3   50     7 $line = $lines[++$i] || last;
416 3         9 $el->{line} .= "\n$line";
417 3         7 _update_lineno($el, $i);
418             }
419             elsif ($type eq 'perl' && $line =~ s/^$attributes_end//) {
420 30         37 last;
421             }
422             elsif ($type eq 'html' && $line =~ s/^$attributes_end2//)
423             {
424 10         12 last;
425             }
426             else {
427 61         48 my ($name, $value);
428              
429 61 100       1670 if ($line =~ s/^\s*$attribute_prefix?
    50          
430             ($attribute_name)\s*
431             $attribute_arrow\s*
432             ($attribute_value)\s*
433             (?:$attributes_sep\s*)?//x
434             )
435             {
436 44         66 $name = $1;
437 44         58 $value = $2;
438             }
439             elsif (
440             $line =~ s/^\s*
441             ($attribute_name2)\s*
442             $attribute_arrow2\s*
443             ($attribute_value2)\s*
444             (?:$attributes_sep2\s*)?//x
445             )
446             {
447 17         45 $name = $1;
448 17         21 $value = $2;
449             }
450             else {
451 0         0 $self->error('Tag attributes parsing error');
452 0         0 return;
453             }
454              
455 61 100       171 if ($name =~ s/^(?:'|")//) {
456 3         8 $name =~ s/(?:'|")$//;
457 3         26 $name =~ s/($UNESCAPE_RE)/$ESCAPE->{$1}/g;
458             }
459              
460 61 100 100     165 if ($value =~ s/^(?:'|")//) {
    100          
461 47         110 $value =~ s/(?:'|")$//;
462 47         175 $value =~ s/($UNESCAPE_RE)/$ESCAPE->{$1}/g;
463 47         180 push @$attrs,
464             $name => {type => 'text', text => $value};
465             }
466             elsif ($value eq 'true' || $value eq 'false') {
467 7 100       28 push @$attrs, $name => {
468             type => 'boolean',
469             text => $value eq 'true' ? 1 : 0
470             };
471             }
472             else {
473 7         32 push @$attrs,
474             $name => {type => 'expr', text => $value};
475             }
476             }
477             }
478              
479 40         51 $el->{type} = 'tag';
480 40 50       101 $el->{attrs} = $attrs if @$attrs;
481             }
482              
483 243 100       595 if ($line =~ s/^$trim_out ?//) {
484 1         1 $el->{trim_out} = 1;
485             }
486              
487 243 100       630 if ($line =~ s/^$trim_in ?//) {
488 1         2 $el->{trim_in} = 1;
489             }
490             }
491              
492 550 100       1589 if ($line =~ s/^($escape_token|$unescape_token)?$expr_token //) {
493 55         72 $el->{expr} = 1;
494 55 100       133 if ($1) {
495 8 100       27 $el->{escape} = quotemeta($1) eq $escape_token ? 1 : 0;
496             }
497             }
498              
499 550 100 100     1477 if ($el->{type} eq 'tag'
      66        
500             && ($line =~ s/$autoclose_token$//
501             || grep { $el->{name} eq $_ } @AUTOCLOSE)
502             )
503             {
504 15         105 $el->{autoclose} = 1;
505             }
506              
507 550 100       784 $line =~ s/^ // if $line;
508              
509             # Multiline
510 550 100 100     1651 if ($line && $line =~ s/(\s*)$multiline_token$//) {
511              
512             # For the first time
513 20 100 100     71 if (!$tape->[-1] || ref $tape->[-1]->{text} ne 'ARRAY') {
514 6         12 $el->{text} = [$line];
515 6   33     13 $el->{line} ||= $line . "$1|"; # XXX: is this really necessary?
516              
517 6         7 push @$tape, $el;
518 6         20 push @multiline_el_queue, $el;
519             }
520              
521             # Continue concatenation
522             else {
523 14         12 my $prev_stack_el = $tape->[-1];
524 14         9 push @{$prev_stack_el->{text}}, $line;
  14         26  
525 14         32 $prev_stack_el->{line} .= "\n" . $line . "$1|";
526 14         18 _update_lineno($prev_stack_el, $i);
527             }
528             }
529              
530             # Normal text
531             else {
532 530 100       788 $el->{text} = $line if $line;
533              
534 530         1392 push @$tape, $el;
535             }
536             }
537              
538             # Finalize multilines
539 144         757 for my $el (@multiline_el_queue) {
540 6         7 $el->{text} = join(" ", @{$el->{text}});
  6         40  
541             }
542             }
543              
544             # Updates lineno entry on the tape element
545             # for itens spanning more than one line
546             sub _update_lineno {
547 39     39   44 my ($el, $lineno) = @_;
548 39         27 $lineno++; # report line numbers starting at 1 instead of 0
549 39         184 $el->{lineno} =~ s/^(\d+)(?:-\d+)?/$1-$lineno/;
550 39         119 return;
551             }
552              
553             sub _open_implicit_brace {
554 775     775   612 my ($lines) = @_;
555 775 100 100     2172 if (scalar(@$lines) && $lines->[-1] eq '}') {
556 411         434 pop @$lines;
557             } else {
558 364         452 push @$lines, '{';
559             }
560             }
561              
562             sub _close_implicit_brace {
563 775     775   597 my ($lines) = @_;
564 775 100 66     2093 if (scalar(@$lines) && $lines->[-1] eq '{') {
565 95         88 pop @$lines;
566             } else {
567 680         776 push @$lines, '}';
568             }
569             }
570              
571             sub build {
572 125     125 1 114 my $self = shift;
573 125         209 my %vars = @_;
574              
575 125         88 my $code;
576              
577 125         214 my $ESCAPE = $self->escape;
578 125         262 $ESCAPE = <<"EOF";
579             no strict 'refs'; no warnings 'redefine';
580             sub escape;
581             *escape = sub {
582             $ESCAPE
583             };
584             use strict; use warnings;
585             EOF
586              
587 125         746 $ESCAPE =~ s/\n//g;
588              
589             # ensure namespace is set so that (for now) helpers
590             # can access outs & outs_raw (until we correctly allow
591             # helpers in `=` lines to capture their blocks eg. for `surrounds`
592            
593 125 100       229 if (! $self->namespace) {
594 24         82 $self->namespace(ref($self) . '::template');
595             }
596              
597 125         186 my $namespace = $self->namespace;
598 125         188 $code .= qq/package $namespace;/;
599              
600 125         204 $code .= qq/sub { my \$_H = ''; $ESCAPE; /;
601              
602 125         121 $code .= qq/my \$self = shift;/;
603 125         196 $code .= qq/\$${namespace}::__self = \$self;/;
604              
605 125         121 $code .= qq/my \%____vars = \@_;/;
606              
607 125         120 $code .= qq/no strict 'refs'; no warnings 'redefine';/;
608              
609             # using [1] since when called with arrow from namespace, [0] will be the namespace
610 125         178 $code .= qq/*${namespace}::outs = sub { \$_H .= escape(\$_[1]) };/;
611 125         162 $code .= qq/*${namespace}::outs_raw = sub { \$_H .= \$_[1] };/;
612 125         161 $code .= qq/*${namespace}::out_chomp = sub { chomp \$_H };/;
613              
614             # Install helpers
615 125         114 for my $name (sort keys %{$self->helpers}) {
  125         196  
616 11 50       25 next unless $name =~ m/^\w+$/;
617              
618 11   100     21 my $options = $self->{helpers_options}{$name} || {};
619              
620             # allow bareword helpers and block capturing with optional helper prototypes
621 11         10 my $prototype = $options->{prototype};
622 11 100       15 $prototype = defined $prototype ? "($prototype)" : '';
623              
624             # this option allows per-helper overriding of the helper_arg, important for builtin
625             # helpers to be safe in assuming the arg is self
626 11 100       14 my $helper_arg_code = $options->{arg_force_self} ? "\$${namespace}::__self" : "\$${namespace}::__self->helpers_arg";
627              
628             # sub must be defined inside BEGIN {} for the prototype to be ready before main helper code is
629             # compiled
630 11         15 $code .= "BEGIN { \*${namespace}::${name} = sub $prototype { ";
631 11         21 $code .= "\$${namespace}::__self->helpers->{'$name'}->($helper_arg_code, \@_) }; } ";
632             }
633              
634             # Install variables
635 125         228 foreach my $var (sort keys %vars) {
636 20 100       89 next unless $var =~ m/^\w+$/;
637 19 100       42 if ($self->vars_as_subs) {
638 2 50       4 next if $self->helpers->{$var};
639 2         9 $code
640             .= qq/sub $var() : lvalue; *$var = sub () : lvalue {\$____vars{'$var'}};/;
641             }
642             else {
643 17         51 $code .= qq/my \$$var = \$____vars{'$var'};/;
644             }
645             }
646              
647 125         128 $code .= qq/use strict; use warnings;/;
648              
649 125         235 $code .= $self->prepend;
650              
651 125         144 my $stack = [];
652              
653 125         151 my $output = '';
654 125         122 my @lines;
655 125         116 my $count = 0;
656 125         140 my $in_block = 0;
657             ELEM:
658 125         107 for my $el (@{$self->tape}) {
  125         203  
659 568         630 my $level = $el->{level};
660 568 100       745 $level -= 2 * $in_block if $in_block;
661              
662 568         405 my $offset = '';
663 568 100       832 $offset .= ' ' x $level if $level > 0;
664              
665 568         399 my $escape = '';
666 568 100 100     1433 if ( (!exists $el->{escape} && $self->escape_html)
      100        
      66        
667             || (exists $el->{escape} && $el->{escape} == 1))
668             {
669 522         445 $escape = 'escape';
670             }
671              
672 568         694 my $prev_el = $self->tape->[$count - 1];
673 568         623 my $next_el = $self->tape->[$count + 1];
674              
675 568         460 my $prev_stack_el = $stack->[-1];
676              
677 568 100 100     1378 if ($prev_stack_el && $prev_stack_el->{type} eq 'comment') {
678 25 100 100     60 if ( $el->{line}
679             && $prev_stack_el->{level} >= $el->{level})
680             {
681 8         7 pop @$stack;
682 8         8 undef $prev_stack_el;
683 8         9 _close_implicit_brace(\@lines);
684             }
685             else {
686 17         18 next ELEM;
687             }
688             }
689              
690 551 100 100     1952 if ( $el->{line}
      100        
691             && $prev_stack_el
692             && $prev_stack_el->{level} >= $el->{level})
693             {
694             STACKEDBLK:
695 67         132 while (my $poped = pop @$stack) {
696 85         71 my $level = $poped->{level};
697 85 100       121 $level -= 2 * $in_block if $in_block;
698 85 100       128 my $poped_offset = $level > 0 ? ' ' x $level : '';
699              
700 85         76 my $ending = '';
701 85 100       155 if ($poped->{type} eq 'tag') {
    50          
702 49         69 $ending .= "{name}>";
703             }
704             elsif ($poped->{type} eq 'html_comment') {
705 0 0       0 $ending .= "{if};
706 0         0 $ending .= "-->";
707             }
708              
709 85 100       135 if ($poped->{type} ne 'block') {
710 49         81 push @lines, qq|\$_H .= "$poped_offset$ending\n";|;
711             }
712              
713 85         111 _close_implicit_brace(\@lines);
714            
715 85 100       146 if ($poped->{type} eq 'block') {
716 36         45 _close_implicit_brace(\@lines);
717             }
718              
719 85 100       192 last STACKEDBLK if $poped->{level} == $el->{level};
720             }
721             }
722              
723              
724             SWITCH: {
725              
726 551 100       398 if ($el->{type} eq 'tag') {
  551         787  
727             my $ending =
728 219 100 100     471 $el->{autoclose} && $self->format eq 'xhtml' ? ' /' : '';
729              
730 219         174 my $attrs = '';
731 219 100       302 if ($el->{attrs}) {
732             ATTR:
733 27         36 for (my $i = 0; $i < @{$el->{attrs}}; $i += 2) {
  65         139  
734 38         44 my $name = $el->{attrs}->[$i];
735 38         45 my $value = $el->{attrs}->[$i + 1];
736 38         39 my $text = $value->{text};
737              
738 38 100       84 if ($name eq 'class') {
    100          
739 6   100     23 $el->{class} ||= [];
740 6 100       12 if ($value->{type} eq 'text') {
741 4         5 push @{$el->{class}}, $self->_parse_text($text);
  4         10  
742             }
743             else {
744 2         2 push @{$el->{class}}, qq/" . $text . "/;
  2         7  
745             }
746 6         12 next ATTR;
747             }
748             elsif ($name eq 'id') {
749 3   100     21 $el->{id} ||= '';
750 3 100       7 $el->{id} = $el->{id} . '_' if $el->{id};
751 3         9 $el->{id} .= $self->_parse_text($value->{text});
752 3         6 next ATTR;
753             }
754              
755 29 100 100     87 if ( $value->{type} eq 'text'
    100 33        
756             || $value->{type} eq 'expr')
757             {
758 26         30 $attrs .= ' ';
759 26         26 $attrs .= $name;
760 26         18 $attrs .= '=';
761              
762 26 100       45 if ($value->{type} eq 'text') {
763 25         48 $attrs
764             .= "'" . $self->_parse_text($text) . "'";
765             }
766             else {
767 1         3 $attrs .= qq/'" . $text . "'/;
768             }
769             }
770             elsif ($value->{type} eq 'boolean' && $value->{text})
771             {
772 2         3 $attrs .= ' ';
773 2         2 $attrs .= $name;
774 2 100       3 if ($self->format eq 'xhtml') {
775 1         2 $attrs .= '=';
776 1         3 $attrs .= qq/'$name'/;
777             }
778             }
779             } #end:for ATTR
780             }
781              
782 219         161 my $tail = '';
783 219 100       336 if ($el->{class}) {
784 28         29 $tail .= qq/ class='"./;
785 28         22 $tail .= qq/join(' ', sort(/;
786 28         23 $tail .= join(',', map {"\"$_\""} @{$el->{class}});
  35         70  
  28         41  
787 28         27 $tail .= qq/))/;
788 28         24 $tail .= qq/."'/;
789             }
790              
791 219 100       306 if ($el->{id}) {
792 14         30 $tail .= qq/ id='$el->{id}'/;
793             }
794              
795 219         373 $output .= qq|"$offset<$el->{name}$tail$attrs$ending>"|;
796              
797 219 100 66     1227 if ($el->{text} && $el->{expr}) {
    100 66        
    100 33        
    50          
798 17 100       30 if ($escape eq 'escape') {
799 13         33 $output .= '. ( do { my $ret = ' . qq/ $escape( do { $el->{text} } )/ . '; defined($ret) ? $ret : "" } )';
800 13         21 $output .= qq| . "{name}>"|;
801             } else {
802 4         7 $output .= '. ( do {' . $el->{text} . '} || "")';
803 4         6 $output .= qq| . "{name}>"|;
804             }
805             }
806             elsif ($el->{text}) {
807             $output .= qq/. $escape(/ . '"'
808 60         154 . $self->_parse_text($el->{text}) . '");';
809             $output .= qq|\$_H .= "{name}>"|
810 60 50       189 unless $el->{autoclose};
811             }
812             elsif (
813             !$next_el
814             || ( $next_el
815             && $next_el->{level} <= $el->{level})
816             )
817             {
818 28 100       68 $output .= qq|. "{name}>"| unless $el->{autoclose};
819             }
820             elsif (!$el->{autoclose}) {
821 114         111 push @$stack, $el;
822 114         159 _open_implicit_brace(\@lines);
823             }
824              
825 219         200 $output .= qq|. "\n"|;
826 219         164 $output .= qq|;|;
827 219         314 last SWITCH;
828             }
829              
830 332 100 100     891 if ($el->{line} && $el->{type} eq 'text') {
831 145         179 $output = qq/"$offset"/;
832              
833 145 50       237 $el->{text} = '' unless defined $el->{text};
834              
835 145 100       275 if ($el->{expr}) {
    50          
836 31         89 $output .= '. ( do { my $ret = ' . qq/ $escape( do { $el->{text} } )/ . '; defined($ret) ? $ret : "" } )';
837 31         36 $output .= qq/;\$_H .= "\n"/;
838             }
839             elsif ($el->{text}) {
840             $output
841             .= '.'
842             . qq/$escape / . '"'
843 114         248 . $self->_parse_text($el->{text}) . '"';
844 114         121 $output .= qq/. "\n"/;
845             }
846              
847 145         121 $output .= qq/;/;
848 145         202 last SWITCH;
849             }
850              
851 187 100       290 if ($el->{type} eq 'block') {
852 41         64 _open_implicit_brace(\@lines);
853 41         80 push @lines, ';' . $el->{text};
854 41         39 push @$stack, $el;
855 41         53 _open_implicit_brace(\@lines);
856              
857 41 100 66     151 if ($prev_el && $prev_el->{level} > $el->{level}) {
858 9         9 $in_block--;
859             }
860              
861 41 100 66     143 if ($next_el && $next_el->{level} > $el->{level}) {
862 14         10 $in_block++;
863             }
864 41         60 last SWITCH;
865             }
866              
867 146 100       250 if ($el->{type} eq 'html_comment') {
868 6         8 $output = qq/"$offset"/;
869              
870 6         5 $output .= qq/ . "\n"/;
876             }
877             else {
878 2         3 $output .= qq/. "\n"/;
879 2         2 push @$stack, $el;
880 2         4 _open_implicit_brace(\@lines);
881             }
882              
883 6         2 $output .= qq/;/;
884 6         10 last SWITCH;
885             }
886              
887 140 100       217 if ($el->{type} eq 'comment') {
888 9         10 push @$stack, $el;
889 9         10 _open_implicit_brace(\@lines);
890 9         11 last SWITCH;
891             }
892              
893 131 100       208 if ($el->{type} eq 'filter') {
894 10         14 my $filter = $self->filters->{$el->{name}};
895 10 50       17 die "unknown filter: $el->{name}" unless $filter;
896              
897 10 100       15 if ($el->{name} eq 'escaped') {
898             $output =
899             qq/escape "/
900             . $self->_parse_text($el->{text})
901 1         4 . qq/\n";/;
902             }
903             else {
904 9         16 $el->{text} = $filter->($el->{text});
905              
906 9         24 my $text = $self->_parse_text($el->{text});
907 9         25 $text =~ s/\\\n/\\n/g;
908 9         25 $output = qq/"/ . $text . qq/\n";/;
909             }
910 10         15 last SWITCH;
911             }
912              
913 121 50       214 unless ($el->{text}) {
914 121         159 last SWITCH;
915             }
916              
917 0         0 die "unknown type=" . $el->{type};
918              
919             } #end:SWITCH
920             } #end:ELEM
921             continue {
922              
923             # by bracing the content blocks, we will continue any existing block at the same level.
924             # this is important eg. if previously at this level the template has declared a `my`
925             # variable.
926            
927 568         719 _open_implicit_brace(\@lines);
928 568 100       1093 push @lines, '$_H .= ' . $output if $output;
929 568         707 _close_implicit_brace(\@lines);
930 568         446 $output = '';
931 568         638 $count++;
932             } #ELEM
933              
934 125         127 my $last_empty_line = 0;
935             $last_empty_line = 1
936 125 100 66     174 if $self->tape->[-1] && $self->tape->[-1]->{line} eq '';
937              
938             # Close remaining conten tblocks, last-seen first
939 125         198 foreach my $el (reverse @$stack) {
940 73         105 my $offset = ' ' x $el->{level};
941 73         63 my $ending = '';
942 73 100       122 if ($el->{type} eq 'tag') {
    100          
943 65         96 $ending = "{name}>";
944             }
945             elsif ($el->{type} eq 'html_comment') {
946 2 100       3 $ending .= '{if};
947 2         3 $ending .= "-->";
948             }
949              
950 73 100       183 push @lines, qq|\$_H .= "$offset$ending\n";| if $ending;
951              
952 73         99 _close_implicit_brace(\@lines);
953 73 100       188 if ($el->{type} eq 'block') {
954 5         8 _close_implicit_brace(\@lines);
955             }
956              
957             }
958              
959 125 100 66     466 if ($lines[-1] && !$last_empty_line) {
960             # usually (always?) there will be a closing '}' after the actual last .=
961 12 50 33     39 if ($lines[-2] && $lines[-1] eq '}') {
962 12         52 $lines[-2] =~ s/\n";$/";/;
963             } else {
964 0         0 $lines[-1] =~ s/\n";$/";/;
965             }
966             }
967              
968 125         370 $code .= join("\n", @lines);
969              
970 125         215 $code .= $self->append;
971              
972 125         127 $code .= q/return $_H; };/;
973              
974 125         216 $self->code($code);
975              
976 125         474 return $self;
977             }
978              
979             sub _parse_text {
980 216     216   190 my $self = shift;
981 216         177 my $text = shift;
982              
983 216         166 my $expr = 0;
984 216 50 33     461 if ($text =~ m/^\"/ && $text =~ m/\"$/) {
985 0         0 $text =~ s/^"//;
986 0         0 $text =~ s/"$//;
987 0         0 $expr = 1;
988             }
989              
990 216         773 $text =~ s/($UNESCAPE_RE)/$ESCAPE->{$1}/g;
991              
992 216         205 my $output = '';
993 216         157 while (1) {
994 243         161 my $t;
995 243         183 my $escape = 0;
996 243         168 my $found = 0;
997 243         151 my $variable;
998              
999 243         152 our $curly_brace_n;
1000 243         860 $curly_brace_n = qr/ (?> [^{}]+ | \{ (??{ $curly_brace_n }) \} )* /x;
1001              
1002 243 100       9048 if ($text =~ s/^(.*?)?(?
    100          
1003 25         22 $found = 1;
1004 25         30 $t = $1;
1005 25         39 $variable = $2;
1006             }
1007             elsif ($text =~ s/^(.*?)?\\\\(\#\{$curly_brace_n\})//xms) {
1008 2         3 $found = 1;
1009 2         9 $t = $1;
1010 2         3 $variable = $2;
1011 2         1 $escape = 1;
1012             }
1013              
1014 243 100       478 if ($t) {
1015 20         14 $t =~ s/\\\#/\#/g;
1016 20 50       40 $output .= $expr ? $t : quotemeta($t);
1017             }
1018              
1019 243 100       306 if ($found) {
1020 27         93 $variable =~ s/\#\{(.*)\}/$1/;
1021              
1022 27 100       41 my $prefix = $escape ? quotemeta("\\") : '';
1023 27         50 $output .= qq/$prefix".do { $variable }."/;
1024             }
1025             else {
1026 216         355 $text = $self->_parse_interpolation($text);
1027 216         290 $output .= $text;
1028 216         285 last;
1029             }
1030             }
1031              
1032 216 50       476 return $expr ? qq/$output/ : $output;
1033             }
1034              
1035             sub _parse_interpolation {
1036 216     216   227 my $self = shift;
1037 216         238 my ($text) = @_;
1038              
1039 216         160 my @parts;
1040              
1041 216         329 my $start_tag = qr{(?
1042 216         280 my $end_tag = qr{\}};
1043              
1044 216         389 pos $text = 0;
1045 216         451 while (pos $text < length $text) {
1046 212 50       1915 if ($text =~ m/\G $start_tag (.*?) $end_tag/xgcms) {
    50          
1047 0         0 push @parts, 'do {' . $1 . '}';
1048             }
1049             elsif ($text =~ m/\G (.*?) (?=$start_tag)/xgcms) {
1050 0         0 push @parts, 'qq{' . quotemeta($1) . '}';
1051             }
1052             else {
1053 212         315 my $leftover = substr($text, pos($text));
1054 212         403 push @parts, 'qq{' . quotemeta($leftover) . '}';
1055 212         280 last;
1056             }
1057             }
1058              
1059 216 100       382 return '' unless @parts;
1060              
1061 212         263 return '" . ' . join('.', map {s/\\\\#\\\{/#\\\{/; $_} @parts) . '."';
  212         229  
  212         804  
1062             }
1063              
1064             sub compile {
1065 125     125 1 120 my $self = shift;
1066              
1067 125         196 my $code = $self->code;
1068 125 50       203 return unless $code;
1069              
1070 30 50   30   140 my $compiled = eval $code;
  30     30   769  
  30     30   841  
  30     28   121  
  30     28   197  
  30     28   3046  
  30     28   103  
  30     28   573  
  28     26   495  
  28     26   113  
  28     26   200  
  28     25   1062  
  28     25   86  
  28     25   884  
  28     25   603  
  28     25   86  
  28     22   186  
  28     22   2028  
  28     22   119  
  28     21   285  
  27     21   367  
  28     21   87  
  28     21   160  
  27     21   2536  
  26     17   90  
  26     17   547  
  26     17   579  
  26     16   72  
  26     16   121  
  26     16   2240  
  26     16   85  
  26     16   469  
  25     14   386  
  25     14   69  
  25     14   180  
  25     12   882  
  25     12   66  
  25     12   688  
  25     12   477  
  25     12   67  
  25     12   113  
  25         1735  
  25         79  
  25         272  
  25         406  
  25         65  
  25         168  
  25         1920  
  22         178  
  21         482  
  21         438  
  22         94  
  22         113  
  21         1675  
  22         76  
  21         463  
  20         388  
  21         64  
  21         164  
  21         607  
  21         55  
  21         589  
  21         489  
  21         50  
  21         104  
  21         1261  
  21         103  
  21         247  
  21         417  
  21         136  
  21         125  
  21         1010  
  17         92  
  17         434  
  17         336  
  17         46  
  17         95  
  17         1242  
  17         49  
  17         328  
  16         314  
  16         118  
  15         104  
  15         427  
  16         76  
  15         441  
  15         229  
  16         77  
  15         84  
  15         966  
  16         74  
  16         179  
  15         183  
  16         66  
  16         97  
  15         737  
  14         60  
  14         312  
  13         284  
  14         40  
  14         89  
  14         1054  
  14         50  
  14         302  
  13         316  
  12         34  
  12         100  
  12         366  
  12         34  
  12         433  
  12         267  
  12         35  
  12         70  
  12         768  
  12         36  
  12         144  
  12         173  
  12         28  
  12         74  
  12         500  
  125         9296  
  12         19  
  21         25  
  44         56  
  48         60  
  48         52  
  48         34  
  48         43  
  48         46  
  43         48  
  29         38  
  5         11  
1071              
1072 125 100       322 if ($@) {
1073 2         6 $self->error($@);
1074 2         9 return undef;
1075             }
1076              
1077 123         282 $self->compiled($compiled);
1078              
1079 123         313 return $self;
1080             }
1081              
1082             sub interpret {
1083 125     125 1 125 my $self = shift;
1084              
1085 125         170 my $compiled = $self->compiled;
1086              
1087 125         142 my $output = eval { $compiled->($self, @_) };
  125         2999  
1088              
1089 125 100       258 if ($@) {
1090 1         3 $self->error($@);
1091 1         6 return undef;
1092             }
1093              
1094 124         305 return $output;
1095             }
1096              
1097             sub render {
1098 125     125 1 31923 my $self = shift;
1099 125         139 my $tmpl = shift;
1100              
1101             # Parse
1102 125         266 $self->parse($tmpl);
1103              
1104             # Build
1105 125 50       318 return unless defined $self->build(@_);
1106              
1107             # Compile
1108 125 100       204 $self->compile || return undef;
1109              
1110             # Interpret
1111 123         256 return $self->interpret(@_);
1112             }
1113              
1114             # For templates in __DATA__ section
1115             sub _eq_checksum {
1116 1     1   2 my $self = shift;
1117              
1118             # Exit if not virtual path
1119 1 50       2 return 0 unless ref $self->fullpath eq 'SCALAR';
1120              
1121 0 0       0 return 1 if $self->cache == 2;
1122 0 0       0 return 0 if $self->cache == 0;
1123              
1124 0         0 my $fullpath = $self->fullpath;
1125 0         0 $fullpath = $$fullpath;
1126              
1127 0         0 my $file = IO::File->new;
1128 0 0       0 $file->open($self->cache_path, 'r') or return;
1129 0         0 $file->sysread(my $cache_md5_checksum, 33); # 33 = # + hashsum
1130 0         0 $file->close;
1131              
1132 0         0 my $orig_md5_checksum = '#'.$self->_digest($fullpath);
1133              
1134 0         0 return $cache_md5_checksum eq $orig_md5_checksum;
1135             }
1136              
1137             sub _digest {
1138 1     1   2 my ($self, $content) = @_;
1139              
1140 1         9 my $md5 = Digest::MD5->new();
1141 1 50       4 $content = decode($self->encoding, $content) if $self->encoding;
1142 1         41 $md5->add($content);
1143 1         6 return $md5->hexdigest();
1144             }
1145              
1146             sub render_file {
1147 8     8 1 2489 my $self = shift;
1148 8         11 my $path = shift;
1149              
1150             # Set file fullpath
1151 8         20 $self->_fullpath($path);
1152              
1153 8 100       18 if ($self->cache >= 1) {
1154             # Make cache directory
1155 6         13 my $cache_dir = $self->_cache_dir;
1156             # Set cache path
1157 6         17 $self->_cache_path($path, $cache_dir);
1158              
1159             # Exists same cache file?
1160 6 100 66     11 if (-e $self->cache_path && ($self->_eq_mtime || $self->_eq_checksum)) {
      66        
1161 2         6 return $self->_interpret_cached(@_);
1162             }
1163             }
1164              
1165 6         11 my $content = '';
1166 6         33 my $file = IO::File->new;
1167 6 100       153 if (ref $self->fullpath eq 'SCALAR') { # virtual path
1168 1         2 $content = $self->fullpath;
1169 1         2 $content = $$content;
1170             } else {
1171             # Open file
1172 5 50       11 $file->open($self->fullpath, 'r') or die "Can't open template '$path': $!";
1173              
1174             # Slurp file
1175 5         272 while ($file->sysread(my $buffer, CHUNK_SIZE, 0)) {
1176 5         79 $content .= $buffer;
1177             }
1178 5         39 $file->close;
1179             }
1180              
1181 6         51 $content =~ s/\r//g;
1182              
1183             # Encoding
1184 6 50       14 $content = decode($self->encoding, $content) if $self->encoding;
1185              
1186             # Render
1187 6         559 my $output;
1188 6 50       18 if ($output = $self->render($content, @_)) {
1189 6 100       15 if ($self->cache >= 1) {
1190             # Create cache
1191 4 50       9 if ($file->open($self->cache_path, 'w')) {
1192 4         432 binmode $file, ':utf8';
1193              
1194 4 100       11 if (ref $self->fullpath eq 'SCALAR') {
1195 1         4 my $md5_checksum = $self->_digest($content);
1196 1         5 print $file '#'.$md5_checksum."\n".$self->code; # Write with file checksum (virtual path)
1197             } else {
1198 3         6 my $mtime = (stat($self->fullpath))[9];
1199 3         11 print $file '#'.$mtime."\n".$self->code; # Write with file mtime
1200             }
1201              
1202 4         24 $file->close;
1203             }
1204             }
1205             }
1206              
1207 6         160 return $output;
1208             }
1209              
1210             sub _fullpath {
1211 8     8   8 my $self = shift;
1212 8         7 my $path = shift;
1213              
1214 8 100 66     84 if (File::Spec->file_name_is_absolute($path) and -r $path) {
1215 1         3 $self->fullpath($path);
1216 1         1 return;
1217             }
1218              
1219 7         9 for my $p (@{$self->path}) {
  7         16  
1220 7 100       15 if (ref $p eq 'HASH') { # virtual path
1221 1 50       3 if (defined(my $content = $p->{$path})) {
1222 1         3 $self->fullpath(\$content);
1223 1         1 return;
1224             }
1225             } else {
1226 6         62 my $fullpath = File::Spec->catfile($p, $path);
1227 6 50       139 if (-r $fullpath) { # is readable ?
1228 6         14 $self->fullpath($fullpath);
1229 6         9 return;
1230             }
1231             }
1232             }
1233              
1234 0         0 Carp::croak("Can't find template '$path'");
1235             }
1236              
1237             sub _cache_dir {
1238 6     6   8 my $self = shift;
1239              
1240 6 100       8 my $cache_prefix = (ref $self->fullpath eq 'SCALAR')
1241             ? 'HASH'
1242             : URI::Escape::uri_escape(
1243             File::Basename::dirname($self->fullpath)
1244             );
1245              
1246 6         123 my $cache_dir = File::Spec->catdir(
1247             $self->cache_dir,
1248             $cache_prefix,
1249             );
1250              
1251 6 100       106 if (not -e $cache_dir) {
1252 3         16 require File::Path;
1253 3         3 eval { File::Path::mkpath($cache_dir) };
  3         371  
1254 3 50       10 Carp::carp("Can't mkpath '$cache_dir': $@") if $@;
1255             }
1256              
1257 6         9 return $cache_dir;
1258             }
1259              
1260             sub _cache_path {
1261 6     6   6 my $self = shift;
1262 6         6 my $path = shift;
1263 6         12 my $cache_dir = shift;
1264              
1265 6         174 $self->cache_path(File::Spec->catfile(
1266             $cache_dir,
1267             File::Basename::basename($path).'.pl',
1268             ));
1269             }
1270              
1271             sub _eq_mtime {
1272 3     3   3 my $self = shift;
1273              
1274             # Exit if virtual path
1275 3 50       6 return 0 if ref $self->fullpath eq 'SCALAR';
1276              
1277 3 100       5 return 1 if $self->cache == 2;
1278 1 50       2 return 0 if $self->cache == 0;
1279              
1280 1         8 my $file = IO::File->new;
1281 1 50       30 $file->open($self->cache_path, 'r') or return;
1282 1         48 $file->sysread(my $cache_mtime, length('#xxxxxxxxxx'));
1283 1         9 $file->close;
1284 1         9 my $orig_mtime = '#'.(stat($self->fullpath))[9];
1285              
1286 1         8 return $cache_mtime eq $orig_mtime;
1287             }
1288              
1289             sub _interpret_cached {
1290 2     2   3 my $self = shift;
1291              
1292 2         5 my $compiled = do $self->cache_path;
1293 2         6 $self->compiled($compiled);
1294 2         6 return $self->interpret(@_);
1295             }
1296              
1297             sub _doctype {
1298 16     16   16 my $self = shift;
1299 16         31 my ($type, $encoding) = @_;
1300              
1301 16   100     41 $type ||= '';
1302 16   100     48 $encoding ||= 'utf-8';
1303              
1304 16         20 $type = lc $type;
1305              
1306 16 100       31 if ($type eq 'xml') {
1307 2 50       4 return '' if $self->format eq 'html5';
1308 2 50       5 return '' if $self->format eq 'html4';
1309              
1310 2         8 return qq||;
1311             }
1312              
1313 14 100       23 if ($self->format eq 'xhtml') {
    100          
    50          
1314 10 100       53 if ($type eq 'strict') {
    100          
    100          
    100          
    100          
    100          
1315             return
1316 1         4 q||;
1317             }
1318             elsif ($type eq 'frameset') {
1319             return
1320 1         2 q||;
1321             }
1322             elsif ($type eq '5') {
1323 1         3 return '';
1324             }
1325             elsif ($type eq '1.1') {
1326             return
1327 1         2 q||;
1328             }
1329             elsif ($type eq 'basic') {
1330             return
1331 1         3 q||;
1332             }
1333             elsif ($type eq 'mobile') {
1334             return
1335 1         2 q||;
1336             }
1337             else {
1338             return
1339 4         10 q||;
1340             }
1341             }
1342             elsif ($self->format eq 'html4') {
1343 3 100       8 if ($type eq 'strict') {
    100          
1344             return
1345 1         3 q||;
1346             }
1347             elsif ($type eq 'frameset') {
1348             return
1349 1         2 q||;
1350             }
1351             else {
1352             return
1353 1         2 q||;
1354             }
1355             }
1356             elsif ($self->format eq 'html5') {
1357 1         3 return '';
1358             }
1359              
1360 0           return '';
1361             }
1362              
1363             1;
1364             __END__