liquid 3.0.6 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +154 -58
  3. data/{MIT-LICENSE → LICENSE} +0 -0
  4. data/README.md +33 -0
  5. data/lib/liquid/block.rb +42 -125
  6. data/lib/liquid/block_body.rb +99 -79
  7. data/lib/liquid/condition.rb +52 -32
  8. data/lib/liquid/context.rb +57 -51
  9. data/lib/liquid/document.rb +19 -9
  10. data/lib/liquid/drop.rb +17 -16
  11. data/lib/liquid/errors.rb +20 -24
  12. data/lib/liquid/expression.rb +26 -10
  13. data/lib/liquid/extensions.rb +19 -7
  14. data/lib/liquid/file_system.rb +11 -11
  15. data/lib/liquid/forloop_drop.rb +42 -0
  16. data/lib/liquid/i18n.rb +6 -6
  17. data/lib/liquid/interrupts.rb +1 -2
  18. data/lib/liquid/lexer.rb +12 -8
  19. data/lib/liquid/locales/en.yml +6 -2
  20. data/lib/liquid/parse_context.rb +38 -0
  21. data/lib/liquid/parse_tree_visitor.rb +42 -0
  22. data/lib/liquid/parser_switching.rb +4 -4
  23. data/lib/liquid/profiler/hooks.rb +7 -7
  24. data/lib/liquid/profiler.rb +18 -19
  25. data/lib/liquid/range_lookup.rb +16 -1
  26. data/lib/liquid/resource_limits.rb +23 -0
  27. data/lib/liquid/standardfilters.rb +207 -61
  28. data/lib/liquid/strainer.rb +15 -8
  29. data/lib/liquid/tablerowloop_drop.rb +62 -0
  30. data/lib/liquid/tag.rb +9 -8
  31. data/lib/liquid/tags/assign.rb +25 -4
  32. data/lib/liquid/tags/break.rb +0 -3
  33. data/lib/liquid/tags/capture.rb +1 -1
  34. data/lib/liquid/tags/case.rb +27 -12
  35. data/lib/liquid/tags/comment.rb +2 -2
  36. data/lib/liquid/tags/cycle.rb +16 -8
  37. data/lib/liquid/tags/decrement.rb +1 -4
  38. data/lib/liquid/tags/for.rb +103 -75
  39. data/lib/liquid/tags/if.rb +60 -44
  40. data/lib/liquid/tags/ifchanged.rb +0 -2
  41. data/lib/liquid/tags/include.rb +71 -51
  42. data/lib/liquid/tags/raw.rb +32 -4
  43. data/lib/liquid/tags/table_row.rb +21 -31
  44. data/lib/liquid/tags/unless.rb +3 -4
  45. data/lib/liquid/template.rb +42 -54
  46. data/lib/liquid/tokenizer.rb +31 -0
  47. data/lib/liquid/truffle.rb +5 -0
  48. data/lib/liquid/utils.rb +52 -8
  49. data/lib/liquid/variable.rb +59 -46
  50. data/lib/liquid/variable_lookup.rb +14 -6
  51. data/lib/liquid/version.rb +2 -1
  52. data/lib/liquid.rb +10 -7
  53. data/test/integration/assign_test.rb +8 -8
  54. data/test/integration/blank_test.rb +14 -14
  55. data/test/integration/block_test.rb +12 -0
  56. data/test/integration/context_test.rb +2 -2
  57. data/test/integration/document_test.rb +19 -0
  58. data/test/integration/drop_test.rb +42 -40
  59. data/test/integration/error_handling_test.rb +96 -43
  60. data/test/integration/filter_test.rb +60 -20
  61. data/test/integration/hash_ordering_test.rb +9 -9
  62. data/test/integration/output_test.rb +26 -27
  63. data/test/integration/parse_tree_visitor_test.rb +247 -0
  64. data/test/integration/parsing_quirks_test.rb +19 -13
  65. data/test/integration/render_profiling_test.rb +20 -20
  66. data/test/integration/security_test.rb +23 -7
  67. data/test/integration/standard_filter_test.rb +426 -46
  68. data/test/integration/tags/break_tag_test.rb +1 -2
  69. data/test/integration/tags/continue_tag_test.rb +0 -1
  70. data/test/integration/tags/for_tag_test.rb +135 -100
  71. data/test/integration/tags/if_else_tag_test.rb +75 -77
  72. data/test/integration/tags/include_tag_test.rb +50 -31
  73. data/test/integration/tags/increment_tag_test.rb +10 -11
  74. data/test/integration/tags/raw_tag_test.rb +7 -1
  75. data/test/integration/tags/standard_tag_test.rb +121 -122
  76. data/test/integration/tags/statements_test.rb +3 -5
  77. data/test/integration/tags/table_row_test.rb +20 -19
  78. data/test/integration/tags/unless_else_tag_test.rb +6 -6
  79. data/test/integration/template_test.rb +199 -49
  80. data/test/integration/trim_mode_test.rb +529 -0
  81. data/test/integration/variable_test.rb +27 -13
  82. data/test/test_helper.rb +33 -6
  83. data/test/truffle/truffle_test.rb +9 -0
  84. data/test/unit/block_unit_test.rb +8 -5
  85. data/test/unit/condition_unit_test.rb +94 -77
  86. data/test/unit/context_unit_test.rb +69 -72
  87. data/test/unit/file_system_unit_test.rb +3 -3
  88. data/test/unit/i18n_unit_test.rb +2 -2
  89. data/test/unit/lexer_unit_test.rb +12 -9
  90. data/test/unit/parser_unit_test.rb +2 -2
  91. data/test/unit/regexp_unit_test.rb +1 -1
  92. data/test/unit/strainer_unit_test.rb +96 -1
  93. data/test/unit/tag_unit_test.rb +7 -2
  94. data/test/unit/tags/case_tag_unit_test.rb +1 -1
  95. data/test/unit/tags/for_tag_unit_test.rb +2 -2
  96. data/test/unit/tags/if_tag_unit_test.rb +1 -1
  97. data/test/unit/template_unit_test.rb +14 -5
  98. data/test/unit/tokenizer_unit_test.rb +24 -7
  99. data/test/unit/variable_unit_test.rb +60 -43
  100. metadata +62 -50
  101. data/lib/liquid/module_ex.rb +0 -62
  102. data/lib/liquid/token.rb +0 -18
  103. data/test/unit/module_ex_unit_test.rb +0 -87
data/lib/liquid/tag.rb CHANGED
@@ -1,26 +1,27 @@
1
1
  module Liquid
2
2
  class Tag
3
- attr_accessor :options, :line_number
4
- attr_reader :nodelist, :warnings
3
+ attr_reader :nodelist, :tag_name, :line_number, :parse_context
4
+ alias_method :options, :parse_context
5
5
  include ParserSwitching
6
6
 
7
7
  class << self
8
- def parse(tag_name, markup, tokens, options)
8
+ def parse(tag_name, markup, tokenizer, options)
9
9
  tag = new(tag_name, markup, options)
10
- tag.parse(tokens)
10
+ tag.parse(tokenizer)
11
11
  tag
12
12
  end
13
13
 
14
14
  private :new
15
15
  end
16
16
 
17
- def initialize(tag_name, markup, options)
17
+ def initialize(tag_name, markup, parse_context)
18
18
  @tag_name = tag_name
19
19
  @markup = markup
20
- @options = options
20
+ @parse_context = parse_context
21
+ @line_number = parse_context.line_number
21
22
  end
22
23
 
23
- def parse(tokens)
24
+ def parse(_tokens)
24
25
  end
25
26
 
26
27
  def raw
@@ -31,7 +32,7 @@ module Liquid
31
32
  self.class.name.downcase
32
33
  end
33
34
 
34
- def render(context)
35
+ def render(_context)
35
36
  ''.freeze
36
37
  end
37
38
 
@@ -1,5 +1,4 @@
1
1
  module Liquid
2
-
3
2
  # Assign sets a variable in your template.
4
3
  #
5
4
  # {% assign foo = 'monkey' %}
@@ -11,12 +10,13 @@ module Liquid
11
10
  class Assign < Tag
12
11
  Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
13
12
 
13
+ attr_reader :to, :from
14
+
14
15
  def initialize(tag_name, markup, options)
15
16
  super
16
17
  if markup =~ Syntax
17
18
  @to = $1
18
- @from = Variable.new($2,options)
19
- @from.line_number = line_number
19
+ @from = Variable.new($2, options)
20
20
  else
21
21
  raise SyntaxError.new options[:locale].t("errors.syntax.assign".freeze)
22
22
  end
@@ -25,13 +25,34 @@ module Liquid
25
25
  def render(context)
26
26
  val = @from.render(context)
27
27
  context.scopes.last[@to] = val
28
- context.increment_used_resources(:assign_score_current, val)
28
+ context.resource_limits.assign_score += assign_score_of(val)
29
29
  ''.freeze
30
30
  end
31
31
 
32
32
  def blank?
33
33
  true
34
34
  end
35
+
36
+ private
37
+
38
+ def assign_score_of(val)
39
+ if val.instance_of?(String)
40
+ val.length
41
+ elsif val.instance_of?(Array) || val.instance_of?(Hash)
42
+ sum = 1
43
+ # Uses #each to avoid extra allocations.
44
+ val.each { |child| sum += assign_score_of(child) }
45
+ sum
46
+ else
47
+ 1
48
+ end
49
+ end
50
+
51
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
52
+ def children
53
+ [@node.from]
54
+ end
55
+ end
35
56
  end
36
57
 
37
58
  Template.register_tag('assign'.freeze, Assign)
@@ -1,5 +1,4 @@
1
1
  module Liquid
2
-
3
2
  # Break tag to be used to break out of a for loop.
4
3
  #
5
4
  # == Basic Usage:
@@ -10,11 +9,9 @@ module Liquid
10
9
  # {% endfor %}
11
10
  #
12
11
  class Break < Tag
13
-
14
12
  def interrupt
15
13
  BreakInterrupt.new
16
14
  end
17
-
18
15
  end
19
16
 
20
17
  Template.register_tag('break'.freeze, Break)
@@ -25,7 +25,7 @@ module Liquid
25
25
  def render(context)
26
26
  output = super
27
27
  context.scopes.last[@to] = output
28
- context.increment_used_resources(:assign_score_current, output)
28
+ context.resource_limits.assign_score += output.length
29
29
  ''.freeze
30
30
  end
31
31
 
@@ -3,23 +3,31 @@ module Liquid
3
3
  Syntax = /(#{QuotedFragment})/o
4
4
  WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
5
5
 
6
+ attr_reader :blocks, :left
7
+
6
8
  def initialize(tag_name, markup, options)
7
9
  super
8
10
  @blocks = []
9
11
 
10
12
  if markup =~ Syntax
11
- @left = $1
13
+ @left = Expression.parse($1)
12
14
  else
13
15
  raise SyntaxError.new(options[:locale].t("errors.syntax.case".freeze))
14
16
  end
15
17
  end
16
18
 
19
+ def parse(tokens)
20
+ body = BlockBody.new
21
+ while parse_body(body, tokens)
22
+ body = @blocks.last.attachment
23
+ end
24
+ end
25
+
17
26
  def nodelist
18
- @blocks.flat_map(&:attachment)
27
+ @blocks.map(&:attachment)
19
28
  end
20
29
 
21
30
  def unknown_tag(tag, markup, tokens)
22
- @nodelist = []
23
31
  case tag
24
32
  when 'when'.freeze
25
33
  record_when_condition(markup)
@@ -37,10 +45,10 @@ module Liquid
37
45
  output = ''
38
46
  @blocks.each do |block|
39
47
  if block.else?
40
- return render_all(block.attachment, context) if execute_else_block
48
+ return block.attachment.render(context) if execute_else_block
41
49
  elsif block.evaluate(context)
42
50
  execute_else_block = false
43
- output << render_all(block.attachment, context)
51
+ output << block.attachment.render(context)
44
52
  end
45
53
  end
46
54
  output
@@ -50,29 +58,36 @@ module Liquid
50
58
  private
51
59
 
52
60
  def record_when_condition(markup)
61
+ body = BlockBody.new
62
+
53
63
  while markup
54
- # Create a new nodelist and assign it to the new block
55
- if not markup =~ WhenSyntax
64
+ unless markup =~ WhenSyntax
56
65
  raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_when".freeze))
57
66
  end
58
67
 
59
68
  markup = $2
60
69
 
61
- block = Condition.new(@left, '=='.freeze, $1)
62
- block.attach(@nodelist)
63
- @blocks.push(block)
70
+ block = Condition.new(@left, '=='.freeze, Expression.parse($1))
71
+ block.attach(body)
72
+ @blocks << block
64
73
  end
65
74
  end
66
75
 
67
76
  def record_else_condition(markup)
68
- if not markup.strip.empty?
77
+ unless markup.strip.empty?
69
78
  raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_else".freeze))
70
79
  end
71
80
 
72
81
  block = ElseCondition.new
73
- block.attach(@nodelist)
82
+ block.attach(BlockBody.new)
74
83
  @blocks << block
75
84
  end
85
+
86
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
87
+ def children
88
+ [@node.left] + @node.blocks
89
+ end
90
+ end
76
91
  end
77
92
 
78
93
  Template.register_tag('case'.freeze, Case)
@@ -1,10 +1,10 @@
1
1
  module Liquid
2
2
  class Comment < Block
3
- def render(context)
3
+ def render(_context)
4
4
  ''.freeze
5
5
  end
6
6
 
7
- def unknown_tag(tag, markup, tokens)
7
+ def unknown_tag(_tag, _markup, _tokens)
8
8
  end
9
9
 
10
10
  def blank?
@@ -15,29 +15,31 @@ module Liquid
15
15
  SimpleSyntax = /\A#{QuotedFragment}+/o
16
16
  NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
17
17
 
18
+ attr_reader :variables
19
+
18
20
  def initialize(tag_name, markup, options)
19
21
  super
20
22
  case markup
21
23
  when NamedSyntax
22
24
  @variables = variables_from_string($2)
23
- @name = $1
25
+ @name = Expression.parse($1)
24
26
  when SimpleSyntax
25
27
  @variables = variables_from_string(markup)
26
- @name = "'#{@variables.to_s}'"
28
+ @name = @variables.to_s
27
29
  else
28
30
  raise SyntaxError.new(options[:locale].t("errors.syntax.cycle".freeze))
29
31
  end
30
32
  end
31
33
 
32
34
  def render(context)
33
- context.registers[:cycle] ||= Hash.new(0)
35
+ context.registers[:cycle] ||= {}
34
36
 
35
37
  context.stack do
36
- key = context[@name]
37
- iteration = context.registers[:cycle][key]
38
- result = context[@variables[iteration]]
38
+ key = context.evaluate(@name)
39
+ iteration = context.registers[:cycle][key].to_i
40
+ result = context.evaluate(@variables[iteration])
39
41
  iteration += 1
40
- iteration = 0 if iteration >= @variables.size
42
+ iteration = 0 if iteration >= @variables.size
41
43
  context.registers[:cycle][key] = iteration
42
44
  result
43
45
  end
@@ -48,9 +50,15 @@ module Liquid
48
50
  def variables_from_string(markup)
49
51
  markup.split(',').collect do |var|
50
52
  var =~ /\s*(#{QuotedFragment})\s*/o
51
- $1 ? $1 : nil
53
+ $1 ? Expression.parse($1) : nil
52
54
  end.compact
53
55
  end
56
+
57
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
58
+ def children
59
+ Array(@node.variables)
60
+ end
61
+ end
54
62
  end
55
63
 
56
64
  Template.register_tag('cycle', Cycle)
@@ -1,5 +1,4 @@
1
1
  module Liquid
2
-
3
2
  # decrement is used in a place where one needs to insert a counter
4
3
  # into a template, and needs the counter to survive across
5
4
  # multiple instantiations of the template.
@@ -26,12 +25,10 @@ module Liquid
26
25
 
27
26
  def render(context)
28
27
  value = context.environments.first[@variable] ||= 0
29
- value = value - 1
28
+ value -= 1
30
29
  context.environments.first[@variable] = value
31
30
  value.to_s
32
31
  end
33
-
34
- private
35
32
  end
36
33
 
37
34
  Template.register_tag('decrement'.freeze, Decrement)
@@ -1,5 +1,4 @@
1
1
  module Liquid
2
-
3
2
  # "For" iterates over an array or collection.
4
3
  # Several useful variables are available to you within the loop.
5
4
  #
@@ -24,7 +23,7 @@ module Liquid
24
23
  # {{ item.name }}
25
24
  # {% end %}
26
25
  #
27
- # To reverse the for loop simply use {% for item in collection reversed %}
26
+ # To reverse the for loop simply use {% for item in collection reversed %} (note that the flag's spelling is different to the filter `reverse`)
28
27
  #
29
28
  # == Available variables:
30
29
  #
@@ -42,85 +41,43 @@ module Liquid
42
41
  # where 0 is the last item.
43
42
  # forloop.first:: Returns true if the item is the first item.
44
43
  # forloop.last:: Returns true if the item is the last item.
44
+ # forloop.parentloop:: Provides access to the parent loop, if present.
45
45
  #
46
46
  class For < Block
47
47
  Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
48
48
 
49
+ attr_reader :collection_name, :variable_name, :limit, :from
50
+
49
51
  def initialize(tag_name, markup, options)
50
52
  super
53
+ @from = @limit = nil
51
54
  parse_with_selected_parser(markup)
52
- @nodelist = @for_block = []
55
+ @for_block = BlockBody.new
56
+ @else_block = nil
57
+ end
58
+
59
+ def parse(tokens)
60
+ return unless parse_body(@for_block, tokens)
61
+ parse_body(@else_block, tokens)
53
62
  end
54
63
 
55
64
  def nodelist
56
- if @else_block
57
- @for_block + @else_block
58
- else
59
- @for_block
60
- end
65
+ @else_block ? [@for_block, @else_block] : [@for_block]
61
66
  end
62
67
 
63
68
  def unknown_tag(tag, markup, tokens)
64
69
  return super unless tag == 'else'.freeze
65
- @nodelist = @else_block = []
70
+ @else_block = BlockBody.new
66
71
  end
67
72
 
68
73
  def render(context)
69
- context.registers[:for] ||= Hash.new(0)
70
-
71
- collection = context[@collection_name]
72
- collection = collection.to_a if collection.is_a?(Range)
74
+ segment = collection_segment(context)
73
75
 
74
- # Maintains Ruby 1.8.7 String#each behaviour on 1.9
75
- return render_else(context) unless iterable?(collection)
76
-
77
- from = if @attributes['offset'.freeze] == 'continue'.freeze
78
- context.registers[:for][@name].to_i
76
+ if segment.empty?
77
+ render_else(context)
79
78
  else
80
- context[@attributes['offset'.freeze]].to_i
81
- end
82
-
83
- limit = context[@attributes['limit'.freeze]]
84
- to = limit ? limit.to_i + from : nil
85
-
86
- segment = Utils.slice_collection(collection, from, to)
87
-
88
- return render_else(context) if segment.empty?
89
-
90
- segment.reverse! if @reversed
91
-
92
- result = ''
93
-
94
- length = segment.length
95
-
96
- # Store our progress through the collection for the continue flag
97
- context.registers[:for][@name] = from + segment.length
98
-
99
- context.stack do
100
- segment.each_with_index do |item, index|
101
- context[@variable_name] = item
102
- context['forloop'.freeze] = {
103
- 'name'.freeze => @name,
104
- 'length'.freeze => length,
105
- 'index'.freeze => index + 1,
106
- 'index0'.freeze => index,
107
- 'rindex'.freeze => length - index,
108
- 'rindex0'.freeze => length - index - 1,
109
- 'first'.freeze => (index == 0),
110
- 'last'.freeze => (index == length - 1)
111
- }
112
-
113
- result << render_all(@for_block, context)
114
-
115
- # Handle any interrupts if they exist.
116
- if context.has_interrupt?
117
- interrupt = context.pop_interrupt
118
- break if interrupt.is_a? BreakInterrupt
119
- next if interrupt.is_a? ContinueInterrupt
120
- end
121
- end
79
+ render_segment(context, segment)
122
80
  end
123
- result
124
81
  end
125
82
 
126
83
  protected
@@ -128,12 +85,12 @@ module Liquid
128
85
  def lax_parse(markup)
129
86
  if markup =~ Syntax
130
87
  @variable_name = $1
131
- @collection_name = $2
132
- @name = "#{$1}-#{$2}"
133
- @reversed = $3
134
- @attributes = {}
88
+ collection_name = $2
89
+ @reversed = !!$3
90
+ @name = "#{@variable_name}-#{collection_name}"
91
+ @collection_name = Expression.parse(collection_name)
135
92
  markup.scan(TagAttributes) do |key, value|
136
- @attributes[key] = value
93
+ set_attribute(key, value)
137
94
  end
138
95
  else
139
96
  raise SyntaxError.new(options[:locale].t("errors.syntax.for".freeze))
@@ -143,31 +100,102 @@ module Liquid
143
100
  def strict_parse(markup)
144
101
  p = Parser.new(markup)
145
102
  @variable_name = p.consume(:id)
146
- raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze)
147
- @collection_name = p.expression
148
- @name = "#{@variable_name}-#{@collection_name}"
103
+ raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze)
104
+ collection_name = p.expression
105
+ @name = "#{@variable_name}-#{collection_name}"
106
+ @collection_name = Expression.parse(collection_name)
149
107
  @reversed = p.id?('reversed'.freeze)
150
108
 
151
- @attributes = {}
152
109
  while p.look(:id) && p.look(:colon, 1)
153
110
  unless attribute = p.id?('limit'.freeze) || p.id?('offset'.freeze)
154
111
  raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_attribute".freeze))
155
112
  end
156
113
  p.consume
157
- val = p.expression
158
- @attributes[attribute] = val
114
+ set_attribute(attribute, p.expression)
159
115
  end
160
116
  p.consume(:end_of_string)
161
117
  end
162
118
 
163
119
  private
164
120
 
121
+ def collection_segment(context)
122
+ offsets = context.registers[:for] ||= {}
123
+
124
+ from = if @from == :continue
125
+ offsets[@name].to_i
126
+ else
127
+ context.evaluate(@from).to_i
128
+ end
129
+
130
+ collection = context.evaluate(@collection_name)
131
+ collection = collection.to_a if collection.is_a?(Range)
132
+
133
+ limit = context.evaluate(@limit)
134
+ to = limit ? limit.to_i + from : nil
135
+
136
+ segment = Utils.slice_collection(collection, from, to)
137
+ segment.reverse! if @reversed
138
+
139
+ offsets[@name] = from + segment.length
140
+
141
+ segment
142
+ end
143
+
144
+ def render_segment(context, segment)
145
+ for_stack = context.registers[:for_stack] ||= []
146
+ length = segment.length
147
+
148
+ result = ''
149
+
150
+ context.stack do
151
+ loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
152
+
153
+ for_stack.push(loop_vars)
154
+
155
+ begin
156
+ context['forloop'.freeze] = loop_vars
157
+
158
+ segment.each do |item|
159
+ context[@variable_name] = item
160
+ result << @for_block.render(context)
161
+ loop_vars.send(:increment!)
162
+
163
+ # Handle any interrupts if they exist.
164
+ if context.interrupt?
165
+ interrupt = context.pop_interrupt
166
+ break if interrupt.is_a? BreakInterrupt
167
+ next if interrupt.is_a? ContinueInterrupt
168
+ end
169
+ end
170
+ ensure
171
+ for_stack.pop
172
+ end
173
+ end
174
+
175
+ result
176
+ end
177
+
178
+ def set_attribute(key, expr)
179
+ case key
180
+ when 'offset'.freeze
181
+ @from = if expr == 'continue'.freeze
182
+ :continue
183
+ else
184
+ Expression.parse(expr)
185
+ end
186
+ when 'limit'.freeze
187
+ @limit = Expression.parse(expr)
188
+ end
189
+ end
190
+
165
191
  def render_else(context)
166
- return @else_block ? [render_all(@else_block, context)] : ''.freeze
192
+ @else_block ? @else_block.render(context) : ''.freeze
167
193
  end
168
194
 
169
- def iterable?(collection)
170
- collection.respond_to?(:each) || Utils.non_blank_string?(collection)
195
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
196
+ def children
197
+ (super + [@node.limit, @node.from, @node.collection_name]).compact
198
+ end
171
199
  end
172
200
  end
173
201