Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed up pipe character support in split #23

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
20 changes: 16 additions & 4 deletions lib/Template/Liquid/Condition.pm
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ sub new {
if !defined $args->{'parent'};
my ($lval, $condition, $rval)
= ((defined $args->{'attrs'} ? $args->{'attrs'} : '')
=~ m[("[^"]+"|'[^']+'|(?:eq|==|ne|!=|lt|<|gt|>|contains|&&|\|\|)|(?:[\w.]+))]go
=~ m[("[^"]+"|'[^']+'|(?:eq\s+|==|ne\s+|!=|lt\s+|<|gt\s+|>|contains|&&|\|\|)|(?:[\w.]+))]go
);

if($condition) {
$condition =~ s/\s+$//;
}
if (defined $lval) {
if (!defined $rval && !defined $condition) {
return
Expand Down Expand Up @@ -84,6 +88,7 @@ sub eq {
my $_r = $s->{template}{context}->get($r);
$l = $_l if defined $_l;
$r = $_r if defined $_r;
$r = "" if !defined $r; # hack to match blank strings properly
return _equal($l, $r);
}

Expand All @@ -92,9 +97,12 @@ sub _equal { # XXX - Pray we don't have a recursive data structure...
my $ref_l = ref $l;
return !1 if $ref_l ne ref $r;
if (!$ref_l) {
if(!defined $l or !defined $r) {
return !1;
}
return
!!(grep {defined} $l, $r)
? (grep {m[\D]o} $l, $r)
? (grep {m[\D]o || m[^$]} $l, $r)
? $l eq $r
: $l == $r
: !1;
Expand Down Expand Up @@ -149,10 +157,14 @@ sub contains {
my $_r = $s->{template}{context}->get($r);
$l = $_l if defined $_l;
$r = $_r if defined $_r;
$r = quotemeta $r;
return if defined $r && !defined $l;
return defined($l->{$r}) ? 1 : !1 if ref $l eq 'HASH';
return (grep { $_ eq $r } @$l) ? 1 : !1 if ref $l eq 'ARRAY';
if (ref $l eq 'ARRAY') {
for my $i (0 .. $#$l) {
return 1 if $l->[$i] eq $r;
}
return !1;
}
return $l =~ qr[${r}] ? 1 : !1;
}

Expand Down
11 changes: 10 additions & 1 deletion lib/Template/Liquid/Context.pm
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,16 @@ sub get {

# print STDERR "DEBUG:obj property. var=$var. 1=$1,2=$2";
my $obj = $s->get($1);
return $obj->{$2} if $obj;
my $objreftype = ref $obj;
return $obj->{$2} if $objreftype eq "HASH";

if($objreftype eq 'ARRAY') {
# resolve the scope variable into an array index
my $a_idx = $s->get($2);
# print STDERR "\nDEBUG:a_idx=$a_idx";
return @$obj[$a_idx] if @$obj[$a_idx];
}

return; # return if nothing
}
STEP: while (@path) {
Expand Down
39 changes: 36 additions & 3 deletions lib/Template/Liquid/Filters.pm
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ string. It does not affect spaces between words.

sub lstrip {
my ($x) = @_;
$x = $x // "";
$x =~ s[^\s*][];
$x;
}
Expand Down Expand Up @@ -799,6 +800,7 @@ string. It does not affect spaces between words.

sub rstrip {
my ($x) = @_;
$x = $x // "";
$x =~ s[\s*$][];
$x;
}
Expand Down Expand Up @@ -837,6 +839,7 @@ sub size {

=head2 C<slice>

String Processing:
Returns a substring of 1 character beginning at the index specified by the
first argument. An optional second argument specifies the length of the
substring to be returned.
Expand All @@ -852,12 +855,40 @@ end of the string:

{{ "Liquid" | slice: -3, 2 }} => ui

Array Processing:
Also works with arrays


=cut

sub slice {
my ($x, $pos, $len) = @_;
$len = 1 unless defined $len;
substr $x, $pos, $len;
my ($x, $pos, $len) = @_;
$len = 1 unless defined $len;

if(ref $x eq "ARRAY") {
my @array = @$x;

# Handle negative indices
$pos = scalar(@array) + $pos if $pos < 0;

# If pos is out of bounds, return an empty list
return () if $pos < 0 || $pos >= scalar(@array);

# If len is undefined, take everything from pos
$len = scalar(@array) - $pos unless defined $len;

# Ensure len does not exceed array bounds
$len = scalar(@array) - $pos if $pos + $len > scalar(@array);

# Slice and return the result
# print STDERR Dumper(@array[$pos .. $pos + $len - 1]);
# return array reference
return [ @array[$pos .. $pos + $len - 1] ];

}

# return string
substr $x, $pos, $len;
}

=head2 C<sort>
Expand Down Expand Up @@ -950,6 +981,7 @@ commonly used to convert comma-separated items from a string to an array.
sub split {
my ($x, $y) = @_;
return [] if !defined $x;
$y = quotemeta($y) if defined $y;
[split $y, $x];
}

Expand All @@ -965,6 +997,7 @@ right sides of a string. It does not affect spaces between words.

sub strip {
my ($x) = @_;
$x = $x // "";
$x =~ s[^\s+|\s+$][]g;
$x;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Template/Liquid/Utility.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use experimental 'signatures';
use strict;
use warnings;
our $VERSION = '1.0.23';
our $FilterSeparator = qr[\s*\|\s*]o;
our $FilterSeparator = qr[(?<!")\s*\|\s*(?!")]o;
my $ArgumentSeparator = qr[,]o;
our $FilterArgumentSeparator = qr[\s*:\s*]o;
our $VariableAttributeSeparator = qr[\.]o;
Expand Down
68 changes: 65 additions & 3 deletions t/0100_filters/0101_standard_filters.t
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ is( Template::Liquid->parse(
q[{{ my_array.size }} => 4]
);

# slice
# slice string
is(Template::Liquid->parse(q[{{ "Liquid" | slice: 0 }}])->render(),
'L', q[{{ "Liquid" | slice: 0 }} => L]);
is(Template::Liquid->parse(q[{{ "Liquid" | slice: 2 }}])->render(),
Expand All @@ -616,6 +616,38 @@ is(Template::Liquid->parse(q[{{ "Liquid" | slice: 2, 5 }}])->render(),
is(Template::Liquid->parse(q[{{ "Liquid" | slice: -3, 2 }}])->render(),
'ui', q[{{ "Liquid" | slice: -3, 2 }} => ui]);

# slice array
is( Template::Liquid->parse(
q[{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{{ my_array | slice: 0 }}]
)->render(),
'zebra',
'{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{{ my_array | slice: 0 }}'
);
is( Template::Liquid->parse(
q[{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{% assign first = my_array | slice: 0 %}{{ first }}]
)->render(),
'zebra',
'{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{% assign first = my_array | slice: 0 %}{{ first }}'
);
is( Template::Liquid->parse(
q[{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{{ my_array | slice: 0,2 }}]
)->render(),
'zebraoctopus',
'{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{{ my_array | slice: 0,2 }}'
);
is( Template::Liquid->parse(
q[{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{{ my_array | slice: 1,3 }}]
)->render(),
'octopusgiraffeSally Snake',
'{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{{ my_array | slice: 1,3 }}'
);
is( Template::Liquid->parse(
q[{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{{ my_array | slice: -2,2 }}]
)->render(),
'giraffeSally Snake',
'{% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " %}{{ my_array | slice: -2,2 }}'
);

# sort
note 'For this next test, C<array> is defined as C<[10,62,14,257,65,32]>';
is( Template::Liquid->parse(q[{{array | sort | join: ', ' }}])
Expand All @@ -639,10 +671,40 @@ is( Template::Liquid->parse(
);

# split
is( Template::Liquid->parse(q[{{ values | split: ',' | last }}])
is( Template::Liquid->parse(q[{{ values | split: "," | last }}])
->render(values => 'foo,bar,baz'),
'baz',
q[{{ values | split: ',' | last}}]
q[{{ values | split: "," | last}}]
);
is( Template::Liquid->parse(q[{{ values | split: "::" | last }}])
->render(values => 'foo::bar::baz'),
'baz',
q[{{ values | split: "::" | last}}]
);
is( Template::Liquid->parse(q[{{ values | split: "|" | last }}])
->render(values => 'foo|bar|baz'),
'baz',
q[{{ values | split: "|" | last}}]
);
is( Template::Liquid->parse(q[{{ values | split: "| " | last }}])
->render(values => 'foo| bar| baz'),
'baz',
q[{{ values | split: "| " | last}}]
);
is( Template::Liquid->parse(q[{{ values | split: "| |" | last }}])
->render(values => 'foo| |bar| |baz'),
'baz',
q[{{ values | split: "| |" | last}}]
);
is( Template::Liquid->parse(q[{{ values | split: "." | last }}])
->render(values => 'foo.bar.baz'),
'baz',
q[{{ values | split: "." | last}}]
);
is( Template::Liquid->parse(q[{{ values | split: "(" | last }}])
->render(values => 'foo(bar(baz'),
'baz',
q[{{ values | split: "(" | last}}]
);
is(Template::Liquid->parse(<<'END')->render, <<'OUT', q[...split: ', ' ]);
{%- assign beatles = "John, Paul, George, Ringo" | split: ', ' -%}
Expand Down
6 changes: 6 additions & 0 deletions t/0200_tags/02001_for.t
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ use Template::Liquid;
#{% assign items = "foo"%}{%for item in items%}{{item}}{%endfor%}
$|++;
#
is( Template::Liquid->parse(
<<'TEMPLATE')->render(array => "foo,bar"), <<'EXPECTED', 'string split with index');
{% assign items = array | split: "," %}{% for i in (0..2) %}{{items[i]}}{%endfor%}
TEMPLATE
foobar
EXPECTED
is( Template::Liquid->parse(
<<'TEMPLATE')->render(array => "foo,bar"), <<'EXPECTED', 'string split');
{% assign items = array | split: "," %}{%for item in items%}{{item}}{%endfor%}
Expand Down
91 changes: 91 additions & 0 deletions t/0200_tags/02006_if.t
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,25 @@ is( Template::Liquid->parse(
{% if 'This string' != 'This string' %}Yep.{% endif %}
INPUT

EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(email => 'test@example.com'), <<'EXPECTED', q[assigned list contains email]);
{% assign list = "foo@bar.com, another@example.com, test@example.com" | split: ", " %}{% if list contains email %}Found.{% endif %}
INPUT
Found.
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(list => [qw[some other value]]), <<'EXPECTED', q[list contains 'other']);
{% if list contains 'other' %}Yep.{% endif %}
INPUT
Yep.
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => 'bar'), <<'EXPECTED', q[assigned list contains foo]);
{% assign list = "a, b, c, bar" | split : ", " %}{% if list contains foo %}Yep.{% endif %}
INPUT
Yep.
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(list => [qw[some other value]]), <<'EXPECTED', q[list contains 'missing element']);
{% if list contains 'missing element' %}Yep.{% endif %}
Expand All @@ -123,6 +135,18 @@ is( Template::Liquid->parse(
{% if list_one == list_two %}Yep.{% endif %}
INPUT

EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(equals => 'BLOG'), <<'EXPECTED', q[equals starts with eq]);
{% if equals %}{{equals}}{% endif %}
INPUT
BLOG
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(newsletter => 'BLOG'), <<'EXPECTED', q[newsletter starts with ne]);
{% if newsletter %}{{newsletter}}{% endif %}
INPUT
BLOG
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(hash_one => {key => 'value'}, hash_two => {key => 'value'}), <<'EXPECTED', q[hash_one == hash_two [A]]);
Expand Down Expand Up @@ -303,6 +327,73 @@ INPUT
Not foo or bar

EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => 5), <<'EXPECTED', 'Check "" == 5');
{% if foo == bar %}Yep.{% else %}Nope{% endif %}
INPUT
Nope
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => undef), <<'EXPECTED', 'Check "" != undef');
{% if foo == bar %}Yep.{% else %}Nope{% endif %}
INPUT
Nope
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => undef), <<'EXPECTED', 'Check if ""');
{% if foo %}Yep.{% else %}Nope{% endif %}
INPUT
Nope
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => undef), <<'EXPECTED', 'Check if undef');
{% if bar %}Yep.{% else %}Nope{% endif %}
INPUT
Nope
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => undef), <<'EXPECTED', 'Check undef == ""');
{% if mobile == "" %}BLANKMOBILE{% else %}NOTBLANK{% endif %}
INPUT
NOTBLANK
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => undef), <<'EXPECTED', 'Check undef == ""');
{% unless mobile %}BLANKMOBILE{% else %}NOTBLANK{% endunless %}
INPUT
BLANKMOBILE
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => undef), <<'EXPECTED', 'Check blank and undef');
{% if foo and bar %}FAIL{% else %}PASS{% endif %}
INPUT
PASS
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => undef), <<'EXPECTED', 'Check blank and undef');
{% if mobile and fax %}FAIL{% else %}PASS{% endif %}
INPUT
PASS
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => undef), <<'EXPECTED', 'Check blank or undef');
{% if mobile or fax %}FAIL{% else %}PASS{% endif %}
INPUT
PASS
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(foo => "", bar => undef), <<'EXPECTED', 'Check blank or undef');
{% if foo or bar %}FAIL{% else %}PASS{% endif %}
INPUT
PASS
EXPECTED
is( Template::Liquid->parse(
<<'INPUT')->render(phone => "", bar => undef), <<'EXPECTED', 'Check empty string == "" ');
{% if phone == '' %}PASS{% else %}FAIL{% endif %}
INPUT
PASS
EXPECTED


# I'm finished
done_testing();
Loading