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