liquid 4.0.0 → 5.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/History.md +235 -2
- data/README.md +58 -8
- data/lib/liquid/block.rb +51 -20
- data/lib/liquid/block_body.rb +216 -82
- data/lib/liquid/condition.rb +83 -32
- data/lib/liquid/const.rb +8 -0
- data/lib/liquid/context.rb +130 -59
- data/lib/liquid/deprecations.rb +22 -0
- data/lib/liquid/document.rb +47 -9
- data/lib/liquid/drop.rb +8 -2
- data/lib/liquid/environment.rb +159 -0
- data/lib/liquid/errors.rb +23 -20
- data/lib/liquid/expression.rb +114 -31
- data/lib/liquid/extensions.rb +8 -0
- data/lib/liquid/file_system.rb +6 -4
- data/lib/liquid/forloop_drop.rb +51 -4
- data/lib/liquid/i18n.rb +5 -3
- data/lib/liquid/interrupts.rb +3 -1
- data/lib/liquid/lexer.rb +165 -39
- data/lib/liquid/locales/en.yml +16 -6
- data/lib/liquid/parse_context.rb +62 -7
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser.rb +31 -19
- data/lib/liquid/parser_switching.rb +42 -3
- data/lib/liquid/partial_cache.rb +33 -0
- data/lib/liquid/profiler/hooks.rb +26 -14
- data/lib/liquid/profiler.rb +67 -86
- data/lib/liquid/range_lookup.rb +26 -6
- data/lib/liquid/registers.rb +51 -0
- data/lib/liquid/resource_limits.rb +47 -8
- data/lib/liquid/snippet_drop.rb +22 -0
- data/lib/liquid/standardfilters.rb +813 -137
- data/lib/liquid/strainer_template.rb +62 -0
- data/lib/liquid/tablerowloop_drop.rb +64 -5
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +13 -0
- data/lib/liquid/tag.rb +42 -6
- data/lib/liquid/tags/assign.rb +46 -18
- data/lib/liquid/tags/break.rb +15 -4
- data/lib/liquid/tags/capture.rb +26 -18
- data/lib/liquid/tags/case.rb +108 -32
- data/lib/liquid/tags/comment.rb +76 -4
- data/lib/liquid/tags/continue.rb +15 -13
- data/lib/liquid/tags/cycle.rb +117 -34
- data/lib/liquid/tags/decrement.rb +30 -23
- data/lib/liquid/tags/doc.rb +81 -0
- data/lib/liquid/tags/echo.rb +39 -0
- data/lib/liquid/tags/for.rb +109 -96
- data/lib/liquid/tags/if.rb +72 -41
- data/lib/liquid/tags/ifchanged.rb +10 -11
- data/lib/liquid/tags/include.rb +89 -63
- data/lib/liquid/tags/increment.rb +31 -20
- data/lib/liquid/tags/inline_comment.rb +28 -0
- data/lib/liquid/tags/raw.rb +25 -13
- data/lib/liquid/tags/render.rb +151 -0
- data/lib/liquid/tags/snippet.rb +45 -0
- data/lib/liquid/tags/table_row.rb +104 -21
- data/lib/liquid/tags/unless.rb +37 -20
- data/lib/liquid/tags.rb +51 -0
- data/lib/liquid/template.rb +90 -106
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +143 -13
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +114 -5
- data/lib/liquid/variable.rb +119 -45
- data/lib/liquid/variable_lookup.rb +35 -13
- data/lib/liquid/version.rb +3 -1
- data/lib/liquid.rb +31 -18
- metadata +56 -107
- data/lib/liquid/strainer.rb +0 -66
- data/test/fixtures/en_locale.yml +0 -9
- data/test/integration/assign_test.rb +0 -48
- data/test/integration/blank_test.rb +0 -106
- data/test/integration/capture_test.rb +0 -50
- data/test/integration/context_test.rb +0 -32
- data/test/integration/document_test.rb +0 -19
- data/test/integration/drop_test.rb +0 -273
- data/test/integration/error_handling_test.rb +0 -260
- data/test/integration/filter_test.rb +0 -178
- data/test/integration/hash_ordering_test.rb +0 -23
- data/test/integration/output_test.rb +0 -123
- data/test/integration/parsing_quirks_test.rb +0 -118
- data/test/integration/render_profiling_test.rb +0 -154
- data/test/integration/security_test.rb +0 -66
- data/test/integration/standard_filter_test.rb +0 -535
- data/test/integration/tags/break_tag_test.rb +0 -15
- data/test/integration/tags/continue_tag_test.rb +0 -15
- data/test/integration/tags/for_tag_test.rb +0 -410
- data/test/integration/tags/if_else_tag_test.rb +0 -188
- data/test/integration/tags/include_tag_test.rb +0 -238
- data/test/integration/tags/increment_tag_test.rb +0 -23
- data/test/integration/tags/raw_tag_test.rb +0 -31
- data/test/integration/tags/standard_tag_test.rb +0 -296
- data/test/integration/tags/statements_test.rb +0 -111
- data/test/integration/tags/table_row_test.rb +0 -64
- data/test/integration/tags/unless_else_tag_test.rb +0 -26
- data/test/integration/template_test.rb +0 -323
- data/test/integration/trim_mode_test.rb +0 -525
- data/test/integration/variable_test.rb +0 -92
- data/test/test_helper.rb +0 -117
- data/test/unit/block_unit_test.rb +0 -58
- data/test/unit/condition_unit_test.rb +0 -158
- data/test/unit/context_unit_test.rb +0 -483
- data/test/unit/file_system_unit_test.rb +0 -35
- data/test/unit/i18n_unit_test.rb +0 -37
- data/test/unit/lexer_unit_test.rb +0 -51
- data/test/unit/parser_unit_test.rb +0 -82
- data/test/unit/regexp_unit_test.rb +0 -44
- data/test/unit/strainer_unit_test.rb +0 -148
- data/test/unit/tag_unit_test.rb +0 -21
- data/test/unit/tags/case_tag_unit_test.rb +0 -10
- data/test/unit/tags/for_tag_unit_test.rb +0 -13
- data/test/unit/tags/if_tag_unit_test.rb +0 -8
- data/test/unit/template_unit_test.rb +0 -78
- data/test/unit/tokenizer_unit_test.rb +0 -55
- data/test/unit/variable_unit_test.rb +0 -162
data/lib/liquid/tags/for.rb
CHANGED
|
@@ -1,62 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Liquid
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# {% for item in collection %}
|
|
12
|
-
# <div {% if forloop.first %}class="first"{% endif %}>
|
|
13
|
-
# Item {{ forloop.index }}: {{ item.name }}
|
|
14
|
-
# </div>
|
|
15
|
-
# {% else %}
|
|
16
|
-
# There is nothing in the collection.
|
|
17
|
-
# {% endfor %}
|
|
18
|
-
#
|
|
19
|
-
# You can also define a limit and offset much like SQL. Remember
|
|
20
|
-
# that offset starts at 0 for the first item.
|
|
21
|
-
#
|
|
22
|
-
# {% for item in collection limit:5 offset:10 %}
|
|
23
|
-
# {{ item.name }}
|
|
24
|
-
# {% end %}
|
|
25
|
-
#
|
|
26
|
-
# To reverse the for loop simply use {% for item in collection reversed %}
|
|
27
|
-
#
|
|
28
|
-
# == Available variables:
|
|
29
|
-
#
|
|
30
|
-
# forloop.name:: 'item-collection'
|
|
31
|
-
# forloop.length:: Length of the loop
|
|
32
|
-
# forloop.index:: The current item's position in the collection;
|
|
33
|
-
# forloop.index starts at 1.
|
|
34
|
-
# This is helpful for non-programmers who start believe
|
|
35
|
-
# the first item in an array is 1, not 0.
|
|
36
|
-
# forloop.index0:: The current item's position in the collection
|
|
37
|
-
# where the first item is 0
|
|
38
|
-
# forloop.rindex:: Number of items remaining in the loop
|
|
39
|
-
# (length - index) where 1 is the last item.
|
|
40
|
-
# forloop.rindex0:: Number of items remaining in the loop
|
|
41
|
-
# where 0 is the last item.
|
|
42
|
-
# forloop.first:: Returns true if the item is the first item.
|
|
43
|
-
# forloop.last:: Returns true if the item is the last item.
|
|
44
|
-
# forloop.parentloop:: Provides access to the parent loop, if present.
|
|
4
|
+
# @liquid_public_docs
|
|
5
|
+
# @liquid_type tag
|
|
6
|
+
# @liquid_category iteration
|
|
7
|
+
# @liquid_name for
|
|
8
|
+
# @liquid_summary
|
|
9
|
+
# Renders an expression for every item in an array.
|
|
10
|
+
# @liquid_description
|
|
11
|
+
# You can do a maximum of 50 iterations with a `for` loop. If you need to iterate over more than 50 items, then use the
|
|
12
|
+
# [`paginate` tag](/docs/api/liquid/tags/paginate) to split the items over multiple pages.
|
|
45
13
|
#
|
|
14
|
+
# > Tip:
|
|
15
|
+
# > Every `for` loop has an associated [`forloop` object](/docs/api/liquid/objects/forloop) with information about the loop.
|
|
16
|
+
# @liquid_syntax
|
|
17
|
+
# {% for variable in array %}
|
|
18
|
+
# expression
|
|
19
|
+
# {% endfor %}
|
|
20
|
+
# @liquid_syntax_keyword variable The current item in the array.
|
|
21
|
+
# @liquid_syntax_keyword array The array to iterate over.
|
|
22
|
+
# @liquid_syntax_keyword expression The expression to render for each iteration.
|
|
23
|
+
# @liquid_optional_param limit: [number] The number of iterations to perform.
|
|
24
|
+
# @liquid_optional_param offset: [number] The 1-based index to start iterating at.
|
|
25
|
+
# @liquid_optional_param range [untyped] A custom numeric range to iterate over.
|
|
26
|
+
# @liquid_optional_param reversed [untyped] Iterate in reverse order.
|
|
46
27
|
class For < Block
|
|
47
28
|
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
|
48
29
|
|
|
30
|
+
attr_reader :collection_name, :variable_name, :limit, :from
|
|
31
|
+
|
|
49
32
|
def initialize(tag_name, markup, options)
|
|
50
33
|
super
|
|
51
34
|
@from = @limit = nil
|
|
52
35
|
parse_with_selected_parser(markup)
|
|
53
|
-
@for_block =
|
|
36
|
+
@for_block = new_body
|
|
54
37
|
@else_block = nil
|
|
55
38
|
end
|
|
56
39
|
|
|
57
40
|
def parse(tokens)
|
|
58
|
-
|
|
59
|
-
|
|
41
|
+
if parse_body(@for_block, tokens)
|
|
42
|
+
parse_body(@else_block, tokens)
|
|
43
|
+
end
|
|
44
|
+
if blank?
|
|
45
|
+
@else_block&.remove_blank_strings
|
|
46
|
+
@for_block.remove_blank_strings
|
|
47
|
+
end
|
|
48
|
+
@else_block&.freeze
|
|
49
|
+
@for_block.freeze
|
|
60
50
|
end
|
|
61
51
|
|
|
62
52
|
def nodelist
|
|
@@ -64,72 +54,90 @@ module Liquid
|
|
|
64
54
|
end
|
|
65
55
|
|
|
66
56
|
def unknown_tag(tag, markup, tokens)
|
|
67
|
-
return super unless tag == 'else'
|
|
68
|
-
@else_block =
|
|
57
|
+
return super unless tag == 'else'
|
|
58
|
+
@else_block = new_body
|
|
69
59
|
end
|
|
70
60
|
|
|
71
|
-
def
|
|
61
|
+
def render_to_output_buffer(context, output)
|
|
72
62
|
segment = collection_segment(context)
|
|
73
63
|
|
|
74
64
|
if segment.empty?
|
|
75
|
-
render_else(context)
|
|
65
|
+
render_else(context, output)
|
|
76
66
|
else
|
|
77
|
-
render_segment(context, segment)
|
|
67
|
+
render_segment(context, output, segment)
|
|
78
68
|
end
|
|
69
|
+
|
|
70
|
+
output
|
|
79
71
|
end
|
|
80
72
|
|
|
81
73
|
protected
|
|
82
74
|
|
|
83
75
|
def lax_parse(markup)
|
|
84
76
|
if markup =~ Syntax
|
|
85
|
-
@variable_name
|
|
86
|
-
collection_name
|
|
87
|
-
@reversed
|
|
88
|
-
@name
|
|
89
|
-
@collection_name =
|
|
77
|
+
@variable_name = Regexp.last_match(1)
|
|
78
|
+
collection_name = Regexp.last_match(2)
|
|
79
|
+
@reversed = !!Regexp.last_match(3)
|
|
80
|
+
@name = "#{@variable_name}-#{collection_name}"
|
|
81
|
+
@collection_name = parse_expression(collection_name)
|
|
90
82
|
markup.scan(TagAttributes) do |key, value|
|
|
91
83
|
set_attribute(key, value)
|
|
92
84
|
end
|
|
93
85
|
else
|
|
94
|
-
raise SyntaxError
|
|
86
|
+
raise SyntaxError, options[:locale].t("errors.syntax.for")
|
|
95
87
|
end
|
|
96
88
|
end
|
|
97
89
|
|
|
98
90
|
def strict_parse(markup)
|
|
99
|
-
p =
|
|
91
|
+
p = @parse_context.new_parser(markup)
|
|
100
92
|
@variable_name = p.consume(:id)
|
|
101
|
-
raise SyntaxError
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
@collection_name =
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')
|
|
94
|
+
|
|
95
|
+
collection_name = p.expression
|
|
96
|
+
@collection_name = parse_expression(collection_name, safe: true)
|
|
97
|
+
|
|
98
|
+
@name = "#{@variable_name}-#{collection_name}"
|
|
99
|
+
@reversed = p.id?('reversed')
|
|
100
|
+
|
|
101
|
+
while p.look(:comma) || p.look(:id)
|
|
102
|
+
p.consume?(:comma)
|
|
103
|
+
unless (attribute = p.id?('limit') || p.id?('offset'))
|
|
104
|
+
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
|
|
110
105
|
end
|
|
111
|
-
p.consume
|
|
112
|
-
set_attribute(attribute, p.expression)
|
|
106
|
+
p.consume(:colon)
|
|
107
|
+
set_attribute(attribute, p.expression, safe: true)
|
|
113
108
|
end
|
|
114
109
|
p.consume(:end_of_string)
|
|
115
110
|
end
|
|
116
111
|
|
|
117
112
|
private
|
|
118
113
|
|
|
114
|
+
def rigid_parse(markup)
|
|
115
|
+
strict_parse(markup)
|
|
116
|
+
end
|
|
117
|
+
|
|
119
118
|
def collection_segment(context)
|
|
120
|
-
offsets = context.registers[:for] ||=
|
|
119
|
+
offsets = context.registers[:for] ||= {}
|
|
121
120
|
|
|
122
121
|
from = if @from == :continue
|
|
123
122
|
offsets[@name].to_i
|
|
124
123
|
else
|
|
125
|
-
context.evaluate(@from)
|
|
124
|
+
from_value = context.evaluate(@from)
|
|
125
|
+
if from_value.nil?
|
|
126
|
+
0
|
|
127
|
+
else
|
|
128
|
+
Utils.to_integer(from_value)
|
|
129
|
+
end
|
|
126
130
|
end
|
|
127
131
|
|
|
128
132
|
collection = context.evaluate(@collection_name)
|
|
129
133
|
collection = collection.to_a if collection.is_a?(Range)
|
|
130
134
|
|
|
131
|
-
|
|
132
|
-
to =
|
|
135
|
+
limit_value = context.evaluate(@limit)
|
|
136
|
+
to = if limit_value.nil?
|
|
137
|
+
nil
|
|
138
|
+
else
|
|
139
|
+
Utils.to_integer(limit_value) + from
|
|
140
|
+
end
|
|
133
141
|
|
|
134
142
|
segment = Utils.slice_collection(collection, from, to)
|
|
135
143
|
segment.reverse! if @reversed
|
|
@@ -139,11 +147,9 @@ module Liquid
|
|
|
139
147
|
segment
|
|
140
148
|
end
|
|
141
149
|
|
|
142
|
-
def render_segment(context, segment)
|
|
150
|
+
def render_segment(context, output, segment)
|
|
143
151
|
for_stack = context.registers[:for_stack] ||= []
|
|
144
|
-
length
|
|
145
|
-
|
|
146
|
-
result = ''
|
|
152
|
+
length = segment.length
|
|
147
153
|
|
|
148
154
|
context.stack do
|
|
149
155
|
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
|
@@ -151,45 +157,52 @@ module Liquid
|
|
|
151
157
|
for_stack.push(loop_vars)
|
|
152
158
|
|
|
153
159
|
begin
|
|
154
|
-
context['forloop'
|
|
160
|
+
context['forloop'] = loop_vars
|
|
155
161
|
|
|
156
|
-
segment.
|
|
162
|
+
segment.each do |item|
|
|
157
163
|
context[@variable_name] = item
|
|
158
|
-
|
|
164
|
+
@for_block.render_to_output_buffer(context, output)
|
|
159
165
|
loop_vars.send(:increment!)
|
|
160
166
|
|
|
161
167
|
# Handle any interrupts if they exist.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
end
|
|
168
|
+
next unless context.interrupt?
|
|
169
|
+
interrupt = context.pop_interrupt
|
|
170
|
+
break if interrupt.is_a?(BreakInterrupt)
|
|
171
|
+
next if interrupt.is_a?(ContinueInterrupt)
|
|
167
172
|
end
|
|
168
173
|
ensure
|
|
169
174
|
for_stack.pop
|
|
170
175
|
end
|
|
171
176
|
end
|
|
172
177
|
|
|
173
|
-
|
|
178
|
+
output
|
|
174
179
|
end
|
|
175
180
|
|
|
176
|
-
def set_attribute(key, expr)
|
|
181
|
+
def set_attribute(key, expr, safe: false)
|
|
177
182
|
case key
|
|
178
|
-
when 'offset'
|
|
179
|
-
@from = if expr == 'continue'
|
|
183
|
+
when 'offset'
|
|
184
|
+
@from = if expr == 'continue'
|
|
180
185
|
:continue
|
|
181
186
|
else
|
|
182
|
-
|
|
187
|
+
parse_expression(expr, safe: safe)
|
|
183
188
|
end
|
|
184
|
-
when 'limit'
|
|
185
|
-
@limit =
|
|
189
|
+
when 'limit'
|
|
190
|
+
@limit = parse_expression(expr, safe: safe)
|
|
186
191
|
end
|
|
187
192
|
end
|
|
188
193
|
|
|
189
|
-
def render_else(context)
|
|
190
|
-
|
|
194
|
+
def render_else(context, output)
|
|
195
|
+
if @else_block
|
|
196
|
+
@else_block.render_to_output_buffer(context, output)
|
|
197
|
+
else
|
|
198
|
+
output
|
|
199
|
+
end
|
|
191
200
|
end
|
|
192
|
-
end
|
|
193
201
|
|
|
194
|
-
|
|
202
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
203
|
+
def children
|
|
204
|
+
(super + [@node.limit, @node.from, @node.collection_name]).compact
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
195
208
|
end
|
data/lib/liquid/tags/if.rb
CHANGED
|
@@ -1,79 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Liquid
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
4
|
+
# @liquid_public_docs
|
|
5
|
+
# @liquid_type tag
|
|
6
|
+
# @liquid_category conditional
|
|
7
|
+
# @liquid_name if
|
|
8
|
+
# @liquid_summary
|
|
9
|
+
# Renders an expression if a specific condition is `true`.
|
|
10
|
+
# @liquid_syntax
|
|
11
|
+
# {% if condition %}
|
|
12
|
+
# expression
|
|
8
13
|
# {% endif %}
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
14
|
+
# @liquid_syntax_keyword condition The condition to evaluate.
|
|
15
|
+
# @liquid_syntax_keyword expression The expression to render if the condition is met.
|
|
12
16
|
class If < Block
|
|
13
|
-
Syntax
|
|
17
|
+
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
|
14
18
|
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
|
|
15
|
-
BOOLEAN_OPERATORS
|
|
19
|
+
BOOLEAN_OPERATORS = %w(and or).freeze
|
|
20
|
+
|
|
21
|
+
attr_reader :blocks
|
|
16
22
|
|
|
17
23
|
def initialize(tag_name, markup, options)
|
|
18
24
|
super
|
|
19
25
|
@blocks = []
|
|
20
|
-
push_block('if'
|
|
26
|
+
push_block('if', markup)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def nodelist
|
|
30
|
+
@blocks.map(&:attachment)
|
|
21
31
|
end
|
|
22
32
|
|
|
23
33
|
def parse(tokens)
|
|
24
34
|
while parse_body(@blocks.last.attachment, tokens)
|
|
25
35
|
end
|
|
36
|
+
@blocks.reverse_each do |block|
|
|
37
|
+
block.attachment.remove_blank_strings if blank?
|
|
38
|
+
block.attachment.freeze
|
|
39
|
+
end
|
|
26
40
|
end
|
|
27
41
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
42
|
+
ELSE_TAG_NAMES = ['elsif', 'else'].freeze
|
|
43
|
+
private_constant :ELSE_TAG_NAMES
|
|
31
44
|
|
|
32
45
|
def unknown_tag(tag, markup, tokens)
|
|
33
|
-
if
|
|
46
|
+
if ELSE_TAG_NAMES.include?(tag)
|
|
34
47
|
push_block(tag, markup)
|
|
35
48
|
else
|
|
36
49
|
super
|
|
37
50
|
end
|
|
38
51
|
end
|
|
39
52
|
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
def render_to_output_buffer(context, output)
|
|
54
|
+
@blocks.each do |block|
|
|
55
|
+
result = Liquid::Utils.to_liquid_value(
|
|
56
|
+
block.evaluate(context),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if result
|
|
60
|
+
return block.attachment.render_to_output_buffer(context, output)
|
|
46
61
|
end
|
|
47
|
-
''.freeze
|
|
48
62
|
end
|
|
63
|
+
|
|
64
|
+
output
|
|
49
65
|
end
|
|
50
66
|
|
|
51
67
|
private
|
|
52
68
|
|
|
69
|
+
def rigid_parse(markup)
|
|
70
|
+
strict_parse(markup)
|
|
71
|
+
end
|
|
72
|
+
|
|
53
73
|
def push_block(tag, markup)
|
|
54
|
-
block = if tag == 'else'
|
|
74
|
+
block = if tag == 'else'
|
|
55
75
|
ElseCondition.new
|
|
56
76
|
else
|
|
57
77
|
parse_with_selected_parser(markup)
|
|
58
78
|
end
|
|
59
79
|
|
|
60
80
|
@blocks.push(block)
|
|
61
|
-
block.attach(
|
|
81
|
+
block.attach(new_body)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def parse_expression(markup, safe: false)
|
|
85
|
+
Condition.parse_expression(parse_context, markup, safe: safe)
|
|
62
86
|
end
|
|
63
87
|
|
|
64
88
|
def lax_parse(markup)
|
|
65
89
|
expressions = markup.scan(ExpressionsAndOperators)
|
|
66
|
-
raise
|
|
90
|
+
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop =~ Syntax
|
|
67
91
|
|
|
68
|
-
condition = Condition.new(
|
|
92
|
+
condition = Condition.new(parse_expression(Regexp.last_match(1)), Regexp.last_match(2), parse_expression(Regexp.last_match(3)))
|
|
69
93
|
|
|
70
94
|
until expressions.empty?
|
|
71
95
|
operator = expressions.pop.to_s.strip
|
|
72
96
|
|
|
73
|
-
raise
|
|
97
|
+
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop.to_s =~ Syntax
|
|
74
98
|
|
|
75
|
-
new_condition = Condition.new(
|
|
76
|
-
raise
|
|
99
|
+
new_condition = Condition.new(parse_expression(Regexp.last_match(1)), Regexp.last_match(2), parse_expression(Regexp.last_match(3)))
|
|
100
|
+
raise SyntaxError, options[:locale].t("errors.syntax.if") unless BOOLEAN_OPERATORS.include?(operator)
|
|
77
101
|
new_condition.send(operator, condition)
|
|
78
102
|
condition = new_condition
|
|
79
103
|
end
|
|
@@ -82,30 +106,37 @@ module Liquid
|
|
|
82
106
|
end
|
|
83
107
|
|
|
84
108
|
def strict_parse(markup)
|
|
85
|
-
p =
|
|
86
|
-
condition =
|
|
109
|
+
p = @parse_context.new_parser(markup)
|
|
110
|
+
condition = parse_binary_comparisons(p)
|
|
87
111
|
p.consume(:end_of_string)
|
|
88
112
|
condition
|
|
89
113
|
end
|
|
90
114
|
|
|
91
|
-
def
|
|
115
|
+
def parse_binary_comparisons(p)
|
|
92
116
|
condition = parse_comparison(p)
|
|
93
|
-
|
|
94
|
-
|
|
117
|
+
first_condition = condition
|
|
118
|
+
while (op = p.id?('and') || p.id?('or'))
|
|
119
|
+
child_condition = parse_comparison(p)
|
|
120
|
+
condition.send(op, child_condition)
|
|
121
|
+
condition = child_condition
|
|
95
122
|
end
|
|
96
|
-
|
|
123
|
+
first_condition
|
|
97
124
|
end
|
|
98
125
|
|
|
99
126
|
def parse_comparison(p)
|
|
100
|
-
a =
|
|
101
|
-
if op = p.consume?(:comparison)
|
|
102
|
-
b =
|
|
127
|
+
a = parse_expression(p.expression, safe: true)
|
|
128
|
+
if (op = p.consume?(:comparison))
|
|
129
|
+
b = parse_expression(p.expression, safe: true)
|
|
103
130
|
Condition.new(a, op, b)
|
|
104
131
|
else
|
|
105
132
|
Condition.new(a)
|
|
106
133
|
end
|
|
107
134
|
end
|
|
108
|
-
end
|
|
109
135
|
|
|
110
|
-
|
|
136
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
137
|
+
def children
|
|
138
|
+
@node.blocks
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
111
142
|
end
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Liquid
|
|
2
4
|
class Ifchanged < Block
|
|
3
|
-
def
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
def render_to_output_buffer(context, output)
|
|
6
|
+
block_output = +''
|
|
7
|
+
super(context, block_output)
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
else
|
|
11
|
-
''.freeze
|
|
12
|
-
end
|
|
9
|
+
if block_output != context.registers[:ifchanged]
|
|
10
|
+
context.registers[:ifchanged] = block_output
|
|
11
|
+
output << block_output
|
|
13
12
|
end
|
|
13
|
+
|
|
14
|
+
output
|
|
14
15
|
end
|
|
15
16
|
end
|
|
16
|
-
|
|
17
|
-
Template.register_tag('ifchanged'.freeze, Ifchanged)
|
|
18
17
|
end
|