File Coverage

lib/Template/VMethods.pm
Criterion Covered Total %
statement 253 262 96.5
branch 91 122 74.5
condition 24 36 66.6
subroutine 74 76 97.3
pod 0 64 0.0
total 442 560 78.9


line stmt bran cond sub pod time code
1             #============================================================= -*-Perl-*-
2             #
3             # Template::VMethods
4             #
5             # DESCRIPTION
6             # Module defining virtual methods for the Template Toolkit
7             #
8             # AUTHOR
9             # Andy Wardley
10             #
11             # COPYRIGHT
12             # Copyright (C) 1996-2015 Andy Wardley. All Rights Reserved.
13             #
14             # This module is free software; you can redistribute it and/or
15             # modify it under the same terms as Perl itself.
16             #
17             # REVISION
18             # $Id$
19             #
20             #============================================================================
21              
22             package Template::VMethods;
23              
24 84     84   277 use strict;
  84         849  
  84         1807  
25 84     84   234 use warnings;
  84         84  
  84         1816  
26 84     84   251 use Scalar::Util qw( blessed looks_like_number );
  84         85  
  84         3621  
27 84     84   3894 use Template::Filters;
  84         77  
  84         31221  
28             require Template::Stash;
29              
30             our $VERSION = 2.16;
31             our $DEBUG = 0 unless defined $DEBUG;
32             our $PRIVATE = $Template::Stash::PRIVATE;
33              
34             our $ROOT_VMETHODS = {
35             inc => \&root_inc,
36             dec => \&root_dec,
37             };
38              
39             our $TEXT_VMETHODS = {
40             item => \&text_item,
41             list => \&text_list,
42             hash => \&text_hash,
43             length => \&text_length,
44             size => \&text_size,
45             empty => \&text_empty,
46             defined => \&text_defined,
47             upper => \&text_upper,
48             lower => \&text_lower,
49             ucfirst => \&text_ucfirst,
50             lcfirst => \&text_lcfirst,
51             match => \&text_match,
52             search => \&text_search,
53             repeat => \&text_repeat,
54             replace => \&text_replace,
55             remove => \&text_remove,
56             split => \&text_split,
57             chunk => \&text_chunk,
58             substr => \&text_substr,
59             trim => \&text_trim,
60             collapse => \&text_collapse,
61             squote => \&text_squote,
62             dquote => \&text_dquote,
63             html => \&Template::Filters::html_filter,
64             xml => \&Template::Filters::xml_filter,
65             };
66              
67             our $HASH_VMETHODS = {
68             item => \&hash_item,
69             hash => \&hash_hash,
70             size => \&hash_size,
71             empty => \&hash_empty,
72             each => \&hash_each,
73             keys => \&hash_keys,
74             values => \&hash_values,
75             items => \&hash_items,
76             pairs => \&hash_pairs,
77             list => \&hash_list,
78             exists => \&hash_exists,
79             defined => \&hash_defined,
80             delete => \&hash_delete,
81             import => \&hash_import,
82             sort => \&hash_sort,
83             nsort => \&hash_nsort,
84             };
85              
86             our $LIST_VMETHODS = {
87             item => \&list_item,
88             list => \&list_list,
89             hash => \&list_hash,
90             push => \&list_push,
91             pop => \&list_pop,
92             unshift => \&list_unshift,
93             shift => \&list_shift,
94             max => \&list_max,
95             size => \&list_size,
96             empty => \&list_empty,
97             defined => \&list_defined,
98             first => \&list_first,
99             last => \&list_last,
100             reverse => \&list_reverse,
101             grep => \&list_grep,
102             join => \&list_join,
103             sort => \&list_sort,
104             nsort => \&list_nsort,
105             unique => \&list_unique,
106             import => \&list_import,
107             merge => \&list_merge,
108             slice => \&list_slice,
109             splice => \&list_splice,
110             };
111              
112              
113             #========================================================================
114             # root virtual methods
115             #========================================================================
116              
117             sub root_inc {
118 84     84   360 no warnings;
  84         95  
  84         4554  
119 4     4 0 90 my $item = shift;
120 4         17 ++$item;
121             }
122              
123             sub root_dec {
124 84     84   272 no warnings;
  84         137  
  84         121797  
125 0     0 0 0 my $item = shift;
126 0         0 --$item;
127             }
128              
129              
130             #========================================================================
131             # text virtual methods
132             #========================================================================
133              
134             sub text_item {
135 6     6 0 45 $_[0];
136             }
137              
138             sub text_list {
139 5     5 0 46 [ $_[0] ];
140             }
141              
142             sub text_hash {
143 3     3 0 29 { value => $_[0] };
144             }
145              
146             sub text_length {
147 3     3 0 9 length $_[0];
148             }
149              
150             sub text_size {
151 1     1 0 2 return 1;
152             }
153              
154             sub text_empty {
155 2 100   2 0 4 return 0 == text_length($_[0]) ? 1 : 0;
156             }
157              
158             sub text_defined {
159 4     4 0 7 return 1;
160             }
161              
162             sub text_upper {
163 1     1 0 3 return uc $_[0];
164             }
165              
166             sub text_lower {
167 2     2 0 5 return lc $_[0];
168             }
169              
170             sub text_ucfirst {
171 3     3 0 6 return ucfirst $_[0];
172             }
173              
174             sub text_lcfirst {
175 2     2 0 6 return lcfirst $_[0];
176             }
177              
178             sub text_trim {
179 1     1 0 2 for ($_[0]) {
180 1         4 s/^\s+//;
181 1         5 s/\s+$//;
182             }
183 1         2 return $_[0];
184             }
185              
186             sub text_collapse {
187 1     1 0 2 for ($_[0]) {
188 1         3 s/^\s+//;
189 1         5 s/\s+$//;
190 1         5 s/\s+/ /g
191             }
192 1         3 return $_[0];
193             }
194              
195             sub text_match {
196 10     10 0 10 my ($str, $search, $global) = @_;
197 10 50 33     30 return $str unless defined $str and defined $search;
198 10 100       125 my @matches = $global ? ($str =~ /$search/g)
199             : ($str =~ /$search/);
200 10 100       32 return @matches ? \@matches : '';
201             }
202              
203             sub text_search {
204 5     5 0 7 my ($str, $pattern) = @_;
205 5 50 33     19 return $str unless defined $str and defined $pattern;
206 5         65 return $str =~ /$pattern/;
207             }
208              
209             sub text_repeat {
210 2     2 0 2 my ($str, $count) = @_;
211 2 50       6 $str = '' unless defined $str;
212 2 50       4 return '' unless $count;
213 2   50     3 $count ||= 1;
214 2         7 return $str x $count;
215             }
216              
217             sub text_replace {
218 25     25 0 28 my ($text, $pattern, $replace, $global) = @_;
219 25 50       36 $text = '' unless defined $text;
220 25 50       32 $pattern = '' unless defined $pattern;
221 25 50       32 $replace = '' unless defined $replace;
222 25 50       40 $global = 1 unless defined $global;
223              
224 25 100       65 if ($replace =~ /\$\d+/) {
225             # replacement string may contain backrefs
226             my $expand = sub {
227 35     35   39 my ($chunk, $start, $end) = @_;
228 35         127 $chunk =~ s{ \\(\\|\$) | \$ (\d+) }{
229 66 100 100     526 $1 ? $1
    100          
230             : ($2 > $#$start || $2 == 0 || !defined $start->[$2]) ? ''
231             : substr($text, $start->[$2], $end->[$2] - $start->[$2]);
232             }exg;
233 35         133 $chunk;
234 17         61 };
235 17 50       23 if ($global) {
236 17         293 $text =~ s{$pattern}{ &$expand($replace, [@-], [@+]) }eg;
  35         215  
237             }
238             else {
239 0         0 $text =~ s{$pattern}{ &$expand($replace, [@-], [@+]) }e;
  0         0  
240             }
241             }
242             else {
243 8 50       12 if ($global) {
244 8         96 $text =~ s/$pattern/$replace/g;
245             }
246             else {
247 0         0 $text =~ s/$pattern/$replace/;
248             }
249             }
250 25         106 return $text;
251             }
252              
253             sub text_remove {
254 1     1 0 2 my ($str, $search) = @_;
255 1 50 33     6 return $str unless defined $str and defined $search;
256 1         14 $str =~ s/$search//g;
257 1         3 return $str;
258             }
259              
260             sub text_split {
261 11     11 0 83 my ($str, $split, $limit) = @_;
262 11 50       30 $str = '' unless defined $str;
263              
264             # For versions of Perl prior to 5.18 we have to be very careful about
265             # spelling out each possible combination of arguments because split()
266             # is very sensitive to them, for example C behaves
267             # differently to C<$space=' '; split($space, ...)>. Test 33 of
268             # vmethods/text.t depends on this behaviour.
269              
270 11 50       29 if ($] < 5.018) {
271 0 0       0 if (defined $limit) {
272 0 0       0 return [ defined $split
273             ? split($split, $str, $limit)
274             : split(' ', $str, $limit) ];
275             }
276             else {
277 0 0       0 return [ defined $split
278             ? split($split, $str)
279             : split(' ', $str) ];
280             }
281             }
282              
283             # split's behavior changed in Perl 5.18.0 making this:
284             # C<$space=' '; split($space, ...)>
285             # behave the same as this:
286             # C
287             # qr// behaves the same, so use that for user-defined split.
288              
289 11         11 my $split_re;
290 11 100       21 if (defined $split) {
291 8         11 eval {
292 8         62 $split_re = qr/$split/;
293             };
294             }
295 11 100       24 $split_re = ' ' unless defined $split_re;
296 11   100     32 $limit ||= 0;
297 11         133 return [split($split_re, $str, $limit)];
298             }
299              
300             sub text_chunk {
301 8     8 0 87 my ($string, $size) = @_;
302 8         10 my @list;
303 8   50     15 $size ||= 1;
304 8 100       16 if ($size < 0) {
305             # sexeger! It's faster to reverse the string, search
306             # it from the front and then reverse the output than to
307             # search it from the end, believe it nor not!
308 5         13 $string = reverse $string;
309 5         9 $size = -$size;
310 5         133 unshift(@list, scalar reverse $1)
311             while ($string =~ /((.{$size})|(.+))/g);
312             }
313             else {
314 3         81 push(@list, $1) while ($string =~ /((.{$size})|(.+))/g);
315             }
316 8         43 return \@list;
317             }
318              
319             sub text_substr {
320 11     11 0 12 my ($text, $offset, $length, $replacement) = @_;
321 11   100     30 $offset ||= 0;
322              
323 11 100       15 if(defined $length) {
324 9 100       12 if (defined $replacement) {
325 2         6 substr( $text, $offset, $length, $replacement );
326 2         5 return $text;
327             }
328             else {
329 7         48 return substr( $text, $offset, $length );
330             }
331             }
332             else {
333 2         6 return substr( $text, $offset );
334             }
335             }
336              
337             sub text_squote {
338 1     1 0 1 my $text = shift;
339 1         2 for ($text) {
340 1         7 s/(['\\])/\\$1/g;
341             }
342 1         3 return $text;
343             }
344              
345             sub text_dquote {
346 1     1 0 2 my $text = shift;
347 1         2 for ($text) {
348 1         7 s/(["\\])/\\$1/g;
349 1         2 s/\n/\\n/g;
350             }
351 1         4 return $text;
352             }
353              
354             #========================================================================
355             # hash virtual methods
356             #========================================================================
357              
358              
359             sub hash_item {
360 2     2 0 38 my ($hash, $item) = @_;
361 2 50       5 $item = '' unless defined $item;
362 2 50 33     7 return if $PRIVATE && $item =~ /$PRIVATE/;
363 2         6 $hash->{ $item };
364             }
365              
366             sub hash_hash {
367 0     0 0 0 $_[0];
368             }
369              
370             sub hash_size {
371 3     3 0 2 scalar keys %{$_[0]};
  3         11  
372             }
373              
374             sub hash_empty {
375 2 100   2 0 6 return 0 == hash_size($_[0]) ? 1 : 0;
376             }
377              
378             sub hash_each {
379             # this will be changed in TT3 to do what hash_pairs() does
380 1     1 0 2 [ %{ $_[0] } ];
  1         3  
381             }
382              
383             sub hash_keys {
384 6     6 0 4 [ keys %{ $_[0] } ];
  6         25  
385             }
386              
387             sub hash_values {
388 1     1 0 3 [ values %{ $_[0] } ];
  1         4  
389             }
390              
391             sub hash_items {
392 7     7 0 50 [ %{ $_[0] } ];
  7         39  
393             }
394              
395             sub hash_pairs {
396             [ map {
397 12         35 { key => $_ , value => $_[0]->{ $_ } }
398             }
399 4     4 0 4 sort keys %{ $_[0] }
  4         22  
400             ];
401             }
402              
403             sub hash_list {
404 1     1 0 2 my ($hash, $what) = @_;
405 1   50     4 $what ||= '';
406             return ($what eq 'keys') ? [ keys %$hash ]
407             : ($what eq 'values') ? [ values %$hash ]
408             : ($what eq 'each') ? [ %$hash ]
409             : # for now we do what pairs does but this will be changed
410             # in TT3 to return [ $hash ] by default
411 1 50       8 [ map { { key => $_ , value => $hash->{ $_ } } }
  2 50       7  
    50          
412             sort keys %$hash
413             ];
414             }
415              
416             sub hash_exists {
417 2     2 0 5 exists $_[0]->{ $_[1] };
418             }
419              
420             sub hash_defined {
421             # return the item requested, or 1 if no argument
422             # to indicate that the hash itself is defined
423 5     5 0 6 my $hash = shift;
424 5 100       15 return @_ ? defined $hash->{ $_[0] } : 1;
425             }
426              
427             sub hash_delete {
428 2     2 0 17 my $hash = shift;
429 2         11 delete $hash->{ $_ } for @_;
430             }
431              
432             sub hash_import {
433 19     19 0 152 my ($hash, $imp) = @_;
434 19 100       44 $imp = {} unless ref $imp eq 'HASH';
435 19         49 @$hash{ keys %$imp } = values %$imp;
436 19         47 return '';
437             }
438              
439             sub hash_sort {
440 1     1 0 3 my ($hash) = @_;
441 1         6 [ sort { lc $hash->{$a} cmp lc $hash->{$b} } (keys %$hash) ];
  3         7  
442             }
443              
444             sub hash_nsort {
445 1     1 0 2 my ($hash) = @_;
446 1         8 [ sort { $hash->{$a} <=> $hash->{$b} } (keys %$hash) ];
  3         7  
447             }
448              
449              
450             #========================================================================
451             # list virtual methods
452             #========================================================================
453              
454              
455             sub list_item {
456 8   100 8 0 94 $_[0]->[ $_[1] || 0 ];
457             }
458              
459             sub list_list {
460 2     2 0 16 $_[0];
461             }
462              
463             sub list_hash {
464 5     5 0 20 my $list = shift;
465 5 100       16 if (@_) {
466 2   100     6 my $n = shift || 0;
467 2         3 return { map { ($n++, $_) } @$list };
  8         22  
468             }
469 84     84   410 no warnings;
  84         110  
  84         9424  
470 3         14 return { @$list };
471             }
472              
473             sub list_push {
474 13     13 0 154 my $list = shift;
475 13         24 push(@$list, @_);
476 13         34 return '';
477             }
478              
479             sub list_pop {
480 1     1 0 13 my $list = shift;
481 1         4 pop(@$list);
482             }
483              
484             sub list_unshift {
485 1     1 0 14 my $list = shift;
486 1         2 unshift(@$list, @_);
487 1         3 return '';
488             }
489              
490             sub list_shift {
491 2     2 0 51 my $list = shift;
492 2         6 shift(@$list);
493             }
494              
495             sub list_max {
496 84     84   324 no warnings;
  84         110  
  84         3432  
497 1     1 0 2 my $list = shift;
498 1         3 $#$list;
499             }
500              
501             sub list_size {
502 84     84   287 no warnings;
  84         128  
  84         64245  
503 6     6 0 10 my $list = shift;
504 6         15 $#$list + 1;
505             }
506              
507             sub list_empty {
508 2 100   2 0 4 return 0 == list_size($_[0]) ? 1 : 0;
509             }
510              
511             sub list_defined {
512             # return the item requested, or 1 if no argument to
513             # indicate that the hash itself is defined
514 4     4 0 4 my $list = shift;
515 4 100       9 return 1 unless @_; # list.defined is always true
516 3 100       10 return unless looks_like_number $_[0]; # list.defined('bah') is always false
517 2         5 return defined $list->[$_[0]]; # list.defined(n)
518             }
519              
520             sub list_first {
521 24     24 0 180 my $list = shift;
522 24 100       101 return $list->[0] unless @_;
523 1         5 return [ @$list[0..$_[0]-1] ];
524             }
525              
526             sub list_last {
527 25     25 0 169 my $list = shift;
528 25 100       175 return $list->[-1] unless @_;
529 1         5 return [ @$list[-$_[0]..-1] ];
530             }
531              
532             sub list_reverse {
533 1     1 0 2 my $list = shift;
534 1         4 [ reverse @$list ];
535             }
536              
537             sub list_grep {
538 2     2 0 25 my ($list, $pattern) = @_;
539 2   50     3 $pattern ||= '';
540 2         41 return [ grep /$pattern/, @$list ];
541             }
542              
543             sub list_join {
544 57     57 0 62 my ($list, $joint) = @_;
545             join(defined $joint ? $joint : ' ',
546 57 100       102 map { defined $_ ? $_ : '' } @$list);
  248 50       438  
547             }
548              
549             sub _list_sort_make_key {
550 184     184   131 my ($item, $fields) = @_;
551 184         110 my @keys;
552              
553 184 100       298 if (ref($item) eq 'HASH') {
    100          
554 34         27 @keys = map { $item->{ $_ } } @$fields;
  34         60  
555             }
556             elsif (blessed $item) {
557 28 50       23 @keys = map { $item->can($_) ? $item->$_() : $item } @$fields;
  32         89  
558             }
559             else {
560 122         104 @keys = $item;
561             }
562              
563             # ugly hack to generate a single string using a delimiter that is
564             # unlikely (but not impossible) to be found in the wild.
565 184 50       203 return lc join('/*^UNLIKELY^*/', map { defined $_ ? $_ : '' } @keys);
  188         457  
566             }
567              
568             sub list_sort {
569 55     55 0 640 my ($list, @fields) = @_;
570 55 100       174 return $list unless @$list > 1; # no need to sort 1 item lists
571             return [
572             @fields # Schwartzian Transform
573 46         70 ? map { $_->[0] } # for case insensitivity
574 48         53 sort { $a->[1] cmp $b->[1] }
575 46         61 map { [ $_, _list_sort_make_key($_, \@fields) ] }
576             @$list
577 135         295 : map { $_->[0] }
578 156         185 sort { $a->[1] cmp $b->[1] }
579 52 100       139 map { [ $_, lc $_ ] }
  135         351  
580             @$list,
581             ];
582             }
583              
584             sub list_nsort {
585 6     6 0 40 my ($list, @fields) = @_;
586 6 50       18 return $list unless @$list > 1; # no need to sort 1 item lists
587              
588             my $sort = sub {
589 69     69   43 my $cmp;
590              
591 69 100       65 if(@fields) {
592             # compare each field individually
593 8         4 for my $field (@fields) {
594 8         15 my $A = _list_sort_make_key($a, [ $field ]);
595 8         10 my $B = _list_sort_make_key($b, [ $field ]);
596 8 50       18 ($cmp = $A <=> $B) and last;
597             }
598             }
599             else {
600 61         62 my $A = _list_sort_make_key($a);
601 61         60 my $B = _list_sort_make_key($b);
602 61         83 $cmp = $A <=> $B;
603             }
604              
605 69         117 $cmp;
606 6         25 };
607              
608 6         10 return [ sort $sort @{ $list } ];
  6         21  
609             }
610              
611             sub list_unique {
612 2     2 0 1 my %u;
613 2         3 [ grep { ++$u{$_} == 1 } @{$_[0]} ];
  20         32  
  2         5  
614             }
615              
616             sub list_import {
617 2     2 0 2 my $list = shift;
618 2 50       15 push(@$list, grep defined, map ref eq 'ARRAY' ? @$_ : undef, @_);
619 2         5 return $list;
620             }
621              
622             sub list_merge {
623 2     2 0 3 my $list = shift;
624 2 50       15 return [ @$list, grep defined, map ref eq 'ARRAY' ? @$_ : undef, @_ ];
625             }
626              
627             sub list_slice {
628 4     4 0 4 my ($list, $from, $to) = @_;
629 4   100     11 $from ||= 0;
630 4 100       5 $to = $#$list unless defined $to;
631 4 100       9 $from += @$list if $from < 0;
632 4 100       6 $to += @$list if $to < 0;
633 4         12 return [ @$list[$from..$to] ];
634             }
635              
636             sub list_splice {
637 7     7 0 11 my ($list, $offset, $length, @replace) = @_;
638 7 100       18 if (@replace) {
    100          
    100          
639             # @replace can contain a list of multiple replace items, or
640             # be a single reference to a list
641 4 100 100     17 @replace = @{ $replace[0] }
  2         7  
642             if @replace == 1 && ref $replace[0] eq 'ARRAY';
643 4         18 return [ splice @$list, $offset, $length, @replace ];
644             }
645             elsif (defined $length) {
646 1         4 return [ splice @$list, $offset, $length ];
647             }
648             elsif (defined $offset) {
649 1         3 return [ splice @$list, $offset ];
650             }
651             else {
652 1         5 return [ splice(@$list) ];
653             }
654             }
655              
656             1;
657              
658             __END__