liquid 4.0.0 → 5.0.1
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 +101 -2
- data/README.md +8 -0
- data/lib/liquid.rb +18 -5
- data/lib/liquid/block.rb +47 -20
- data/lib/liquid/block_body.rb +192 -76
- data/lib/liquid/condition.rb +69 -29
- data/lib/liquid/context.rb +110 -53
- data/lib/liquid/document.rb +47 -9
- data/lib/liquid/drop.rb +4 -2
- data/lib/liquid/errors.rb +20 -18
- data/lib/liquid/expression.rb +30 -31
- data/lib/liquid/extensions.rb +8 -0
- data/lib/liquid/file_system.rb +6 -4
- data/lib/liquid/forloop_drop.rb +11 -4
- data/lib/liquid/i18n.rb +5 -3
- data/lib/liquid/interrupts.rb +3 -1
- data/lib/liquid/lexer.rb +35 -26
- data/lib/liquid/locales/en.yml +4 -2
- data/lib/liquid/parse_context.rb +21 -4
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser.rb +30 -18
- data/lib/liquid/parser_switching.rb +17 -3
- data/lib/liquid/partial_cache.rb +24 -0
- data/lib/liquid/profiler.rb +67 -86
- data/lib/liquid/profiler/hooks.rb +26 -14
- data/lib/liquid/range_lookup.rb +5 -3
- data/lib/liquid/register.rb +6 -0
- data/lib/liquid/resource_limits.rb +47 -8
- data/lib/liquid/standardfilters.rb +170 -63
- data/lib/liquid/static_registers.rb +44 -0
- data/lib/liquid/strainer_factory.rb +36 -0
- data/lib/liquid/strainer_template.rb +53 -0
- data/lib/liquid/tablerowloop_drop.rb +6 -4
- data/lib/liquid/tag.rb +28 -6
- data/lib/liquid/tag/disableable.rb +22 -0
- data/lib/liquid/tag/disabler.rb +21 -0
- data/lib/liquid/tags/assign.rb +32 -10
- data/lib/liquid/tags/break.rb +8 -3
- data/lib/liquid/tags/capture.rb +11 -8
- data/lib/liquid/tags/case.rb +41 -27
- data/lib/liquid/tags/comment.rb +5 -3
- data/lib/liquid/tags/continue.rb +8 -3
- data/lib/liquid/tags/cycle.rb +35 -16
- data/lib/liquid/tags/decrement.rb +6 -3
- data/lib/liquid/tags/echo.rb +34 -0
- data/lib/liquid/tags/for.rb +79 -47
- data/lib/liquid/tags/if.rb +53 -30
- data/lib/liquid/tags/ifchanged.rb +11 -10
- data/lib/liquid/tags/include.rb +42 -44
- data/lib/liquid/tags/increment.rb +7 -3
- data/lib/liquid/tags/raw.rb +14 -11
- data/lib/liquid/tags/render.rb +84 -0
- data/lib/liquid/tags/table_row.rb +32 -20
- data/lib/liquid/tags/unless.rb +15 -15
- data/lib/liquid/template.rb +53 -72
- data/lib/liquid/template_factory.rb +9 -0
- data/lib/liquid/tokenizer.rb +17 -9
- data/lib/liquid/usage.rb +8 -0
- data/lib/liquid/utils.rb +6 -4
- data/lib/liquid/variable.rb +55 -38
- data/lib/liquid/variable_lookup.rb +14 -6
- data/lib/liquid/version.rb +3 -1
- data/test/integration/assign_test.rb +74 -5
- data/test/integration/blank_test.rb +11 -8
- data/test/integration/block_test.rb +58 -0
- data/test/integration/capture_test.rb +18 -10
- data/test/integration/context_test.rb +609 -5
- data/test/integration/document_test.rb +4 -2
- data/test/integration/drop_test.rb +67 -83
- data/test/integration/error_handling_test.rb +73 -61
- data/test/integration/expression_test.rb +46 -0
- data/test/integration/filter_test.rb +53 -42
- data/test/integration/hash_ordering_test.rb +5 -3
- data/test/integration/output_test.rb +26 -24
- data/test/integration/parsing_quirks_test.rb +24 -8
- data/test/integration/{render_profiling_test.rb → profiler_test.rb} +84 -25
- data/test/integration/security_test.rb +41 -18
- data/test/integration/standard_filter_test.rb +513 -210
- data/test/integration/tag/disableable_test.rb +59 -0
- data/test/integration/tag_test.rb +45 -0
- data/test/integration/tags/break_tag_test.rb +4 -2
- data/test/integration/tags/continue_tag_test.rb +4 -2
- data/test/integration/tags/echo_test.rb +13 -0
- data/test/integration/tags/for_tag_test.rb +109 -53
- data/test/integration/tags/if_else_tag_test.rb +5 -3
- data/test/integration/tags/include_tag_test.rb +83 -52
- data/test/integration/tags/increment_tag_test.rb +4 -2
- data/test/integration/tags/liquid_tag_test.rb +116 -0
- data/test/integration/tags/raw_tag_test.rb +14 -11
- data/test/integration/tags/render_tag_test.rb +213 -0
- data/test/integration/tags/standard_tag_test.rb +38 -31
- data/test/integration/tags/statements_test.rb +23 -21
- data/test/integration/tags/table_row_test.rb +2 -0
- data/test/integration/tags/unless_else_tag_test.rb +4 -2
- data/test/integration/template_test.rb +123 -120
- data/test/integration/trim_mode_test.rb +82 -44
- data/test/integration/variable_test.rb +46 -31
- data/test/test_helper.rb +75 -23
- data/test/unit/block_unit_test.rb +19 -24
- data/test/unit/condition_unit_test.rb +82 -72
- data/test/unit/file_system_unit_test.rb +6 -4
- data/test/unit/i18n_unit_test.rb +7 -5
- data/test/unit/lexer_unit_test.rb +12 -10
- data/test/unit/parse_tree_visitor_test.rb +254 -0
- data/test/unit/parser_unit_test.rb +37 -35
- data/test/unit/partial_cache_unit_test.rb +128 -0
- data/test/unit/regexp_unit_test.rb +17 -15
- data/test/unit/static_registers_unit_test.rb +156 -0
- data/test/unit/strainer_factory_unit_test.rb +100 -0
- data/test/unit/strainer_template_unit_test.rb +82 -0
- data/test/unit/tag_unit_test.rb +5 -3
- data/test/unit/tags/case_tag_unit_test.rb +3 -1
- data/test/unit/tags/for_tag_unit_test.rb +4 -2
- data/test/unit/tags/if_tag_unit_test.rb +3 -1
- data/test/unit/template_factory_unit_test.rb +12 -0
- data/test/unit/template_unit_test.rb +19 -10
- data/test/unit/tokenizer_unit_test.rb +26 -19
- data/test/unit/variable_unit_test.rb +51 -49
- metadata +79 -46
- data/lib/liquid/strainer.rb +0 -66
- data/test/unit/context_unit_test.rb +0 -483
- data/test/unit/strainer_unit_test.rb +0 -148
data/lib/liquid/tags/comment.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
class Comment < Block
|
3
|
-
def
|
4
|
-
|
5
|
+
def render_to_output_buffer(_context, output)
|
6
|
+
output
|
5
7
|
end
|
6
8
|
|
7
9
|
def unknown_tag(_tag, _markup, _tokens)
|
@@ -12,5 +14,5 @@ module Liquid
|
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
15
|
-
Template.register_tag('comment'
|
17
|
+
Template.register_tag('comment', Comment)
|
16
18
|
end
|
data/lib/liquid/tags/continue.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
# Continue tag to be used to break out of a for loop.
|
3
5
|
#
|
@@ -9,10 +11,13 @@ module Liquid
|
|
9
11
|
# {% endfor %}
|
10
12
|
#
|
11
13
|
class Continue < Tag
|
12
|
-
|
13
|
-
|
14
|
+
INTERRUPT = ContinueInterrupt.new.freeze
|
15
|
+
|
16
|
+
def render_to_output_buffer(context, output)
|
17
|
+
context.push_interrupt(INTERRUPT)
|
18
|
+
output
|
14
19
|
end
|
15
20
|
end
|
16
21
|
|
17
|
-
Template.register_tag('continue'
|
22
|
+
Template.register_tag('continue', Continue)
|
18
23
|
end
|
data/lib/liquid/tags/cycle.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
# Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
|
3
5
|
#
|
@@ -15,32 +17,43 @@ module Liquid
|
|
15
17
|
SimpleSyntax = /\A#{QuotedFragment}+/o
|
16
18
|
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
|
17
19
|
|
20
|
+
attr_reader :variables
|
21
|
+
|
18
22
|
def initialize(tag_name, markup, options)
|
19
23
|
super
|
20
24
|
case markup
|
21
25
|
when NamedSyntax
|
22
|
-
@variables = variables_from_string(
|
23
|
-
@name
|
26
|
+
@variables = variables_from_string(Regexp.last_match(2))
|
27
|
+
@name = parse_expression(Regexp.last_match(1))
|
24
28
|
when SimpleSyntax
|
25
29
|
@variables = variables_from_string(markup)
|
26
|
-
@name
|
30
|
+
@name = @variables.to_s
|
27
31
|
else
|
28
|
-
raise SyntaxError
|
32
|
+
raise SyntaxError, options[:locale].t("errors.syntax.cycle")
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
32
|
-
def
|
33
|
-
context.registers[:cycle] ||=
|
34
|
-
|
35
|
-
context.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
36
|
+
def render_to_output_buffer(context, output)
|
37
|
+
context.registers[:cycle] ||= {}
|
38
|
+
|
39
|
+
key = context.evaluate(@name)
|
40
|
+
iteration = context.registers[:cycle][key].to_i
|
41
|
+
|
42
|
+
val = context.evaluate(@variables[iteration])
|
43
|
+
|
44
|
+
if val.is_a?(Array)
|
45
|
+
val = val.join
|
46
|
+
elsif !val.is_a?(String)
|
47
|
+
val = val.to_s
|
43
48
|
end
|
49
|
+
|
50
|
+
output << val
|
51
|
+
|
52
|
+
iteration += 1
|
53
|
+
iteration = 0 if iteration >= @variables.size
|
54
|
+
|
55
|
+
context.registers[:cycle][key] = iteration
|
56
|
+
output
|
44
57
|
end
|
45
58
|
|
46
59
|
private
|
@@ -48,9 +61,15 @@ module Liquid
|
|
48
61
|
def variables_from_string(markup)
|
49
62
|
markup.split(',').collect do |var|
|
50
63
|
var =~ /\s*(#{QuotedFragment})\s*/o
|
51
|
-
|
64
|
+
Regexp.last_match(1) ? parse_expression(Regexp.last_match(1)) : nil
|
52
65
|
end.compact
|
53
66
|
end
|
67
|
+
|
68
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
69
|
+
def children
|
70
|
+
Array(@node.variables)
|
71
|
+
end
|
72
|
+
end
|
54
73
|
end
|
55
74
|
|
56
75
|
Template.register_tag('cycle', Cycle)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
# decrement is used in a place where one needs to insert a counter
|
3
5
|
# into a template, and needs the counter to survive across
|
@@ -23,13 +25,14 @@ module Liquid
|
|
23
25
|
@variable = markup.strip
|
24
26
|
end
|
25
27
|
|
26
|
-
def
|
28
|
+
def render_to_output_buffer(context, output)
|
27
29
|
value = context.environments.first[@variable] ||= 0
|
28
30
|
value -= 1
|
29
31
|
context.environments.first[@variable] = value
|
30
|
-
value.to_s
|
32
|
+
output << value.to_s
|
33
|
+
output
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
34
|
-
Template.register_tag('decrement'
|
37
|
+
Template.register_tag('decrement', Decrement)
|
35
38
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Liquid
|
4
|
+
# Echo outputs an expression
|
5
|
+
#
|
6
|
+
# {% echo monkey %}
|
7
|
+
# {% echo user.name %}
|
8
|
+
#
|
9
|
+
# This is identical to variable output syntax, like {{ foo }}, but works
|
10
|
+
# inside {% liquid %} tags. The full syntax is supported, including filters:
|
11
|
+
#
|
12
|
+
# {% echo user | link %}
|
13
|
+
#
|
14
|
+
class Echo < Tag
|
15
|
+
attr_reader :variable
|
16
|
+
|
17
|
+
def initialize(tag_name, markup, parse_context)
|
18
|
+
super
|
19
|
+
@variable = Variable.new(markup, parse_context)
|
20
|
+
end
|
21
|
+
|
22
|
+
def render(context)
|
23
|
+
@variable.render_to_output_buffer(context, +'')
|
24
|
+
end
|
25
|
+
|
26
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
27
|
+
def children
|
28
|
+
[@node.variable]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Template.register_tag('echo', Echo)
|
34
|
+
end
|
data/lib/liquid/tags/for.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
# "For" iterates over an array or collection.
|
3
5
|
# Several useful variables are available to you within the loop.
|
@@ -23,7 +25,7 @@ module Liquid
|
|
23
25
|
# {{ item.name }}
|
24
26
|
# {% end %}
|
25
27
|
#
|
26
|
-
# To reverse the for loop simply use {% for item in collection reversed %}
|
28
|
+
# To reverse the for loop simply use {% for item in collection reversed %} (note that the flag's spelling is different to the filter `reverse`)
|
27
29
|
#
|
28
30
|
# == Available variables:
|
29
31
|
#
|
@@ -46,17 +48,26 @@ module Liquid
|
|
46
48
|
class For < Block
|
47
49
|
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
48
50
|
|
51
|
+
attr_reader :collection_name, :variable_name, :limit, :from
|
52
|
+
|
49
53
|
def initialize(tag_name, markup, options)
|
50
54
|
super
|
51
55
|
@from = @limit = nil
|
52
56
|
parse_with_selected_parser(markup)
|
53
|
-
@for_block =
|
57
|
+
@for_block = new_body
|
54
58
|
@else_block = nil
|
55
59
|
end
|
56
60
|
|
57
61
|
def parse(tokens)
|
58
|
-
|
59
|
-
|
62
|
+
if parse_body(@for_block, tokens)
|
63
|
+
parse_body(@else_block, tokens)
|
64
|
+
end
|
65
|
+
if blank?
|
66
|
+
@else_block&.remove_blank_strings
|
67
|
+
@for_block.remove_blank_strings
|
68
|
+
end
|
69
|
+
@else_block&.freeze
|
70
|
+
@for_block.freeze
|
60
71
|
end
|
61
72
|
|
62
73
|
def nodelist
|
@@ -64,49 +75,53 @@ module Liquid
|
|
64
75
|
end
|
65
76
|
|
66
77
|
def unknown_tag(tag, markup, tokens)
|
67
|
-
return super unless tag == 'else'
|
68
|
-
@else_block =
|
78
|
+
return super unless tag == 'else'
|
79
|
+
@else_block = new_body
|
69
80
|
end
|
70
81
|
|
71
|
-
def
|
82
|
+
def render_to_output_buffer(context, output)
|
72
83
|
segment = collection_segment(context)
|
73
84
|
|
74
85
|
if segment.empty?
|
75
|
-
render_else(context)
|
86
|
+
render_else(context, output)
|
76
87
|
else
|
77
|
-
render_segment(context, segment)
|
88
|
+
render_segment(context, output, segment)
|
78
89
|
end
|
90
|
+
|
91
|
+
output
|
79
92
|
end
|
80
93
|
|
81
94
|
protected
|
82
95
|
|
83
96
|
def lax_parse(markup)
|
84
97
|
if markup =~ Syntax
|
85
|
-
@variable_name
|
86
|
-
collection_name
|
87
|
-
@reversed
|
88
|
-
@name
|
89
|
-
@collection_name =
|
98
|
+
@variable_name = Regexp.last_match(1)
|
99
|
+
collection_name = Regexp.last_match(2)
|
100
|
+
@reversed = !!Regexp.last_match(3)
|
101
|
+
@name = "#{@variable_name}-#{collection_name}"
|
102
|
+
@collection_name = parse_expression(collection_name)
|
90
103
|
markup.scan(TagAttributes) do |key, value|
|
91
104
|
set_attribute(key, value)
|
92
105
|
end
|
93
106
|
else
|
94
|
-
raise SyntaxError
|
107
|
+
raise SyntaxError, options[:locale].t("errors.syntax.for")
|
95
108
|
end
|
96
109
|
end
|
97
110
|
|
98
111
|
def strict_parse(markup)
|
99
112
|
p = Parser.new(markup)
|
100
113
|
@variable_name = p.consume(:id)
|
101
|
-
raise SyntaxError
|
102
|
-
|
103
|
-
|
104
|
-
@collection_name =
|
105
|
-
|
114
|
+
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')
|
115
|
+
|
116
|
+
collection_name = p.expression
|
117
|
+
@collection_name = parse_expression(collection_name)
|
118
|
+
|
119
|
+
@name = "#{@variable_name}-#{collection_name}"
|
120
|
+
@reversed = p.id?('reversed')
|
106
121
|
|
107
122
|
while p.look(:id) && p.look(:colon, 1)
|
108
|
-
unless attribute = p.id?('limit'
|
109
|
-
raise SyntaxError
|
123
|
+
unless (attribute = p.id?('limit') || p.id?('offset'))
|
124
|
+
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
|
110
125
|
end
|
111
126
|
p.consume
|
112
127
|
set_attribute(attribute, p.expression)
|
@@ -117,19 +132,28 @@ module Liquid
|
|
117
132
|
private
|
118
133
|
|
119
134
|
def collection_segment(context)
|
120
|
-
offsets = context.registers[:for] ||=
|
135
|
+
offsets = context.registers[:for] ||= {}
|
121
136
|
|
122
137
|
from = if @from == :continue
|
123
138
|
offsets[@name].to_i
|
124
139
|
else
|
125
|
-
context.evaluate(@from)
|
140
|
+
from_value = context.evaluate(@from)
|
141
|
+
if from_value.nil?
|
142
|
+
0
|
143
|
+
else
|
144
|
+
Utils.to_integer(from_value)
|
145
|
+
end
|
126
146
|
end
|
127
147
|
|
128
148
|
collection = context.evaluate(@collection_name)
|
129
149
|
collection = collection.to_a if collection.is_a?(Range)
|
130
150
|
|
131
|
-
|
132
|
-
to =
|
151
|
+
limit_value = context.evaluate(@limit)
|
152
|
+
to = if limit_value.nil?
|
153
|
+
nil
|
154
|
+
else
|
155
|
+
Utils.to_integer(limit_value) + from
|
156
|
+
end
|
133
157
|
|
134
158
|
segment = Utils.slice_collection(collection, from, to)
|
135
159
|
segment.reverse! if @reversed
|
@@ -139,11 +163,9 @@ module Liquid
|
|
139
163
|
segment
|
140
164
|
end
|
141
165
|
|
142
|
-
def render_segment(context, segment)
|
166
|
+
def render_segment(context, output, segment)
|
143
167
|
for_stack = context.registers[:for_stack] ||= []
|
144
|
-
length
|
145
|
-
|
146
|
-
result = ''
|
168
|
+
length = segment.length
|
147
169
|
|
148
170
|
context.stack do
|
149
171
|
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
@@ -151,45 +173,55 @@ module Liquid
|
|
151
173
|
for_stack.push(loop_vars)
|
152
174
|
|
153
175
|
begin
|
154
|
-
context['forloop'
|
176
|
+
context['forloop'] = loop_vars
|
155
177
|
|
156
|
-
segment.
|
178
|
+
segment.each do |item|
|
157
179
|
context[@variable_name] = item
|
158
|
-
|
180
|
+
@for_block.render_to_output_buffer(context, output)
|
159
181
|
loop_vars.send(:increment!)
|
160
182
|
|
161
183
|
# Handle any interrupts if they exist.
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
end
|
184
|
+
next unless context.interrupt?
|
185
|
+
interrupt = context.pop_interrupt
|
186
|
+
break if interrupt.is_a?(BreakInterrupt)
|
187
|
+
next if interrupt.is_a?(ContinueInterrupt)
|
167
188
|
end
|
168
189
|
ensure
|
169
190
|
for_stack.pop
|
170
191
|
end
|
171
192
|
end
|
172
193
|
|
173
|
-
|
194
|
+
output
|
174
195
|
end
|
175
196
|
|
176
197
|
def set_attribute(key, expr)
|
177
198
|
case key
|
178
|
-
when 'offset'
|
179
|
-
@from = if expr == 'continue'
|
199
|
+
when 'offset'
|
200
|
+
@from = if expr == 'continue'
|
201
|
+
Usage.increment('for_offset_continue')
|
180
202
|
:continue
|
181
203
|
else
|
182
|
-
|
204
|
+
parse_expression(expr)
|
183
205
|
end
|
184
|
-
when 'limit'
|
185
|
-
@limit =
|
206
|
+
when 'limit'
|
207
|
+
@limit = parse_expression(expr)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def render_else(context, output)
|
212
|
+
if @else_block
|
213
|
+
@else_block.render_to_output_buffer(context, output)
|
214
|
+
else
|
215
|
+
output
|
186
216
|
end
|
187
217
|
end
|
188
218
|
|
189
|
-
|
190
|
-
|
219
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
220
|
+
def children
|
221
|
+
(super + [@node.limit, @node.from, @node.collection_name]).compact
|
222
|
+
end
|
191
223
|
end
|
192
224
|
end
|
193
225
|
|
194
|
-
Template.register_tag('for'
|
226
|
+
Template.register_tag('for', For)
|
195
227
|
end
|
data/lib/liquid/tags/if.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Liquid
|
2
4
|
# If is the conditional block
|
3
5
|
#
|
@@ -10,70 +12,82 @@ module Liquid
|
|
10
12
|
# There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
|
11
13
|
#
|
12
14
|
class If < Block
|
13
|
-
Syntax
|
15
|
+
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
14
16
|
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
|
17
|
+
BOOLEAN_OPERATORS = %w(and or).freeze
|
18
|
+
|
19
|
+
attr_reader :blocks
|
16
20
|
|
17
21
|
def initialize(tag_name, markup, options)
|
18
22
|
super
|
19
23
|
@blocks = []
|
20
|
-
push_block('if'
|
24
|
+
push_block('if', markup)
|
25
|
+
end
|
26
|
+
|
27
|
+
def nodelist
|
28
|
+
@blocks.map(&:attachment)
|
21
29
|
end
|
22
30
|
|
23
31
|
def parse(tokens)
|
24
32
|
while parse_body(@blocks.last.attachment, tokens)
|
25
33
|
end
|
34
|
+
@blocks.reverse_each do |block|
|
35
|
+
block.attachment.remove_blank_strings if blank?
|
36
|
+
block.attachment.freeze
|
37
|
+
end
|
26
38
|
end
|
27
39
|
|
28
|
-
|
29
|
-
|
30
|
-
end
|
40
|
+
ELSE_TAG_NAMES = ['elsif', 'else'].freeze
|
41
|
+
private_constant :ELSE_TAG_NAMES
|
31
42
|
|
32
43
|
def unknown_tag(tag, markup, tokens)
|
33
|
-
if
|
44
|
+
if ELSE_TAG_NAMES.include?(tag)
|
34
45
|
push_block(tag, markup)
|
35
46
|
else
|
36
47
|
super
|
37
48
|
end
|
38
49
|
end
|
39
50
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
return block.attachment.render(context)
|
45
|
-
end
|
51
|
+
def render_to_output_buffer(context, output)
|
52
|
+
@blocks.each do |block|
|
53
|
+
if block.evaluate(context)
|
54
|
+
return block.attachment.render_to_output_buffer(context, output)
|
46
55
|
end
|
47
|
-
''.freeze
|
48
56
|
end
|
57
|
+
|
58
|
+
output
|
49
59
|
end
|
50
60
|
|
51
61
|
private
|
52
62
|
|
53
63
|
def push_block(tag, markup)
|
54
|
-
block = if tag == 'else'
|
64
|
+
block = if tag == 'else'
|
55
65
|
ElseCondition.new
|
56
66
|
else
|
57
67
|
parse_with_selected_parser(markup)
|
58
68
|
end
|
59
69
|
|
60
70
|
@blocks.push(block)
|
61
|
-
block.attach(
|
71
|
+
block.attach(new_body)
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_expression(markup)
|
75
|
+
Condition.parse_expression(parse_context, markup)
|
62
76
|
end
|
63
77
|
|
64
78
|
def lax_parse(markup)
|
65
79
|
expressions = markup.scan(ExpressionsAndOperators)
|
66
|
-
raise
|
80
|
+
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop =~ Syntax
|
67
81
|
|
68
|
-
condition = Condition.new(
|
82
|
+
condition = Condition.new(parse_expression(Regexp.last_match(1)), Regexp.last_match(2), parse_expression(Regexp.last_match(3)))
|
69
83
|
|
70
84
|
until expressions.empty?
|
71
85
|
operator = expressions.pop.to_s.strip
|
72
86
|
|
73
|
-
raise
|
87
|
+
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop.to_s =~ Syntax
|
74
88
|
|
75
|
-
new_condition = Condition.new(
|
76
|
-
raise
|
89
|
+
new_condition = Condition.new(parse_expression(Regexp.last_match(1)), Regexp.last_match(2), parse_expression(Regexp.last_match(3)))
|
90
|
+
raise SyntaxError, options[:locale].t("errors.syntax.if") unless BOOLEAN_OPERATORS.include?(operator)
|
77
91
|
new_condition.send(operator, condition)
|
78
92
|
condition = new_condition
|
79
93
|
end
|
@@ -83,29 +97,38 @@ module Liquid
|
|
83
97
|
|
84
98
|
def strict_parse(markup)
|
85
99
|
p = Parser.new(markup)
|
86
|
-
condition =
|
100
|
+
condition = parse_binary_comparisons(p)
|
87
101
|
p.consume(:end_of_string)
|
88
102
|
condition
|
89
103
|
end
|
90
104
|
|
91
|
-
def
|
105
|
+
def parse_binary_comparisons(p)
|
92
106
|
condition = parse_comparison(p)
|
93
|
-
|
94
|
-
|
107
|
+
first_condition = condition
|
108
|
+
while (op = (p.id?('and') || p.id?('or')))
|
109
|
+
child_condition = parse_comparison(p)
|
110
|
+
condition.send(op, child_condition)
|
111
|
+
condition = child_condition
|
95
112
|
end
|
96
|
-
|
113
|
+
first_condition
|
97
114
|
end
|
98
115
|
|
99
116
|
def parse_comparison(p)
|
100
|
-
a =
|
101
|
-
if op = p.consume?(:comparison)
|
102
|
-
b =
|
117
|
+
a = parse_expression(p.expression)
|
118
|
+
if (op = p.consume?(:comparison))
|
119
|
+
b = parse_expression(p.expression)
|
103
120
|
Condition.new(a, op, b)
|
104
121
|
else
|
105
122
|
Condition.new(a)
|
106
123
|
end
|
107
124
|
end
|
125
|
+
|
126
|
+
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
127
|
+
def children
|
128
|
+
@node.blocks
|
129
|
+
end
|
130
|
+
end
|
108
131
|
end
|
109
132
|
|
110
|
-
Template.register_tag('if'
|
133
|
+
Template.register_tag('if', If)
|
111
134
|
end
|