File Coverage

blib/lib/WWW/Shopify/Liquid/Tag/For.pm
Criterion Covered Total %
statement 18 113 15.9
branch 0 72 0.0
condition 0 63 0.0
subroutine 6 16 37.5
pod 0 10 0.0
total 24 274 8.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2 30     30   16342 use strict;
  30         66  
  30         926  
3 30     30   149 use warnings;
  30         48  
  30         1008  
4              
5             package WWW::Shopify::Liquid::Tag::For;
6 30     30   144 use base 'WWW::Shopify::Liquid::Tag::Enclosing';
  30         53  
  30         7464  
7              
8 0     0 0   sub min_arguments { return 1; }
9 0     0 0   sub max_arguments { return 1; }
10              
11             sub verify {
12 0     0 0   my ($self) = @_;
13             die new WWW::Shopify::Liquid::Exception::Parser::Arguments($self, "Requires in operator to be part of loop.") unless
14 0 0         $self->{arguments}->[0]->isa('WWW::Shopify::Liquid::Operator::In');
15             die new WWW::Shopify::Liquid::Exception::Parser::Arguments($self, "Requires the opening variable of a loop to be a simple variable.") unless
16             $self->{arguments}->[0]->{operands}->[0] && $self->{arguments}->[0]->{operands}->[0]->isa('WWW::Shopify::Liquid::Token::Variable') &&
17 0 0 0       int(@{$self->{arguments}->[0]->{operands}->[0]->{core}}) == 1 && $self->{arguments}->[0]->{operands}->[0]->{core}->[0]->isa('WWW::Shopify::Liquid::Token::String');
  0   0        
      0        
18             }
19 0     0 0   sub inner_tags { return qw(else) }
20              
21 30     30   186 use List::Util qw(min);
  30         59  
  30         2239  
22 30     30   160 use Scalar::Util qw(looks_like_number blessed);
  30         58  
  30         1618  
23 30     30   14208 use Clone qw(clone);
  30         74024  
  30         35833  
24              
25              
26              
27             sub new {
28 0     0 0   my $package = shift;
29 0           my $self = bless {
30             line => shift,
31             core => shift,
32             arguments => shift,
33             contents => undef,
34             false_path => undef
35             }, $package;
36 0           $self->interpret_inner_tokens(@{$_[0]});
  0            
37 0           return $self;
38             }
39              
40             sub interpret_inner_tokens {
41 0     0 0   my ($self, @tokens) = @_;
42             # Comes in [for_path], [tag, other_path]...
43 0           my $token = shift(@tokens);
44 0 0         return undef unless $token;
45 0           $self->{contents} = $token->[0];
46 0 0         if (int(@tokens) > 0) {
47 0 0 0       die new WWW::Shopify::Liquid::Exception::Parser($self, "else cannot be anywhere, except the end tag of the for statement.") if int(@tokens) > 1 || $tokens[0]->[0]->tag ne "else";
48 0           $self->{false_path} = $tokens[0]->[1];
49             }
50             }
51              
52             sub render_loop {
53 0     0 0   my ($self, $renderer, $hash, $op1, $op2, $start, $end, @array) = @_;
54            
55 0           my @texts = ();
56 0           my $all_processed = 1;
57 0           my $var = $op1->{core}->[0]->{core};
58 0           my $content;
59            
60 0           for ($start..$end) {
61 0           $hash->{$var} = $array[$_];
62             $hash->{forloop} = {
63 0           index => ($_+1), index0 => $_, first => $_ == 0, last => $_ == $#array,
64             length => int(@array), rindex0 => (($#array - $_) + 1), rindex => (($#array - $_)),
65             };
66 0           eval {
67 0           $content = $self->{contents}->render($renderer, $hash);
68             };
69 0 0         if (my $exp = $@) {
70 0 0 0       if (defined $exp && blessed($exp) && $exp->isa('WWW::Shopify::Liquid::Exception::Control')) {
      0        
71 0 0 0       push(@texts, @{$exp->initial_render}) if $exp->initial_render && int(@{$exp->initial_render}) > 0;
  0            
  0            
72 0 0         if ($exp->isa('WWW::Shopify::Liquid::Exception::Control::Break')) {
    0          
73 0           last;
74             } elsif ($exp->isa('WWW::Shopify::Liquid::Exception::Control::Continue')) {
75 0           next;
76             }
77             } else {
78 0           die $exp;
79             }
80             }
81 0 0         $all_processed = 0 if !$self->is_processed($content);
82 0           push(@texts, $content);
83             }
84            
85 0 0         return join('', grep { defined $_ } @texts) if $all_processed;
  0            
86 0           return $self;
87             }
88              
89             sub expand_concatenations {
90 0     0 0   my ($self, $result) = @_;
91 0 0 0       return blessed($result) && $result->isa('WWW::Shopify::Liquid::Operator::Concatenate') ? (map { $self->expand_concatenations($_) } @{$result->{operands}}) : ($result);
  0            
  0            
92             }
93              
94             sub optimize_loop {
95 0     0 0   my ($self, $optimizer, $hash, $op1, $op2, $start, $end, @array) = @_;
96            
97             # First step, we replace everything by a big concatenate.
98 0           my @texts = ();
99            
100 0           my $var = $op1->{core}->[0]->{core};
101 0 0         return '' if $start > $end;
102             my @parts = map {
103 0           $hash->{$var} = $array[$_];
  0            
104             $hash->{forloop} = {
105 0           index => ($_+1), index0 => $_, first => $_ == 0, last => $_ == $#array,
106             length => int(@array), rindex0 => (($#array - $_) + 1), rindex => (($#array - $_)),
107             };
108 0           my $result;
109 0           eval {
110 0 0         $result = $self->is_processed($self->{contents}) ? $self->{contents} : clone($self->{contents})->optimize($optimizer, $hash);
111             };
112 0 0         if (my $exp = $@) {
113 0 0 0       if (defined $exp && blessed($exp) && $exp->isa('WWW::Shopify::Liquid::Exception::Control')) {
      0        
114 0 0 0       push(@texts, @{$exp->initial_render}) if $exp->initial_render && int(@{$exp->initial_render}) > 0;
  0            
  0            
115 0 0         if ($exp->isa('WWW::Shopify::Liquid::Exception::Control::Break')) {
    0          
116 0           last;
117             } elsif ($exp->isa('WWW::Shopify::Liquid::Exception::Control::Continue')) {
118 0           next;
119             }
120             } else {
121 0           die $exp;
122             }
123             }
124 0           $self->expand_concatenations($result);
125             } ($start..$end);
126 0 0         return $parts[0] if int(@parts) == 1;
127 0           return WWW::Shopify::Liquid::Operator::Concatenate->new($self->{line}, "", @parts);
128             }
129              
130             # Should eventually support loop unrolling.
131             sub process {
132 0     0 0   my ($self, $hash, $action, $pipeline) = @_;
133 0           my @args = @{$self->{arguments}};
  0            
134            
135            
136 0           my ($op1, $op2) = @{$args[0]->{operands}};
  0            
137 0 0         $op2 = $op2->$action($pipeline, $hash) if !$self->is_processed($op2);
138            
139 0 0 0       $self->{arguments}->[0]->{operands}->[1] = $op2 if $self->is_processed($op2) && $action eq 'optimize';
140 0 0 0       return $self if (!$self->is_processed($op2) && $action eq "optimize");
141 0 0 0       return '' if (!$self->is_processed($op2) && $action eq "render");
142 0 0         $op2 = [keys(%$op2)] if ref($op2) eq 'HASH';
143 0 0 0       return (defined $self->{false_path} ? $self->{false_path}->$action($pipeline, $hash) : '') if ref($op2) ne "ARRAY" || int(@$op2) == 0;
    0          
144 0 0         die new WWW::Shopify::Liquid::Exception::Renderer::Arguments($self, "Requires an array in for loop.") unless ref($op2) eq "ARRAY";
145            
146 0           my @array = @$op2;
147 0           my $limit = int(@array);
148 0           my $offset = 0;
149            
150            
151 0 0         my ($limit_arg) = grep { $_->isa('WWW::Shopify::Liquid::Token::Variable::Named') && $_->{name} eq "limit" } @args;
  0            
152 0 0         my $limit_result = $limit_arg->$action($pipeline, $hash) if $limit_arg;
153 0 0         return $self if !$self->is_processed($limit_result);
154 0 0 0       $limit = $limit_result->{limit} if $limit_result && ref($limit_result) && ref($limit_result) eq 'HASH' && looks_like_number($limit_result->{limit});
      0        
      0        
155            
156            
157 0 0         my ($offset_arg) = grep { $_->isa('WWW::Shopify::Liquid::Token::Variable::Named') && $_->{name} eq "offset" } @args;
  0            
158 0 0         my $offset_result = $offset_arg->$action($pipeline, $hash) if $offset_arg;
159 0 0 0       $offset = $offset_result->{offset} if $offset_result && ref($offset_result) && ref($offset_result) eq 'HASH' && looks_like_number($offset_result->{offset});
      0        
      0        
160            
161 0           my $dispatch = $action . "_loop";
162            
163 0           return $self->$dispatch($pipeline, $hash, $op1, $op2, $offset, min($#array, $limit+$offset-1), @array);
164             }
165              
166              
167              
168             1;