File Coverage

blib/lib/Template/Liquid/Filters.pm
Criterion Covered Total %
statement 212 226 93.8
branch 98 150 65.3
condition 35 57 61.4
subroutine 53 53 100.0
pod 50 50 100.0
total 448 536 83.5


line stmt bran cond sub pod time code
1             package Template::Liquid::Filters;
2             our $VERSION = '1.0.23';
3 25     25   183 use strict;
  25         65  
  25         742  
4 25     25   157 use warnings;
  25         53  
  25         107955  
5              
6             sub import {
7 25     25   133 Template::Liquid::register_filter(
8             qw[
9             abs append at_least at_most
10             capitalize ceil compact concat
11             date default divided_by downcase
12             escape escape_once
13             first floor
14             join
15             last lstrip
16             map minus modulo
17             newline_to_br
18             plus prepend
19             remove remove_first replace replace_first reverse round rstrip
20             size slice sort sort_natural split strip strip_html strip_newlines
21             times truncate truncatewords
22             uniq upcase url_decode url_encode
23             where
24             money stock_price
25             ]
26             );
27             }
28              
29             =pod
30              
31             =encoding UTF-8
32              
33             =begin stopwords
34              
35             Lütke jadedPixel truthy html newlines endcapture vs
36              
37             =end stopwords
38              
39             =head1 NAME
40              
41             Template::Liquid::Filters - Default Filters Based on Liquid's Standard Set
42              
43             =head1 Synopsis
44              
45             Filters are simple methods that modify the output of numbers, strings,
46             variables and objects. They are placed within an output tag C<{{ }}> and are
47             denoted by a pipe character C<|>.
48              
49             # product.title = "Awesome Shoes"
50             {{ product.title | upcase }}
51             # Output: AWESOME SHOES
52              
53             In the example above, C<product> is the object, C<title> is its attribute, and
54             C<upcase> is the filter being applied.
55              
56             Some filters require a parameter to be passed.
57              
58             {{ product.title | remove: "Awesome" }}
59             # Output: Shoes
60              
61             Multiple filters can be used on one output. They are applied from left to
62             right.
63              
64             {{ product.title | upcase | remove: "AWESOME" }}
65             # SHOES
66              
67              
68             =head1 Standard Filters
69              
70             These are the current default filters. They have been written to behave exactly
71             like their Ruby Liquid counterparts accept where Perl makes improvement
72             irresistible.
73              
74             =head2 C<abs>
75              
76             Returns the absolute value of a number.
77              
78             {{ 4 | abs }} => 4
79             {{ -4 | abs }} => 4
80              
81             =cut
82              
83 2     2 1 6 sub abs { CORE::abs($_[0]) }
84              
85             =head2 C<append>
86              
87             Append a string.
88              
89             {{ 'foo' | append:'bar' }} => 'foobar'
90              
91             =cut
92              
93 1 50   1 1 4 sub append { my ($x, $y) = @_; return $x . (defined $y ? $y : ''); }
  1         5  
94              
95             =head2 C<at_least>
96              
97             Limits a number to a minimum value.
98              
99             {{ 4 | at_least: 5 }} => 5
100             {{ 4 | at_least: 3 }} => 4
101              
102             =cut
103              
104             sub at_least {
105 2     2 1 10 my ($value, $min) = @_;
106 2 100       10 $min > $value ? $min : $value;
107             }
108              
109             =head2 C<at_most>
110              
111             Limits a number to a maximum value.
112              
113             {{ 4 | at_most: 5 }} => 4
114             {{ 4 | at_most: 3 }} => 3
115              
116             =cut
117              
118             sub at_most {
119 2     2 1 5 my ($value, $max) = @_;
120 2 100       8 $max < $value ? $max : $value;
121             }
122              
123             =head2 C<capitalize>
124              
125             Capitalize words in the input sentence. This filter first applies Perl's C<lc>
126             function and then the C<ucfirst> function.
127              
128             {{ 'this is ONLY a test.' | capitalize }} => This is only a test.
129              
130             =cut
131              
132 3     3 1 7 sub capitalize { my ($x) = @_; return ucfirst lc $x; }
  3         12  
133              
134             =head2 C<ceil>
135              
136             Rounds an integer up to the nearest integer.
137              
138             {{ 4.6 | ceil }} => 5
139             {{ 4.3 | ceil }} => 5
140              
141             =cut
142              
143 4 100   4 1 13 sub ceil { my ($value) = @_; int($value) + ($value > int($value) ? 1 : 0) }
  4         31  
144              
145             =head2 C<compact>
146              
147             Removes any undefined values from an array.
148              
149             For this example, assume C<site.pages> is an array of content pages for a
150             website, and some of these pages have an attribute called category that
151             specifies their content category. If we map those categories to an array, some
152             of the array items might be undefined if any pages do not have a category
153             attribute.
154              
155             {% assign all_categories = site.pages | map: "category" %}
156              
157             {% for item in all_categories %}
158             - {{ item }}
159             {% endfor %}
160              
161             The output of this template would look like this:
162              
163             - business
164             - celebrities
165             -
166             - lifestyle
167             - sports
168             -
169             - technology
170              
171             By using compact when we create our C<site_categories> array, we can remove all
172             the nil values in the array.
173              
174             {% assign all_categories = site.pages | map: "category" | compact %}
175              
176             {% for item in all_categories %}
177             - {{ item }}
178             {% endfor %}
179              
180             The output of this template would look like this:
181              
182             - business
183             - celebrities
184             - lifestyle
185             - sports
186             - technology
187              
188             =cut
189              
190             sub compact {
191 1     1 1 4 my ($list) = @_;
192 1         3 [grep { defined $_ } @$list];
  7         14  
193             }
194              
195             =head2 C<concat>
196              
197             Concatenates (joins together) multiple arrays. The resulting array contains all
198             the items from the input arrays.
199              
200              
201             {% assign fruits = "apples, oranges, peaches" | split: ", " %}
202             {% assign vegetables = "carrots, turnips, potatoes" | split: ", " %}
203              
204             {% assign everything = fruits | concat: vegetables %}
205              
206             {% for item in everything %}
207             - {{ item }}
208             {% endfor %}
209              
210             ...becomes...
211              
212             - apples
213             - oranges
214             - peaches
215             - carrots
216             - turnips
217             - potatoes
218              
219             You can string together two or more array elements with the C<concat> filter:
220              
221             {% assign furniture = "chairs, tables, shelves" | split: ", " %}
222             {% assign vegetables = "carrots, turnips, potatoes" | split: ", " %}
223             {% assign fruits = "apples, oranges, peaches" | split: ", " %}
224              
225             {% assign everything = fruits | concat: vegetables | concat: furniture %}
226              
227             {% for item in everything %}
228             - {{ item }}
229             {% endfor %}
230              
231             ...becomes...
232              
233             - apples
234             - oranges
235             - peaches
236             - carrots
237             - turnips
238             - potatoes
239             - chairs
240             - tables
241             - shelves
242              
243             =cut
244              
245             sub concat {
246 3     3 1 7 my ($values, $more) = @_;
247 3         10 [map {@$_} grep {defined} $values, $more];
  6         19  
  6         14  
248             }
249              
250             =head2 C<date>
251              
252             Converts a timestamp into another date format. The format for this syntax is
253             the same as C<strftime>.
254              
255             {{ article.published_at | date: "%a, %b %d, %y" }} => Fri, Jul 17, 15
256              
257             {{ article.published_at | date: "%Y" }} => 2015
258              
259             C<date> works on strings if they contain well-formatted dates:
260              
261             {{ "March 14, 2016" | date: "%b %d, %y" }} => Mar 14, 16
262              
263              
264             Natural language dates are parsed by C<DateTime::Format::Natural>.
265              
266             To get the current time, pass the special word <"now"> (or C<"today">) to date:
267              
268             This page was last updated at {{ "now" | date: "%Y-%m-%d %H:%M" }}.
269             => This page was last updated at 2019-09-19 17:48.
270              
271             Note that the value will be the current time of when the page was last
272             generated from the template, not when the page is presented to a user if
273             caching or static site generation is involved.
274              
275             =cut
276              
277             sub date {
278 7     7 1 12 CORE::state $DateTimeFormatNatural;
279 7         29 my ($x, $y) = @_;
280 7 100 100     33 $x = time() if lc $x eq 'now' || lc $x eq 'today';
281 7 100 100     109 if (ref $x ne 'DateTime' && $x =~ m[\D]) { # Any non-digit
282 1 50       7 if (!defined $DateTimeFormatNatural) {
283 1         637 require DateTime::Format::Natural;
284 1         46967 $DateTimeFormatNatural = DateTime::Format::Natural->new();
285             }
286 1         39681 $x = $DateTimeFormatNatural->parse_datetime($x);
287             }
288             #
289 7 50       6623 $y = defined $y ? $y : '%c';
290 7 100 66     40 return $x->strftime($y) if ref $x && $x->can('strftime');
291 5 50       24 return if $x !~ m[^(\d*\.)?\d+?$]o;
292 5         32 require POSIX;
293 5         262 return POSIX::strftime($y, gmtime($x));
294             }
295              
296             =head2 C<default>
297              
298             Allows you to specify a fallback in case a value doesn't exist. default will
299             show its value if the left side is nil, false, or empty.
300              
301             In this example, C<product_price> is not defined, so the default value is used.
302              
303             {{ product_price | default: 2.99 }} => 2.99
304              
305             In this example, C<product_price> is defined, so the default value is not used.
306              
307             {% assign product_price = 4.99 %}
308             {{ product_price | default: 2.99 }} => 4.99
309              
310             In this example, C<product_price> is empty, so the default value is used.
311              
312             {% assign product_price = "" %}
313             {{ product_price | default: 2.99 }} => 2.99
314              
315             =cut
316              
317             sub default {
318 7     7 1 19 my ($x, $y) = @_;
319 7 100       29 return length $x ? $x : $y if !ref $x;
    50          
320 0 0       0 return defined $x ? $x : $y;
321             }
322              
323             =head2 C<divided_by>
324              
325             Divides a number by another number.
326              
327             The result is rounded down to the nearest integer (that is, the floor) if the
328             divisor is an integer.
329              
330             {{ 16 | divided_by: 4 }} => 4
331              
332              
333             {{ 5 | divided_by: 3 }} = 1
334              
335             =head3 Controlling rounding
336              
337             C<divided_by> produces a result of the same type as the divisor -- that is, if
338             you divide by an integer, the result will be an integer. If you divide by a
339             float (a number with a decimal in it), the result will be a float.
340              
341             For example, here the divisor is an integer:
342              
343             {{ 20 | divided_by: 7 }} => 2
344              
345             Here it is a float:
346              
347             {{ 20 | divided_by: 7.0 }} => 2.85714285714286
348              
349             Note that floats will not match thanks to how perl and ruby handle floating
350             point numbers.
351              
352             =cut
353              
354             sub divided_by {
355 6     6 1 15 my ($x, $y) = @_;
356 6         21 my $r = $x / $y;
357 6         16 my ($_x) = $x =~ m[\.(\d+)$];
358 6         16 my ($_y) = $y =~ m[\.(\d+)$];
359 6         40 my ($_r) = $r =~ m[\.(\d+)$];
360 6   50     31 $_x //= '';
361 6   100     20 $_y //= '';
362 6   100     16 $_r //= '';
363 6         8 my $_lx = length $_x;
364 6         11 my $_ly = length $_y;
365 6         8 my $_lr = length $_r;
366 6 100 66     52 ($_lx || $_ly)
367             ? (sprintf '%0.' . ([sort $_lr, 1]->[-1]) . 'f', $r)
368             : int $r;
369             }
370              
371             =head2 C<downcase>
372              
373             Makes each character in a string lowercase. It has no effect on strings which
374             are already all lowercase.
375              
376             {{ "Parker Moore" | downcase }} => parker moore
377              
378             {{ "apple" | downcase }} => apple
379              
380             =cut
381              
382 3     3 1 9 sub downcase { my ($x) = @_; return lc $x }
  3         8  
383              
384             =head2 C<escape>
385              
386             Escapes a string by replacing characters with escape sequences (so that the
387             string can be used in a URL, for example). It doesn't change strings that don't
388             have anything to escape.
389              
390             {{ "Have you read 'James & the Giant Peach'?" | escape }}
391             => Have you read &#39;James &amp; the Giant Peach&#39;?
392              
393             {{ "Tetsuro Takara" | escape }} => Tetsuro Takara
394              
395             =cut
396              
397             sub escape {
398 2     2 1 5 my ($x) = @_;
399 2         9 $x =~ s/([^A-Za-z0-9\-\._~ \?])/
400 3         9 my $x = ord $1;
401 3 50       26 sprintf('&%s;',
    50          
    50          
    50          
    100          
402             $1 eq '&' ? 'amp' :
403             $1 eq '>' ? 'gt' :
404             $1 eq '<' ? 'lt' :
405             $1 eq '"' ? 'quot' :
406             $1 eq "'" ? '#39' :
407             "#$x")
408             /gei;
409 2         8 $x;
410             }
411              
412             =head2 C<escape_once>
413              
414             Escapes a string without changing existing escaped entities. It doesn't change
415             strings that don't have anything to escape.
416              
417             {{ "1 < 2 & 3" | escape_once }} => 1 &lt; 2 &amp; 3
418              
419             {{ "1 &lt; 2 &amp; 3" | escape_once }} => 1 &lt; 2 &amp; 3
420              
421             =cut
422              
423             sub escape_once {
424 2     2 1 6 my ($x) = @_;
425 2         15 $x =~ s/("|>|<|'|&(?!([a-z]+|(#\d+));))/
426 2 0       21 sprintf('&%s;',
    0          
    50          
    50          
    100          
427             $1 eq '&' ? 'amp' :
428             $1 eq '>' ? 'gt' :
429             $1 eq '<' ? 'lt' :
430             $1 eq '"' ? 'quot' :
431             $1 eq "'" ? '#39' :
432             "#$x;")
433             /gei;
434 2         7 $x;
435             }
436              
437             =head2 C<first>
438              
439             Returns the first item of an array.
440              
441             {{ "Ground control to Major Tom." | split: " " | first }} => Ground
442              
443              
444             {% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
445              
446             {{ my_array.first }}
447             => zebra
448              
449             You can use C<first> with dot notation when you need to use the filter inside a
450             tag:
451              
452             {% if my_array.first == "zebra" %}
453             Here comes a zebra!
454             {% endif %}
455             => Here comes a zebra!
456              
457             =cut
458              
459             sub first {
460 4     4 1 9 my ($x) = @_;
461 4 100       18 return ref $x eq 'ARRAY' ? @{$x}[0] : substr($x, 0, 1);
  3         11  
462             }
463              
464             =head2 C<floor>
465              
466             Rounds an integer down to the nearest integer.
467              
468             {{ 1.2 | floor }} => 1
469             {{ 2.0 | floor }} => 2
470             {{ 183.357 | floor }} => 183
471              
472             Here the input value is a string:
473              
474             {{ "3.5" | floor }} => 3
475              
476             =cut
477              
478 4     4 1 18 sub floor { int $_[0] }
479              
480             =head2 C<join>
481              
482             Combines the items in an array into a single string using the argument as a
483             separator.
484              
485             {% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
486             {{ beatles | join: " and " }} => John and Paul and George and Ringo
487              
488             =cut
489              
490             sub join {
491 9     9 1 25 my ($x, $y) = @_;
492 9 50       30 return CORE::join($y, @{$x}) if ref $x eq 'ARRAY';
  9         41  
493 0 0       0 return CORE::join($y, keys %{$x}) if ref $x eq 'HASH';
  0         0  
494 0         0 return $x;
495             }
496              
497             =head2 C<last>
498              
499             Returns the last item of an array.
500              
501             {{ "Ground control to Major Tom." | split: " " | last }} => Tom.
502              
503             {% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
504             {{ my_array.last }} => tiger
505              
506             You can use C<last> with dot notation when you need to use the filter inside a
507             tag:
508              
509             {% assign my_array = "zebra, octopus, giraffe, tiger" | split: ", " %}
510             {% if my_array.last == "tiger" %}
511             There goes a tiger!
512             {% endif %}
513              
514             =cut
515              
516             sub last {
517 4     4 1 12 my ($x, $y) = @_;
518 4         11 my $ref = ref $x;
519 4 100       16 return substr $x, -1 if !$ref;
520 3 50       9 return @{$x}[-1] if $ref eq 'ARRAY';
  3         11  
521             }
522              
523             =head2 C<lstrip>
524              
525             Removes all whitespace (tabs, spaces, and newlines) from the left side of a
526             string. It does not affect spaces between words.
527              
528             {{ " So much room for activities! " | lstrip }}
529             => So much room for activities!
530              
531             =cut
532              
533             sub lstrip {
534 1     1 1 4 my ($x) = @_;
535 1         5 $x =~ s[^\s*][];
536 1         4 $x;
537             }
538              
539             =head2 C<map>
540              
541             Creates an array of values by extracting the values of a named property from
542             another object.
543              
544             In this example, assume the object C<site.pages> contains all the metadata for
545             a website. Using assign with the map filter creates a variable that contains
546             only the values of the category properties of everything in the site.pages
547             object.
548              
549             {% assign all_categories = site.pages | map: "category" %}
550              
551             {% for item in all_categories %}
552             - {{ item }}
553             {% endfor %}
554              
555             The output of this template would look like this:
556              
557             - business
558             - celebrities
559             - lifestyle
560             - sports
561             - technology
562              
563             =cut
564              
565             sub map {
566 4     4 1 11 my ($list, $key) = @_;
567 4         8 [map { $_->{$key} } @$list];
  23         48  
568             }
569              
570             =head2 C<minus>
571              
572             Subtracts a number from another number.
573              
574             {{ 4 | minus: 2 }} => 2
575              
576             {{ 16 | minus: 4 }} => 12
577              
578             {{ 183.357 | minus: 12 }} => 171.357
579              
580             =cut
581              
582             sub minus {
583 11     11 1 27 my ($x, $y) = @_;
584 11   50     26 $x ||= 0;
585 11 100 66     103 return $x =~ m[^[\+-]?(\d*\.)?\d+?$]o &&
586             $y =~ m[^[\+-]?(\d*\.)?\d+?$]o ? $x - $y : ();
587             }
588              
589             =head2 C<modulo>
590              
591             Returns the remainder of a division operation.
592              
593             {{ 3 | modulo: 2 }} => 1
594              
595             {{ 24 | modulo: 7 }} => 3
596              
597             {{ 183.357 | modulo: 12 }} => 3.357
598              
599             =cut
600              
601             sub modulo {
602 3     3 1 9 my ($x, $y) = @_;
603 3         20 require POSIX;
604 3         30 POSIX::fmod($x, $y);
605             }
606              
607             =head2 C<newline_to_br>
608              
609             Replaces each newline (C<\n>) with html break (C<< <br />\n >>).
610              
611             {% capture string_with_newlines %}
612             Hello
613             there
614             {% endcapture %}
615              
616             {{ string_with_newlines | newline_to_br }}
617              
618             ...becomes...
619              
620             <br />
621             Hello<br />
622             there<br />
623              
624             =cut
625              
626 2     2 1 6 sub newline_to_br { my ($x, $y) = @_; $x =~ s[\n][<br />\n]go; return $x; }
  2         9  
  2         7  
627              
628             =head2 C<plus>
629              
630             Adds a number to another number.
631              
632             {{ 154 | plus:1183 }} => 1337
633             {{ 4 | plus: 2 }} => 6
634             {{ 16 | plus: 4 }} => 20
635             {{ 183.357 | plus: 12 }} => 195.357
636              
637             {{ 'What' | plus:'Uhu' }} => WhatUhu
638              
639             =head3 MATHFAIL!
640              
641             Please note that integer behavior differs with Perl vs. Ruby so...
642              
643             {{ '1' | plus: '1' }}
644              
645             ...becomes C<11> in Ruby but C<2> in Perl.
646              
647             =cut
648              
649             sub plus {
650 7     7 1 17 my ($x, $y) = @_;
651 7   50     18 $x ||= 0;
652 7 100 66     65 return $x =~ m[^[\+-]?(\d*\.)?\d+?$]o &&
653             $y =~ m[^[\+-]?(\d*\.)?\d+?$]o ? $x + $y : $x . $y;
654             }
655              
656             =head2 C<prepend>
657              
658             Adds the specified string to the beginning of another string.
659              
660             {{ 'bar' | prepend:'foo' }} => 'foobar'
661              
662             {{ "apples, oranges, and bananas" | prepend: "Some fruit: " }}
663             => Some fruit: apples, oranges, and bananas
664              
665             {% assign url = "example.com" %}
666             {{ "/index.html" | prepend: url }} => example.com/index.html
667              
668             =cut
669              
670 7 100   7 1 18 sub prepend { my ($x, $y) = @_; return (defined $y ? $y : '') . $x; }
  7         28  
671              
672             =head2 C<remove>
673              
674             Removes every occurrence of the specified substring from a string.
675              
676             {{ 'foobarfoobar' | remove:'foo' }} => 'barbar'
677              
678             {{ "I strained to see the train through the rain" | remove: "rain" }}
679             => I sted to see the t through the
680              
681             =cut
682              
683 3     3 1 12 sub remove { my ($x, $y) = @_; $y = quotemeta($y); $x =~ s{$y}{}g; return $x }
  3         8  
  3         45  
  3         11  
684              
685             =head2 C<remove_first>
686              
687             Remove the first occurrence of a string.
688              
689             {{ 'barbar' | remove_first:'bar' }} => bar
690              
691             {{ "I strained to see the train through the rain" | remove_first: "rain" }}
692             => I sted to see the train through the rain
693              
694             =cut
695              
696             sub remove_first {
697 3     3 1 10 my ($x, $y) = @_;
698 3         9 $y = quotemeta($y);
699 3         42 $x =~ s{$y}{};
700 3         11 return $x;
701             }
702              
703             =head2 C<replace>
704              
705             Replaces every occurrence of the first argument in a string with the second
706             argument.
707              
708             The replacement value is optional and defaults to an empty string (C<''>).
709              
710             {{ 'foofoo' | replace:'foo','bar' }} => barbar
711             {% assign this = 'that' %}
712             {{ 'Replace that with this' | replace:this,'this' }} => Replace this with this
713             {{ 'I have a listhp.' | replace:'th' }} => I have a lisp.
714             {{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}
715             => Take your protein pills and put your helmet on
716              
717             =cut
718              
719             sub replace {
720 5     5 1 20 my ($x, $y, $z) = @_;
721 5 100       16 $z = defined $z ? $z : '';
722 5         11 $y = quotemeta($y);
723 5 50       92 $x =~ s{$y}{$z}g if $y;
724 5         19 return $x;
725             }
726              
727             =head2 C<replace_first>
728              
729             Replaces only the first occurrence of the first argument in a string with the
730             second argument.
731              
732             The replacement value is optional and defaults to an empty string (C<''>).
733              
734             {{ 'barbar' | replace_first:'bar','foo' }} => 'foobar'
735              
736             {{ "Take my protein pills and put my helmet on" | replace_first: "my", "your" }}
737             => Take your protein pills and put my helmet on
738              
739             =cut
740              
741             sub replace_first {
742 3     3 1 12 my ($x, $y, $z) = @_;
743 3 50       9 $z = defined $z ? $z : '';
744 3         7 $y = quotemeta($y);
745 3         48 $x =~ s{$y}{$z};
746 3         12 return $x;
747             }
748              
749             =head2 C<reverse>
750              
751             Reverses the order of the items in an array. C<reverse> cannot reverse a
752             string.
753              
754             {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
755             {{ my_array | reverse | join: ", " }} => plums, peaches, oranges, apples
756              
757             Although C<reverse> cannot be used directly on a string, you can split a string
758             into an array, reverse the array, and rejoin it by chaining together filters:
759              
760             {{ "Ground control to Major Tom." | split: "" | reverse | join: "" }}
761             => .moT rojaM ot lortnoc dnuorG
762              
763             =cut
764              
765             sub reverse {
766 2     2 1 5 my ($args) = @_;
767 2         10 [reverse @$args];
768             }
769              
770             =head2 C<round>
771              
772             Rounds a number to the nearest integer or, if a number is passed as an
773             argument, to that number of decimal places.
774              
775             {{ 4.6 | round }} => 5
776             {{ 4.3 | round }} => 4
777             {{ 1.2 | round }} => 1
778             {{ 2.7 | round }} => 3
779             {{ 4.5612 | round: 2 }} => 4.56
780             {{ 183.357 | round: 2 }} => 183.36
781              
782             =cut
783              
784             sub round {
785 6     6 1 14 my ($x, $y) = @_;
786 6 50       30 return if $x !~ m[^(\d*\.)?\d+?$]o;
787 6   100     63 return sprintf '%.' . int($y || 0) . 'f', $x;
788             }
789              
790             =head2 C<rstrip>
791              
792             Removes all whitespace (tabs, spaces, and newlines) from the right side of a
793             string. It does not affect spaces between words.
794              
795             {{ " So much room for activities! " | rstrip }}
796             => So much room for activities!
797              
798             =cut
799              
800             sub rstrip {
801 1     1 1 4 my ($x) = @_;
802 1         18 $x =~ s[\s*$][];
803 1         5 $x;
804             }
805              
806             =head2 C<size>
807              
808             Returns the number of characters in a string, the number of items in an array,
809             or the number of keys in a hash reference. Undefined values return C<0>.
810              
811             # Where array is [1..6] and hash is { child => 'blarg'}
812             {{ array | size }} => 6
813             {{ 'Testing' | size }} => 7
814             {{ hash | size }} => 1
815             {{ undefined | size }} => 0
816             {{ "Ground control to Major Tom." | size }} => 28
817              
818             {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}
819             {{ my_array.size }} => 4
820              
821             You can use C<size> with dot notation when you need to use the filter inside a
822             tag:
823              
824             {% if site.pages.size > 10 %}
825             This is a big website!
826             {% endif %}
827              
828             =cut
829              
830             sub size {
831 5     5 1 13 my ($x, $y) = @_;
832 5 100       14 return 0 if !defined $x;
833 4 100       12 return scalar @{$x} if ref $x eq 'ARRAY';
  1         4  
834 3 100       9 return scalar keys %{$x} if ref $x eq 'HASH';
  1         5  
835 2         5 return length $x;
836             }
837              
838             =head2 C<slice>
839              
840             Returns a substring of 1 character beginning at the index specified by the
841             first argument. An optional second argument specifies the length of the
842             substring to be returned.
843              
844             String indices are numbered starting from 0.
845              
846             {{ "Liquid" | slice: 0 }} => L
847             {{ "Liquid" | slice: 2 }} => q
848             {{ "Liquid" | slice: 2, 5 }} => quid
849              
850             If the first argument is a negative number, the indices are counted from the
851             end of the string:
852              
853             {{ "Liquid" | slice: -3, 2 }} => ui
854              
855             =cut
856              
857             sub slice {
858 4     4 1 13 my ($x, $pos, $len) = @_;
859 4 100       22 $len = 1 unless defined $len;
860 4         17 substr $x, $pos, $len;
861             }
862              
863             =head2 C<sort>
864              
865             Sorts items in an array in case-sensitive order.
866              
867             {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}
868             {{ my_array | sort | join: ", " }} => Sally Snake, giraffe, octopus, zebra
869              
870             An optional argument specifies which property of the array's items to use for
871             sorting.
872              
873             {% assign products_by_price = collection.products | sort: "price" %}
874             {% for product in products_by_price %}
875             <h4>{{ product.title }}</h4>
876             {% endfor %}
877              
878             =cut
879              
880             sub sort {
881 2     2 1 6 my ($x, $y) = @_;
882 15 100 66     62 return [sort { ($a =~ m[\D] || $b =~ m[\D]) ? $a cmp $b : $a <=> $b }
883 2 50       125 @{$x}]
  2         12  
884             if ref $x eq 'ARRAY';
885             return
886 0 0 0     0 sort { ($a =~ m[\D] || $b =~ m[\D]) ? $a cmp $b : $a <=> $b }
887 0 0       0 keys %{$x}
  0         0  
888             if ref $x eq 'HASH';
889 0         0 return $x;
890             }
891              
892             =head2 C<sort_natural>
893              
894             Sorts items in an array in case-sensitive order.
895              
896             {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}
897             {{ my_array | sort_natural | join: ", " }} => giraffe, octopus, Sally Snake, zebra
898              
899             An optional argument specifies which property of the array's items to use for
900             sorting.
901              
902             {% assign products_by_company = collection.products | sort_natural: "company" %}
903             {% for product in products_by_company %}
904             <h4>{{ product.title }}</h4>
905             {% endfor %}
906              
907             =cut
908              
909             sub sort_natural {
910 1     1 1 3 my ($x, $y) = @_;
911             return [
912             sort {
913             ($a->{$y} =~ m[\D] || $b->{$y} =~ m[\D])
914             ? $a->{$y} cmp $b->{$y}
915 0 0 0     0 : $a->{$y} <=> $b->{$y}
916 1 50 33     7 } @{$x}
  0         0  
917             ]
918             if ref $x eq 'HASH' && defined $y;
919             return [
920 5 50 33     34 sort { ($a =~ m[\D] || $b =~ m[\D]) ? lc $a cmp lc $b : $a <=> $b }
921 1 50       4 @{$x}
  1         99  
922             ]
923             if ref $x eq 'ARRAY';
924             return
925 0 0 0     0 sort { ($a =~ m[\D] || $b =~ m[\D]) ? lc $a cmp lc $b : $a <=> $b }
926 0 0       0 keys %{$x}
  0         0  
927             if ref $x eq 'HASH';
928 0         0 return $x;
929             }
930              
931             =head2 C<split>
932              
933             Divides a string into an array using the argument as a separator. C<split> is
934             commonly used to convert comma-separated items from a string to an array.
935              
936             {% assign beatles = "John, Paul, George, Ringo" | split: ", " %}
937             {% for member in beatles %}
938             {{ member }}
939             {% endfor %}
940              
941             ...becomes...
942              
943             John
944             Paul
945             George
946             Ringo
947              
948             =cut
949              
950             sub split {
951 21     21 1 52 my ($x, $y) = @_;
952 21 50       56 return [] if !defined $x;
953 21         339 [split $y, $x];
954             }
955              
956             =head2 C<strip>
957              
958             Removes all whitespace (tabs, spaces, and newlines) from both the left and
959             right sides of a string. It does not affect spaces between words.
960              
961             |{{ " So much room for activities! " | strip }}|
962             => |So much room for activities!|
963              
964             =cut
965              
966             sub strip {
967 1     1 1 3 my ($x) = @_;
968 1         10 $x =~ s[^\s+|\s+$][]g;
969 1         4 $x;
970             }
971              
972             =head2 C<strip_html>
973              
974             Removes any HTML tags from a string.
975              
976             {{ '<div>Hello, <em id="whom">world!</em></div>' | strip_html }} => Hello, world!
977             '{{ '<IMG SRC = "foo.gif" ALT = "A > B">' | strip_html }}' => ' B">'
978             '{{ '<!-- <A comment> -->' | strip_html }}' => ' -->'
979             {{ "Have <em>you</em> read <strong>Ulysses</strong>?" | strip_html }} => Have you read Ulysses?
980              
981             Note that this filter uses C<s[<.*?>][]g> in emulation of the Ruby Liquid
982             library's strip_html function. ...so don't email me if you (correctly) think
983             this is a brain dead way of stripping html.
984              
985             =cut
986              
987             sub strip_html {
988 3     3 1 7 my ($x, $y) = @_;
989 3         21 $x =~ s[<.*?>][]go;
990 3         7 $x =~ s[<!--.*?-->][]go;
991 3         5 $x =~ s[<script.*?<\/script>][]go;
992 3         9 return $x;
993             }
994              
995             =head2 C<strip_newlines>
996              
997             Removes any newline characters (line breaks) from a string.
998              
999             {% capture string_with_newlines %} Hello there {% endcapture %}
1000              
1001             {{ string_with_newlines | strip_newlines }} => Hellothere
1002              
1003             =cut
1004              
1005 2     2 1 5 sub strip_newlines { my ($x, $y) = @_; $x =~ s[\n][]go; return $x; }
  2         12  
  2         5  
1006              
1007             =head2 C<times>
1008              
1009             Simple multiplication or string repetition.
1010              
1011             {{ 'foo' | times: 4 }} => foofoofoofoo
1012             {{ 5 | times: 4 }} => 20
1013             {{ 3 | times: 2 }} => 6
1014             {{ 24 | times: 7 }} => 168
1015             {{ 183.357 | times: 12 }} => 2200.284
1016              
1017             =cut
1018              
1019             sub times {
1020 6     6 1 17 my ($x, $y) = @_;
1021              
1022             #warn sprintf '%s | %s', $x, $y;
1023 6 50       16 return unless $y;
1024 6         11 my $r;
1025 6 50       26 $r = $x if $y !~ m[^[\+-]?(\d*\.)?\d+?$]o;
1026 6 100       26 return $x x $y if $x !~ m[^[\+-]?(\d*\.)?\d+?$]o;
1027 5 50       18 $r = $x * $y unless defined $r;
1028 5         16 my ($_x) = $x =~ m[\.(\d+)$];
1029 5         14 my ($_y) = $y =~ m[\.(\d+)$];
1030 5         26 my ($_r) = $r =~ m[\.(\d+)$];
1031 5   100     24 $_x //= '';
1032 5   100     21 $_y //= '';
1033 5   100     17 $_r //= '';
1034 5         8 my $_lx = length $_x;
1035 5         10 my $_ly = length $_y;
1036 5         6 my $_lr = length $_r;
1037 5 100 66     55 ( ($_lx || $_ly || $_lr)
1038             ? (sprintf '%0.' . ([sort $_lx, $_ly, $_lr]->[-1]) . 'f', $r)
1039             : $r);
1040             }
1041              
1042             =head2 C<truncate>
1043              
1044             Shortens a string down to the number of characters passed as an argument. If
1045             the specified number of characters is less than the length of the string, an
1046             ellipsis (...) is appended to the string and is included in the character
1047             count.
1048              
1049             {{ "Ground control to Major Tom." | truncate: 20 }} => Ground control to...
1050             {{ 'Running the halls!!!' | truncate:19 }} => Running the hall..
1051             {% assign blarg = 'STOP!' %}
1052             {{ 'Any Colour You Like' | truncate:10,blarg }} => Any CSTOP!
1053             {{ 'Why are you running away?' | truncate:4,'?' }} => Why?
1054             {{ 'Ha' | truncate:4 }} => Ha
1055             {{ 'Ha' | truncate:1,'Laugh' }} => Laugh
1056             {{ 'Ha' | truncate:1,'...' }} => ...
1057              
1058             ...and...
1059              
1060             {{ 'This is a long line of text to test the default values for truncate' | truncate }}
1061              
1062             ...becomes...
1063              
1064             This is a long line of text to test the default...
1065              
1066             =head3 Custom ellipsis
1067              
1068             C<truncate> takes an optional second argument that specifies the sequence of
1069             characters to be appended to the truncated string. By default this is an
1070             ellipsis (...), but you can specify a different sequence.
1071              
1072             The length of the second argument counts against the number of characters
1073             specified by the first argument. For example, if you want to truncate a string
1074             to exactly 10 characters, and use a 3-character ellipsis, use B<13> for the
1075             first argument of truncate, since the ellipsis counts as 3 characters.
1076              
1077             {{ "Ground control to Major Tom." | truncate: 25, ", and so on" }}
1078             => Ground control, and so on
1079              
1080             =head3 No ellipsis
1081              
1082             You can truncate to the exact number of characters specified by the first
1083             argument and avoid showing trailing characters by passing a blank string as the
1084             second argument:
1085              
1086             {{ "Ground control to Major Tom." | truncate: 20, "" }}
1087             => Ground control to Ma
1088              
1089             =cut
1090              
1091             sub truncate {
1092 5     5 1 15 my ($data, $length, $truncate_string) = @_;
1093 5 50       12 $length = defined $length ? $length : 50;
1094 5 100       13 $truncate_string = defined $truncate_string ? $truncate_string : '...';
1095 5 50       12 return if !$data;
1096 5         15 my $l = $length - length($truncate_string);
1097 5 50       10 $l = 0 if $l < 0;
1098             return
1099 5 50       23 length($data) > $length
1100             ? substr($data, 0, $l) . $truncate_string
1101             : $data;
1102             }
1103              
1104             =head2 C<truncatewords>
1105              
1106             Shortens a string down to the number of words passed as an argument. If the
1107             specified number of words is less than the number of words in the string, an
1108             ellipsis (...) is appended to the string.
1109              
1110             {{ "Ground control to Major Tom." | truncatewords: 3 }} => Ground control to...
1111              
1112             =head3 Custom ellipsis
1113              
1114             C<truncatewords> takes an optional second argument that specifies the sequence
1115             of characters to be appended to the truncated string. By default this is an
1116             ellipsis (...), but you can specify a different sequence.
1117              
1118             {{ "Ground control to Major Tom." | truncatewords: 3, "--" }} => Ground control to--
1119              
1120             =head3 No ellipsis
1121              
1122             You can avoid showing trailing characters by passing a blank string as the
1123             second argument:
1124              
1125             {{ "Ground control to Major Tom." | truncatewords: 3, "" }} => Ground control to
1126              
1127             =cut
1128              
1129             sub truncatewords {
1130 5     5 1 13 my ($data, $words, $truncate_string) = @_;
1131 5 100       15 $words = defined $words ? $words : 15;
1132 5 100       11 $truncate_string = defined $truncate_string ? $truncate_string : '...';
1133 5 50       11 return if !$data;
1134 5         20 my @wordlist = split ' ', $data;
1135 5         12 my $l = $words - 1;
1136 5 50       12 $l = 0 if $l < 0;
1137 5 50       37 return $#wordlist > $l
1138             ? CORE::join(' ', @wordlist[0 .. $l]) . $truncate_string
1139             : $data;
1140             }
1141              
1142             =head2 C<uniq>
1143              
1144             Removes any duplicate elements in an array.
1145              
1146             {% assign my_array = "ants, bugs, bees, bugs, ants" | split: ", " %}
1147             {{ my_array | uniq | join: ", " }} => ants, bugs, bees
1148              
1149             =cut
1150              
1151             sub uniq {
1152 1     1 1 3 my ($array) = @_;
1153 1         3 my @retval;
1154 1         2 for my $element (@$array) {
1155 5 100       12 push @retval, $element unless grep { $_ eq $element } @retval;
  9         21  
1156             }
1157 1         3 \@retval;
1158             }
1159              
1160             =head2 C<upcase>
1161              
1162             Makes each character in a string uppercase. It has no effect on strings which
1163             are already all uppercase.
1164              
1165             {{ "Parker Moore" | upcase }} => PARKER MOORE
1166              
1167             {{ "APPLE" | upcase }} => APPLE
1168              
1169             =cut
1170              
1171 4     4 1 10 sub upcase { my ($x) = @_; return uc $x }
  4         15  
1172              
1173             =head2 C<url_decode>
1174              
1175             Decodes a string that has been encoded as a URL or by C<url_encode>.
1176              
1177             {{ "%27Stop%21%27+said+Fred" | url_decode }} => 'Stop!' said Fred
1178              
1179             =cut
1180              
1181             sub url_decode {
1182 1     1 1 4 my ($x) = @_;
1183 1         5 $x =~ s[\+][ ]g;
1184 1         6 $x =~ s[%(\d+)][chr hex $1]gex;
  3         15  
1185 1         3 $x;
1186             }
1187              
1188             =head2 C<url_encode>
1189              
1190             Converts any URL-unsafe characters in a string into percent-encoded characters.
1191              
1192             {{ "john@liquid.com" | url_encode }} => john%40liquid.com
1193              
1194             {{ "Tetsuro Takara" | url_encode }} => Tetsuro+Takara
1195              
1196             {{ "'Stop!'" said Fred" | url_encode }} => %27Stop%21%27+said+Fred
1197              
1198             =cut
1199              
1200             sub url_encode {
1201 3     3 1 6 my $s = shift;
1202 3         11 $s =~ s/ /+/g;
1203 3         14 $s =~ s/([^A-Za-z0-9\+-\.])/sprintf("%%%02X", ord($1))/seg;
  4         23  
1204 3         9 return $s;
1205             }
1206              
1207             =head2 C<where>
1208              
1209             Creates an array including only the objects with a given property value, or any
1210             truthy value by default.
1211              
1212             In this example, assume you have a list of products and you want to show your
1213             kitchen products separately. Using C<where>, you can create an array containing
1214             only the products that have a C<"type"> of C<"kitchen">.
1215              
1216             All products:
1217             {% for product in products %}
1218             - {{ product.title }}
1219             {% endfor %}
1220              
1221             {% assign kitchen_products = products | where: "type", "kitchen" %}
1222              
1223             Kitchen products:
1224             {% for product in kitchen_products %}
1225             - {{ product.title }}
1226             {% endfor %}
1227              
1228             ...rendered with this data...
1229              
1230             products => [
1231             { title => 'Vacuum', type => 'carpet', },
1232             { title => 'Spatula', type => 'kitchen' },
1233             { title => 'Television', type => 'den' },
1234             { title => 'Garlic press', type => 'kitchen' },
1235             ]
1236              
1237             ...becomes...
1238              
1239             All products:
1240             - Vacuum
1241             - Spatula
1242             - Television
1243             - Garlic press
1244              
1245             Kitchen products:
1246             - Spatula
1247             - Garlic press
1248              
1249             Say instead you have a list of products and you only want to show those that
1250             are available to buy. You can where with a property name but no target value to
1251             include all products with a truthy "available" value.
1252              
1253             All products:
1254             {% for product in products %}
1255             - {{ product.title }}
1256             {% endfor %}
1257              
1258             {% assign available_products = products | where: "available" %}
1259              
1260             Available products:
1261             {% for product in available_products %}
1262             - {{ product.title }}
1263             {% endfor %}
1264              
1265             ...rendered with this data...
1266              
1267             products => [
1268             { title => 'Coffee mug', available => 1},
1269             { title => 'Limited edition sneakers', available => 0},
1270             { title => 'Boring sneakers', available => 1}
1271             ]
1272              
1273             ...becomes...
1274              
1275             All products:
1276             - Coffee mug
1277             - Limited edition sneakers
1278             - Boring sneakers
1279              
1280             Available products:
1281             - Coffee mug
1282             - Boring sneakers
1283              
1284             The C<where> filter can also be used to find a single object in an array when
1285             combined with the C<first> filter. For example, say you want to show off the
1286             shirt in your new fall collection.
1287              
1288             {% assign new_shirt = products | where: "type", "shirt" | first %}
1289              
1290             Featured product: {{ new_shirt.title }}
1291              
1292             ...rendered with the following data...
1293              
1294             products => [
1295             { title => 'Limited edition sneakers', type => 'shoes' },
1296             { title => 'Hawaiian print sweater vest', type => 'shirt' },
1297             { title => 'Tuxedo print tshirt', type => 'shirt' },
1298             { title => 'Jorts', type => 'shorts' }
1299             ]
1300              
1301             ...becomes...
1302              
1303             Featured product: Hawaiian print sweater vest
1304              
1305             =cut
1306              
1307             sub where {
1308 3     3 1 8 my ($list, $key, $value) = @_;
1309 3 100       7 [grep { defined $value ? $_->{$key} eq $value : !!$_->{$key} } @$list];
  11         35  
1310             }
1311              
1312             =head2 C<money>
1313              
1314             Formats floats and integers as if they were money.
1315              
1316             {{ 4.6 | money }} => $4.60
1317             {{ -4.3 | money }} => -$4.30
1318             {{ 4.5612 | money }} => $4.56
1319              
1320             You may pass a currency symbol to override the default dollar sign (C<$>).
1321              
1322             {{ 4.6 | money:'€' }} => €4.60
1323              
1324             =cut
1325              
1326             sub money {
1327 4     4 1 11 my ($x, $y) = @_;
1328 4 50       18 return if $x !~ m[^[\+-]?(\d*\.)?\d+?$]o;
1329 4 100       45 return (($x < 0 ? '-' : '') . (defined $y ? $y : '$') . sprintf '%.2f',
    100          
1330             CORE::abs($x));
1331             }
1332              
1333             =head2 C<stock_price>
1334              
1335             Formats floats and integers as if they were stock prices.
1336              
1337             {{ 4.6 | stock_price }} => $4.60
1338             {{ 0.30 | stock_price }} => $0.3000
1339             {{ 4.5612 | stock_price }} => $4.56
1340              
1341             You may pass a currency symbol to override the default dollar sign (C<$>).
1342              
1343             {{ 4.6 | stock_price:'€' }} => €4.60
1344              
1345             =cut
1346              
1347             sub stock_price {
1348 4     4 1 13 my ($x, $y) = @_;
1349 4 50       25 return if $x !~ m[^[\+-]?(\d*\.)?\d+?$]o;
1350 4 50       52 return (($x < 0 ? '-' : '') .
    100          
    100          
1351             (defined $y ? $y : '$') .
1352             sprintf '%.' .
1353             (int(CORE::abs($x)) > 0 ? 2 : 4) . 'f',
1354             CORE::abs($x)
1355             );
1356             }
1357              
1358             =head1 Author
1359              
1360             Sanko Robinson <sanko@cpan.org> - http://sankorobinson.com/
1361              
1362             CPAN ID: SANKO
1363              
1364             =head1 License and Legal
1365              
1366             Copyright (C) 2009-2023 by Sanko Robinson E<lt>sanko@cpan.orgE<gt>
1367              
1368             This program is free software; you can redistribute it and/or modify it under
1369             the terms of L<The Artistic License
1370             2.0|http://www.perlfoundation.org/artistic_license_2_0>. See the F<LICENSE>
1371             file included with this distribution or L<notes on the Artistic License
1372             2.0|http://www.perlfoundation.org/artistic_2_0_notes> for clarification.
1373              
1374             When separated from the distribution, all original POD documentation is covered
1375             by the L<Creative Commons Attribution-Share Alike 3.0
1376             License|http://creativecommons.org/licenses/by-sa/3.0/us/legalcode>. See the
1377             L<clarification of the
1378             CCA-SA3.0|http://creativecommons.org/licenses/by-sa/3.0/us/>.
1379              
1380             =for stopwords website
1381              
1382             =cut