Fix an int the cycle tag, add extra unit tests, and updated parser switcher:

- Fixed NoMethod error with .peek (using look instead)
  - Add friendlier error message when {% cycle %}
This commit is contained in:
Guilherme Carreiro
2025-10-27 16:33:31 +01:00
committed by Guilherme Carreiro
parent 75c95d0791
commit 327790cdce
5 changed files with 47 additions and 13 deletions
+8 -5
View File
@@ -2,9 +2,12 @@
module Liquid module Liquid
module ParserSwitching module ParserSwitching
# Do not use this. Use parse_with_selected_parser instead. # Do not use this.
# It's basically doing the same thing, except this will use strict_parse regardless #
# of the error mode and fallback only if strict throws. # It's basically doing the same thing the {#parse_with_selected_parser},
# except this will use the strict parser, instead of the rigid parser.
#
# @deprecated Use {#parse_with_selected_parser} instead.
def strict_parse_with_error_mode_fallback(markup) def strict_parse_with_error_mode_fallback(markup)
strict_parse_with_error_context(markup) strict_parse_with_error_context(markup)
rescue SyntaxError => e rescue SyntaxError => e
@@ -26,7 +29,7 @@ module Liquid
when :lax then lax_parse(markup) when :lax then lax_parse(markup)
when :warn when :warn
begin begin
strict_parse_with_error_context(markup) rigid_parse_with_error_context(markup)
rescue SyntaxError => e rescue SyntaxError => e
parse_context.warnings << e parse_context.warnings << e
lax_parse(markup) lax_parse(markup)
@@ -37,7 +40,7 @@ module Liquid
private private
def rigid_parse_with_error_context(markup) def rigid_parse_with_error_context(markup)
respond_to?(:rigid_parse) ? rigid_parse(markup) : strict_parse(markup) rigid_parse(markup)
rescue SyntaxError => e rescue SyntaxError => e
e.line_number = line_number e.line_number = line_number
e.markup_context = markup_context(markup) e.markup_context = markup_context(markup)
+11 -4
View File
@@ -52,27 +52,34 @@ module Liquid
output output
end end
private
# cycle [name:] expression(, expression)* # cycle [name:] expression(, expression)*
def rigid_parse(markup) def rigid_parse(markup)
p = @parse_context.new_parser(markup) p = @parse_context.new_parser(markup)
if p.look(:id) && p.peek(1) == :colon if p.look(:id) && p.look(:colon, 1)
@name = p.consume(:id) @name = p.consume(:id)
@is_named = true @is_named = true
p.consume(:colon) p.consume(:colon)
end end
@variables = [] @variables = []
raise SyntaxError, options[:locale].t("errors.syntax.cycle") if p.look(:end_of_string)
while (var = p.expression) while (var = p.expression)
var = parse_expression(var)
@variables << var @variables << var
break unless p.consume?(:comma) break unless p.consume?(:comma)
end end
raise_syntax_error(options) if @variables.empty? unless @is_named
@name = @variables.to_s
@is_named = !@name.match?(/\w+:0x\h{8}/)
end
end end
private
# Temporarily until we migrate # Temporarily until we migrate
def strict_parse(markup) def strict_parse(markup)
lax_parse(markup) lax_parse(markup)
+4
View File
@@ -111,6 +111,10 @@ module Liquid
private private
def rigid_parse(markup)
strict_parse(markup)
end
def collection_segment(context) def collection_segment(context)
offsets = context.registers[:for] ||= {} offsets = context.registers[:for] ||= {}
+4
View File
@@ -66,6 +66,10 @@ module Liquid
private private
def rigid_parse(markup)
strict_parse(markup)
end
def push_block(tag, markup) def push_block(tag, markup)
block = if tag == 'else' block = if tag == 'else'
ElseCondition.new ElseCondition.new
+20 -4
View File
@@ -46,18 +46,34 @@ class CycleTagTest < Minitest::Test
assert_template_result("11", template) assert_template_result("11", template)
end end
def test_cycle_tag_without_arguments
error = assert_raises(Liquid::SyntaxError) do
Template.parse("{% cycle %}")
end
assert_match(/Syntax Error in 'cycle' - Valid syntax: cycle \[name :\] var/, error.message)
end
def test_cycle_tag_with_error_mode def test_cycle_tag_with_error_mode
# QuotedFragment is more permissive than what Parser#expression allows. # QuotedFragment is more permissive than what Parser#expression allows.
temlate1 = "{% assign 5 = 'b' %}{% cycle .5, .4 %}"
temlate2 = "{% cycle .5: 'a', 'b' %}"
[:lax, :strict].each do |mode| [:lax, :strict].each do |mode|
with_error_mode(mode) do with_error_mode(mode) do
assert_template_result("a", "{% cycle .5: 'a', 'b' %}") assert_template_result("b", temlate1)
assert_template_result("b", "{% assign 5 = 'b' %}{% cycle .5, .4 %}") assert_template_result("a", temlate2)
end end
end end
with_error_mode(:rigid) do with_error_mode(:rigid) do
assert_raises(Liquid::SyntaxError) { Template.parse("{% cycle .5: 'a', 'b' %}") } error1 = assert_raises(Liquid::SyntaxError) { Template.parse(temlate1) }
assert_raises(Liquid::SyntaxError) { Template.parse("{% cycle .5, .4 %}") } error2 = assert_raises(Liquid::SyntaxError) { Template.parse(temlate2) }
expected_error = /Liquid syntax error: \[:dot, "."\] is not a valid expression/
assert_match(expected_error, error1.message)
assert_match(expected_error, error2.message)
end end
end end
end end