File Coverage

blib/lib/Template/Liquid/Condition.pm
Criterion Covered Total %
statement 95 114 83.3
branch 67 96 69.7
condition 23 48 47.9
subroutine 14 16 87.5
pod 5 9 55.5
total 204 283 72.0


line stmt bran cond sub pod time code
1             package Template::Liquid::Condition;
2             our $VERSION = '1.0.23';
3             require Template::Liquid::Error;
4 25     25   179 use base 'Template::Liquid::Block';
  25         64  
  25         1823  
5 25     25   171 use strict;
  25         51  
  25         519  
6 25     25   135 use warnings;
  25         56  
  25         1531  
7              
8             # Makes life easy
9 25     25   32319 use overload 'bool' => \&is_true, fallback => 1;
  25         25993  
  25         212  
10              
11             sub new {
12 275     275 0 619 my ($class, $args) = @_;
13             raise Template::Liquid::Error {type => 'Context',
14             template => $args->{template},
15             message => 'Missing template argument',
16             fatal => 1
17             }
18 275 50       647 if !defined $args->{'template'};
19             raise Template::Liquid::Error {type => 'Context',
20             template => $args->{template},
21             message => 'Missing parent argument',
22             fatal => 1
23             }
24 275 50       605 if !defined $args->{'parent'};
25             my ($lval, $condition, $rval)
26 275 50       1897 = ((defined $args->{'attrs'} ? $args->{'attrs'} : '')
27             =~ m[("[^"]+"|'[^']+'|(?:eq|==|ne|!=|lt|<|gt|>|contains|&&|\|\|)|(?:[\w.]+))]go
28             );
29 275 50       723 if (defined $lval) {
30 275 100 66     1244 if (!defined $rval && !defined $condition) {
    50          
31             return
32             bless {lvalue => $lval,
33             condition => undef,
34             rvalue => undef,
35             template => $args->{'template'},
36 129         1113 parent => $args->{'parent'}
37             }, $class;
38             }
39             elsif ($condition =~ m[^(?:eq|==|ne|!=|lt|<|gt|>|contains|&&|\|\|)$]o)
40 146 100       369 { $condition = 'eq' if $condition eq '==';
41 146 100       295 $condition = 'ne' if $condition eq '!=';
42 146 100       292 $condition = 'gt' if $condition eq '>';
43 146 100       278 $condition = 'lt' if $condition eq '<';
44 146 50       270 $condition = '_and' if $condition eq '&&';
45 146 50       282 $condition = '_and' if $condition eq 'and';
46 146 50       305 $condition = '_or' if $condition eq '||';
47 146 50       298 $condition = '_or' if $condition eq 'or';
48             return
49             bless {lvalue => $lval,
50             condition => $condition,
51             rvalue => $rval,
52             template => $args->{'template'},
53 146         1250 parent => $args->{'parent'}
54             }, $class;
55             }
56             else {
57 0         0 ...;
58             }
59             raise Template::Liquid::Error {template => $args->{template},
60 0 0       0 type => 'Context',
61             message => 'Unknown operator "' .
62             $condition . '" in ' .
63             $lval . ' ' .
64             $condition . ' ' .
65             (defined $rval ? $rval : '')
66             };
67             }
68             return
69             Template::Liquid::Error->new(
70             template => $args->{template},
71             type => 'Context',
72 0         0 message => 'Bad conditional statement: ' . $args->{'attrs'}
73             );
74             }
75 27     27 1 59 sub ne { return !$_[0]->eq } # hashes
76              
77             sub eq {
78 279     279 1 465 my ($s) = @_;
79 279   66     772 my $l = $s->{template}{context}->get($s->{'lvalue'}) || $s->{'lvalue'};
80 279   66     774 my $r = $s->{template}{context}->get($s->{'rvalue'}) || $s->{'rvalue'};
81              
82             # Might need to render these again
83 279         750 my $_l = $s->{template}{context}->get($l);
84 279         722 my $_r = $s->{template}{context}->get($r);
85 279 100       681 $l = $_l if defined $_l;
86 279 100       546 $r = $_r if defined $_r;
87 279         529 return _equal($l, $r);
88             }
89              
90             sub _equal { # XXX - Pray we don't have a recursive data structure...
91 299     299   600 my ($l, $r) = @_;
92 299         457 my $ref_l = ref $l;
93 299 100       616 return !1 if $ref_l ne ref $r;
94 297 100       592 if (!$ref_l) {
    100          
    50          
95             return
96 570         1419 !!(grep {defined} $l, $r)
97 285 100       503 ? (grep {m[\D]o} $l, $r)
  570 50       2611  
98             ? $l eq $r
99             : $l == $r
100             : !1;
101             }
102             elsif ($ref_l eq 'ARRAY') {
103 6 100       27 return !1 unless scalar @$l == scalar @$r;
104 4         7 for my $index (0 .. $#{$l}) {
  4         15  
105 16 100       40 return !1 if !_equal($l->[$index], $r->[$index]);
106             }
107 2         12 return !!1;
108             }
109             elsif ($ref_l eq 'HASH') {
110 6         58 my %temp = %$r;
111 6         23 for my $key (keys %$l) {
112             return 0
113             unless exists $temp{$key} and
114             defined($l->{$key}) eq defined($temp{$key}) and
115 6 50 66     50 (defined $temp{$key} ? _equal($temp{$key}, $l->{$key}) : !!1);
    100 100        
116 2         8 delete $temp{$key};
117             }
118 2         20 return !keys(%temp);
119             }
120             }
121              
122             sub gt {
123 49     49 1 101 my ($s) = @_;
124             my ($l, $r)
125 98 100       270 = map { $s->{template}{context}->get($_) || $_ }
126 49         98 ($$s{'lvalue'}, $$s{'rvalue'});
127              
128             # Might need to render these again
129 49         165 my $_l = $s->{template}{context}->get($l);
130 49         144 my $_r = $s->{template}{context}->get($r);
131 49 100       147 $l = $_l if defined $_l;
132 49 50       133 $r = $_r if defined $_r;
133             return
134 98         254 !!(grep {defined} $l, $r)
135 49 100       98 ? (grep {m[\D]o} $l, $r)
  98 50       552  
136             ? $l gt $r
137             : $l > $r
138             : 0;
139             }
140 16   100 16 1 53 sub lt { return (!$_[0]->gt && !$_[0]->eq) }
141              
142             sub contains {
143 12     12 1 24 my ($s) = @_;
144 12         39 my $l = $s->{template}{context}->get($s->{'lvalue'});
145 12         38 my $r = $s->{template}{context}->get($s->{'rvalue'});
146              
147             # Might need to render these again
148 12         36 my $_l = $s->{template}{context}->get($l);
149 12         37 my $_r = $s->{template}{context}->get($r);
150 12 50       28 $l = $_l if defined $_l;
151 12 50       26 $r = $_r if defined $_r;
152 12         25 $r = quotemeta $r;
153 12 50 33     70 return if defined $r && !defined $l;
154 12 100       54 return defined($l->{$r}) ? 1 : !1 if ref $l eq 'HASH';
    100          
155 8 100       21 return (grep { $_ eq $r } @$l) ? 1 : !1 if ref $l eq 'ARRAY';
  12 100       50  
156 4 100       112 return $l =~ qr[${r}] ? 1 : !1;
157             }
158              
159             sub _and {
160 0     0   0 my ($s) = @_;
161 0   0     0 my $l = $s->{template}{context}->get($s->{'lvalue'}) || $s->{'lvalue'};
162 0   0     0 my $r = $s->{template}{context}->get($s->{'rvalue'}) || $s->{'rvalue'};
163              
164             # Might need to render these again
165 0         0 my $_l = $s->{template}{context}->get($l);
166 0         0 my $_r = $s->{template}{context}->get($r);
167 0 0       0 $l = $_l if defined $_l;
168 0 0       0 $r = $_r if defined $_r;
169 0   0     0 return !!($l && $r);
170             }
171              
172             sub _or {
173 0     0   0 my ($s) = @_;
174 0   0     0 my $l = $s->{template}{context}->get($s->{'lvalue'}) || $s->{'lvalue'};
175 0   0     0 my $r = $s->{template}{context}->get($s->{'rvalue'}) || $s->{'rvalue'};
176              
177             # Might need to render these again
178 0         0 my $_l = $s->{template}{context}->get($l);
179 0         0 my $_r = $s->{template}{context}->get($r);
180 0 0       0 $l = $_l if defined $_l;
181 0 0       0 $r = $_r if defined $_r;
182 0   0     0 return !!($l || $r);
183             }
184             { # Compound inequalities support
185              
186             sub and {
187 15     15 0 36 my ($s) = @_;
188 15         37 my $l = $s->{'lvalue'};
189 15         27 my $r = $s->{'rvalue'};
190 15   100     34 return !!($l && $r);
191             }
192              
193             sub or {
194 43     43 0 75 my ($s) = @_;
195 43         72 my $l = $s->{'lvalue'};
196 43         60 my $r = $s->{'rvalue'};
197 43   100     108 return !!($l || $r);
198             }
199             }
200              
201             sub is_true {
202 416     416 0 841 my ($s) = @_;
203 416 50 66     1126 if (!defined $s->{'condition'} && !defined $s->{'rvalue'}) {
204              
205             # Might need to render these again
206 28         91 my $l = $s->{template}{context}->get($s->{'lvalue'});
207 28         81 my $_l = $s->{template}{context}->get($l);
208 28 100       92 $l = $_l if defined $_l;
209 28 100       197 return !!($l) ? 1 : 0;
210             }
211 388         1101 my $condition = $s->can($s->{'condition'});
212             raise Template::Liquid::Error {
213             template => $s->{template},
214             type => 'Context',
215 388 50       866 message => 'Bad condition ' . $s->{'condition'},
216             fatal => 1
217             }
218             if !$condition;
219              
220             #return !1 if !$condition;
221 388         721 return $s->$condition();
222             }
223             1;
224              
225             =pod
226              
227             =begin stopwords
228              
229             stringwise greps
230              
231             =end stopwords
232              
233             =head1 NAME
234              
235             Template::Liquid::Condition - Basic Relational, Equality, and Content Operators
236              
237             =head1 Description
238              
239             These operators evaluate to true/false values. This is used internally but
240             since you're here... might as well skim it. Nothing new to most people.
241              
242             =head1 Relational Operators
243              
244             If you're familiar with basic math, you already understand these.
245              
246             Any of the following operators can be combined with logical C<and> and C<or>:
247              
248             5 > 2 and 'okay' contains 'ok' # true
249             4 > 6 or 4 < 6 # true ...silly, but true
250             # where x = [1 .. 10]
251             x contains 3 and x contains 0 # false
252              
253             Binary C<and> performs a short-circuit logical AND operation. That is, if the
254             left operand is false, the right operand is not even evaluated. Likewise,
255             binary C<or> performs a short-circuit logical OR operation. That is, if the
256             left operand is true, the right operand is not even evaluated.
257              
258             =head2 C<< > >>
259              
260             Binary operator which returns true if the left argument is numerically less
261             than the right argument.
262              
263             Given...
264              
265             3 > 4 # false
266             4 > 3 # true
267             # where x == 10 and y == 12
268             x > y # false
269             y > x # true
270             x > 3 # true
271             x > x # false
272              
273             =head2 C<< < >>
274              
275             Binary operator which returns true if the left argument is numerically greater
276             than the right argument.
277              
278             Given...
279              
280             3 < 4 # true
281             4 < 3 # false
282             # where x == 10 and y == 12
283             x < y # true
284             y < x # false
285             x < 30 # true
286             x < x # false
287              
288             =head2 C<==>
289              
290             Binary operator which returns true if the left argument is numerically equal to
291             the right argument.
292              
293             # where x == 10 and y == 12
294             x == y # false
295             x == 10 # true
296             y == y # true
297              
298             =head2 C<!=>
299              
300             Binary operator which returns true if the left argument is numerically not
301             equal to the right argument.
302              
303             # where x == 10 and y == 12
304             x != y # true
305             5 != 5 # false
306              
307             =head2 C<eq>
308              
309             Binary operator which returns true if the left argument is stringwise equal to
310             the right argument.
311              
312             'test' eq 'test' # true
313             'test' eq 'reset' # false
314             # where x = 'cool beans'
315             x eq 'awesome' # false
316             x eq 'Cool beans' # false
317             x eq 'cool beans' # true
318             x eq x # true
319              
320             =head2 C<ne>
321              
322             Binary operator which returns true if the left argument is stringwise not equal
323             to the right argument.
324              
325             'test' ne 'test' # false
326             'test' ne 'reset' # true
327             # where x = 'cool beans'
328             x ne 'awesome' # true
329             x ne 'Cool beans' # true
330             x ne 'cool beans' # false
331             x ne x # false
332              
333             =head2 C<lt>
334              
335             Binary operator which returns true if the left argument is stringwise less than
336             the right argument.
337              
338             'a' lt 'c' # true
339             'A' lt 'a' # true
340             # where x = 'q'
341             x lt 'r' # true
342             x lt 'm' # false
343             x lt x # false
344              
345             =head2 C<gt>
346              
347             Binary operator which returns true if the left argument is stringwise greater
348             than the right argument.
349              
350             'a' gt 'c' # false
351             'A' gt 'a' # false
352             # where x = 'q'
353             x gt 'r' # false
354             x gt 'm' # true
355             x gt x # true
356              
357             =head1 Other Operators
358              
359             These are nice things to have around...
360              
361             =head2 C<contains>
362              
363             The C<contains> operator is context sensitive.
364              
365             =head3 Strings
366              
367             If the variable on the left is a string, this operator searches the string for
368             a pattern match, and (as if in scalar context) returns true if it succeeds,
369             false if it fails.
370              
371             Note that this is a simple C<$x =~ qr[${y}]> match. Case matters.
372              
373             Given...
374              
375             # where x = 'The Angels have the police box!'
376             x contains 'police' # true
377             x contains 'Police' # false
378             x contains 'police box?' # false
379             x contains 'police box!' # true
380             x contains x # true
381              
382             =head3 Lists
383              
384             If the variable is a list, the operator greps the list to find the attribute.
385             If found, a true value is returned. Otherwise, the return value is false.
386              
387             Given...
388              
389             # where x = ['one', 'two', 'three']
390             x contains 'five' # false
391             x contains 'six' # false
392             x contains 'one' # true
393              
394             =head3 Hashes
395              
396             If the variable is a hash reference, the operator returns true if the specified
397             element in the hash has ever been initialized, even if the corresponding value
398             is undefined.
399              
400             Given...
401              
402             # where x = { okay => 'okay', blah => undef }
403             x contains 'okay' # false
404             x contains 'alright' # false
405             x contains 'blah' # true
406              
407             =head1 Known Bugs
408              
409             None right now. Give it time.
410              
411             =head1 Author
412              
413             Sanko Robinson <sanko@cpan.org> - http://sankorobinson.com/
414              
415             =head1 License and Legal
416              
417             Copyright (C) 2009-2022 by Sanko Robinson E<lt>sanko@cpan.orgE<gt>
418              
419             This program is free software; you can redistribute it and/or modify it under
420             the terms of The Artistic License 2.0. See the F<LICENSE> file included with
421             this distribution or http://www.perlfoundation.org/artistic_license_2_0. For
422             clarification, see http://www.perlfoundation.org/artistic_2_0_notes.
423              
424             When separated from the distribution, all original POD documentation is covered
425             by the Creative Commons Attribution-Share Alike 3.0 License. See
426             http://creativecommons.org/licenses/by-sa/3.0/us/legalcode. For clarification,
427             see http://creativecommons.org/licenses/by-sa/3.0/us/.
428              
429             =cut