From 0cc6cdd5531fdf6e5f82966b3c52c7ecc7a3be09 Mon Sep 17 00:00:00 2001 From: Julia Boutin Date: Wed, 22 Oct 2025 16:21:17 -0600 Subject: [PATCH] Remove ... syntax references --- lib/liquid/lexer.rb | 10 +- lib/liquid/tags/render.rb | 63 +- test/integration/tags/snippet_test.rb | 834 ++++---------------------- 3 files changed, 138 insertions(+), 769 deletions(-) diff --git a/lib/liquid/lexer.rb b/lib/liquid/lexer.rb index 94a3d9fa..f1740dba 100644 --- a/lib/liquid/lexer.rb +++ b/lib/liquid/lexer.rb @@ -17,7 +17,6 @@ module Liquid DASH = [:dash, "-"].freeze DOT = [:dot, "."].freeze DOTDOT = [:dotdot, ".."].freeze - DOTDOTDOT = [:dotdotdot, "..."].freeze DOT_ORD = ".".ord DOUBLE_STRING_LITERAL = /"[^\"]*"/ EOS = [:end_of_string].freeze @@ -114,15 +113,10 @@ module Liquid if (special = SPECIAL_TABLE[peeked]) ss.scan_byte - # Special case for ".." and "..." + # Special case for ".." if special == DOT && ss.peek_byte == DOT_ORD ss.scan_byte - if ss.peek_byte == DOT_ORD - ss.scan_byte - output << DOTDOTDOT - else - output << DOTDOT - end + output << DOTDOT elsif special == DASH # Special case for negative numbers if (peeked_byte = ss.peek_byte) && NUMBER_TABLE[peeked_byte] diff --git a/lib/liquid/tags/render.rb b/lib/liquid/tags/render.rb index a6fd0c48..55ab7d3f 100644 --- a/lib/liquid/tags/render.rb +++ b/lib/liquid/tags/render.rb @@ -76,30 +76,7 @@ module Liquid inner_context['forloop'] = forloop if forloop @attributes.each do |key, value| - if key.start_with?("...") && is_inline - if key == "..." - context.scopes.each do |scope| - scope.each do |k, v| - inner_context[k] = v - end - end - else - obj = context.evaluate(value) - if obj.is_a?(Liquid::Drop) - (obj.class.invokable_methods - ['to_liquid']).each do |method_name| - inner_context[method_name] = obj.invoke_drop(method_name) - end - elsif obj.is_a?(Hash) - obj.each do |k, v| - inner_context[k] = v - end - else - raise ::ArgumentError - end - end - else - inner_context[key] = context.evaluate(value) - end + inner_context[key] = context.evaluate(value) end inner_context[context_variable_name] = var unless var.nil? @@ -132,23 +109,11 @@ module Liquid p.consume?(:comma) @attributes = {} - while p.look(:dotdotdot) || p.look(:id) - if p.consume?(:dotdotdot) - if p.look(:id) - identifier = p.read(:id) - key = "...#{identifier}" - @attributes.delete(key) - @attributes[key] = safe_parse_expression(p) - else - @attributes.delete("...") - @attributes["..."] = true - end - else - key = p.consume - p.consume(:colon) - @attributes.delete(key) - @attributes[key] = safe_parse_expression(p) - end + while p.look(:id) + key = p.consume + p.consume(:colon) + @attributes[key] = safe_parse_expression(p) + p.consume?(:comma) # optional comma end @@ -180,20 +145,8 @@ module Liquid @is_for_loop = (with_or_for == FOR) @attributes = {} - markup.scan(/(\.\.\.)(\w+)?(?=\s|,|$)|#{TagAttributes.source}/) do |spread, identifier, key, value| - if spread - if identifier - spread_key = "...#{identifier}" - @attributes.delete(spread_key) - @attributes[spread_key] = parse_expression(identifier) - else - @attributes.delete("...") - @attributes["..."] = true - end - elsif key && value - @attributes.delete(key) - @attributes[key] = parse_expression(value) - end + markup.scan(TagAttributes) do |key, value| + @attributes[key] = parse_expression(value) end end diff --git a/test/integration/tags/snippet_test.rb b/test/integration/tags/snippet_test.rb index 18b41ad4..1476f9da 100644 --- a/test/integration/tags/snippet_test.rb +++ b/test/integration/tags/snippet_test.rb @@ -205,6 +205,49 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template) end + def test_render_snippets_as_arguments + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + + {% snippet header %} +
+ {{ message }} +
+ {% endsnippet %} + + {% snippet main %} + {% assign color_scheme = 'auto' %} + +
+ {% render header, message: 'Welcome!' %} +
+ {% endsnippet %} + + {% render main, header: header %} + LIQUID + + expected = <<~OUTPUT + + + + + + + + + +
+ +
+ Welcome! +
+ +
+ OUTPUT + + assert_template_result(expected, template) + end + def test_render_inline_snippet_shouldnt_leak_context template = <<~LIQUID.strip {% snippet input %} @@ -263,7 +306,7 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template) end - def test_render_inline_snippet_without_outside_context + def test_render_inline_snippet_ignores_outside_context template = <<~LIQUID.strip {% assign color_scheme = 'dark' %} @@ -291,18 +334,23 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template) end - def test_render_inline_snippet_with_outside_context - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - + def test_render_captured_snippet + template = <<~LIQUID {% snippet header %} -
+
{{ message }}
{% endsnippet %} + {% capture up_header %} + {%- render header, message: 'Welcome!' -%} + {% endcapture %} - {% render header, ..., message: 'Welcome!' %} + {{ up_header | upcase }} + + {{ header | upcase }} + + {{ header }} LIQUID expected = <<~OUTPUT @@ -310,10 +358,14 @@ class SnippetTest < Minitest::Test +
+ WELCOME! +
-
- Welcome! -
+ + SNIPPETDROP + + SnippetDrop OUTPUT assert_template_result(expected, template) @@ -332,7 +384,7 @@ class SnippetTest < Minitest::Test {{ color_scheme }} - {% render header, ..., message: 'Welcome!' %} + {% render header, message: 'Welcome!', color_scheme: color_scheme %} {{ color_scheme }} LIQUID @@ -356,328 +408,6 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template) end - def test_render_inline_snippet_with_correct_argument_precedence - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - {% assign message = 'Goodbye!' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, message: 'Welcome!', ... %} - LIQUID - expected = <<~OUTPUT - - - - - - - -
- Goodbye! -
- OUTPUT - - assert_template_result(expected, template) - end - - def test_render_inline_snippet_with_correct_argument_order - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - {% assign message = 'Goodbye!' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, ..., message: 'Welcome!' %} - LIQUID - expected = <<~OUTPUT - - - - - - - -
- Welcome! -
- OUTPUT - - assert_template_result(expected, template) - end - - def test_render_inline_snippet_with_correct_duplicate_argument_precedence - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - {% assign message = 'Goodbye!' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, message: 'Welcome!', ..., message: 'Hi!' %} - LIQUID - expected = <<~OUTPUT - - - - - - - -
- Hi! -
- OUTPUT - - assert_template_result(expected, template) - end - - def test_render_inline_snippet_with_spread_hash - template = <<~LIQUID.strip - {% snippet header %} -
- {{ word }} {{ number }} -
- {% endsnippet %} - - {% render header, ...details %} - LIQUID - - expected = <<~OUTPUT - - - -
- potato 5 -
- OUTPUT - - assert_template_result(expected, template, { 'details' => { 'word' => 'potato', 'number' => 5 } }) - end - - def test_render_inline_snippet_with_spread_drop - product_drop = Class.new(Liquid::Drop) do - def title - 'Cool Product' - end - - def price - 99 - end - - def vendor - 'Acme' - end - end - - template = <<~LIQUID.strip - {% snippet card %} -
- {{ title }} - ${{ price }} by {{ vendor }} -
- {% endsnippet %} - - {% render card, ...product %} - LIQUID - - expected = <<~OUTPUT - - - -
- Cool Product - $99 by Acme -
- OUTPUT - - assert_template_result(expected, template, { 'product' => product_drop.new }) - end - - def test_render_inline_snippet_with_overwritten_spread_drop - product_drop = Class.new(Liquid::Drop) do - def title - 'Cool Product' - end - - def price - 99 - end - - def vendor - 'Acme' - end - end - - template = <<~LIQUID.strip - {% snippet card %} -
- {{ title }} - ${{ price }} by {{ vendor }} -
- {% endsnippet %} - - {% render card, ...product, price: 10 %} - LIQUID - - expected = <<~OUTPUT - - - -
- Cool Product - $10 by Acme -
- OUTPUT - - assert_template_result(expected, template, { 'product' => product_drop.new }) - end - - def test_render_inline_snippet_spread_before_explicit_args - template = <<~LIQUID.strip - {% snippet card %} -
{{ price }}
- {% endsnippet %} - - {% render card, ...details, price: 10 %} - LIQUID - - expected = <<~OUTPUT - - - -
10
- OUTPUT - - assert_template_result(expected, template, { 'details' => { 'price' => 99 } }) - end - - def test_render_inline_snippet_multiple_spreads - product_drop = Class.new(Liquid::Drop) do - def title - 'Cool Product' - end - end - - template = <<~LIQUID.strip - {% snippet card %} -
{{ title }} - {{ price }} {{ color }}
- {% endsnippet %} - - {% render card, ...defaults, ...product %} - LIQUID - - expected = <<~OUTPUT - - - -
Cool Product - 10
- OUTPUT - - assert_template_result( - expected, - template, - { - 'defaults' => { 'title' => 'Default', 'price' => 10 }, - 'product' => product_drop.new, - }, - ) - end - - def test_render_captured_snippet - template = <<~LIQUID - {% assign color_scheme = 'dark' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - {% capture up_header %} - {%- render header, ..., message: 'Welcome!' -%} - {% endcapture %} - - {{ up_header | upcase }} - - {{ header | upcase }} - - {{ header }} - LIQUID - expected = <<~OUTPUT - - - - - - - -
- WELCOME! -
- - - SNIPPETDROP - - SnippetDrop - OUTPUT - - assert_template_result(expected, template) - end - - def test_render_snippets_as_arguments - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - {% snippet main %} - {% assign color_scheme = 'auto' %} - -
- {% render header, ..., message: 'Welcome!' %} -
- {% endsnippet %} - - {% render main, header: header %} - LIQUID - - expected = <<~OUTPUT - - - - - - - - - -
- -
- Welcome! -
- -
- OUTPUT - - assert_template_result(expected, template) - end - def test_render_inline_snippet_forloop template = <<~LIQUID.strip {% snippet item %} @@ -738,36 +468,6 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template) end - - def test_render_inline_snippet_inside_loop - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - {% assign array = '1,2,3' | split: ',' %} - - {% for i in array %} - {% snippet header %} -
- {{ message }} {{ i }} -
- {% endsnippet %} - {% endfor %} - - {% render header, ..., message: '👉' %} - LIQUID - expected = <<~OUTPUT - - - - - - -
- 👉#{" "} -
- OUTPUT - - assert_template_result(expected, template) - end end class RigidMode < SnippetTest @@ -970,6 +670,49 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template, error_mode: :rigid) end + def test_render_snippets_as_arguments + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + + {% snippet header %} +
+ {{ message }} +
+ {% endsnippet %} + + {% snippet main %} + {% assign color_scheme = 'auto' %} + +
+ {% render header, message: 'Welcome!' %} +
+ {% endsnippet %} + + {% render main, header: header %} + LIQUID + + expected = <<~OUTPUT + + + + + + + + + +
+ +
+ Welcome! +
+ +
+ OUTPUT + + assert_template_result(expected, template, error_mode: :rigid) + end + def test_render_inline_snippet_shouldnt_leak_context template = <<~LIQUID.strip {% snippet input %} @@ -1028,7 +771,7 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template, error_mode: :rigid) end - def test_render_inline_snippet_without_outside_context + def test_render_inline_snippet_ignores_outside_context template = <<~LIQUID.strip {% assign color_scheme = 'dark' %} @@ -1056,34 +799,6 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template, error_mode: :rigid) end - def test_render_inline_snippet_with_outside_context - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, ..., message: 'Welcome!' %} - LIQUID - expected = <<~OUTPUT - - - - - - -
- Welcome! -
- OUTPUT - - assert_template_result(expected, template, error_mode: :rigid) - end - def test_inline_snippet_local_scope_takes_precedence template = <<~LIQUID {% assign color_scheme = 'dark' %} @@ -1097,7 +812,7 @@ class SnippetTest < Minitest::Test {{ color_scheme }} - {% render header, ..., message: 'Welcome!' %} + {% render header, message: 'Welcome!', color_scheme: color_scheme %} {{ color_scheme }} LIQUID @@ -1121,306 +836,6 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template, error_mode: :rigid) end - def test_render_inline_snippet_with_correct_argument_precedence - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - {% assign message = 'Goodbye!' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, message: 'Welcome!', ... %} - LIQUID - expected = <<~OUTPUT - - - - - - - -
- Goodbye! -
- OUTPUT - - assert_template_result(expected, template, error_mode: :rigid) - end - - def test_render_inline_snippet_with_correct_argument_order - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - {% assign message = 'Goodbye!' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, ..., message: 'Welcome!' %} - LIQUID - expected = <<~OUTPUT - - - - - - - -
- Welcome! -
- OUTPUT - - assert_template_result(expected, template, error_mode: :rigid) - end - - def test_render_inline_snippet_with_correct_duplicate_argument_precedence - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - {% assign message = 'Goodbye!' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - - {% render header, message: 'Welcome!', ..., message: 'Hi!' %} - LIQUID - expected = <<~OUTPUT - - - - - - - -
- Hi! -
- OUTPUT - - assert_template_result(expected, template, error_mode: :rigid) - end - - def test_render_inline_snippet_with_spread_drop - product_drop = Class.new(Liquid::Drop) do - def title - 'Cool Product' - end - - def price - 99 - end - - def vendor - 'Acme' - end - end - - template = <<~LIQUID.strip - {% snippet card %} -
- {{ title }} - ${{ price }} by {{ vendor }} -
- {% endsnippet %} - - {% render card, ...product %} - LIQUID - - expected = <<~OUTPUT - - - -
- Cool Product - $99 by Acme -
- OUTPUT - - assert_template_result(expected, template, { 'product' => product_drop.new }, error_mode: :rigid) - end - - def test_render_inline_snippet_with_overwritten_spread_drop - product_drop = Class.new(Liquid::Drop) do - def title - 'Cool Product' - end - - def price - 99 - end - - def vendor - 'Acme' - end - end - - template = <<~LIQUID.strip - {% snippet card %} -
- {{ title }} - ${{ price }} by {{ vendor }} -
- {% endsnippet %} - - {% render card, ...product, price: 10 %} - LIQUID - - expected = <<~OUTPUT - - - -
- Cool Product - $10 by Acme -
- OUTPUT - - assert_template_result(expected, template, { 'product' => product_drop.new }, error_mode: :rigid) - end - - def test_render_inline_snippet_spread_before_explicit_args - template = <<~LIQUID.strip - {% snippet card %} -
{{ price }}
- {% endsnippet %} - - {% render card, ...details, price: 10 %} - LIQUID - - expected = <<~OUTPUT - - - -
10
- OUTPUT - - assert_template_result(expected, template, { 'details' => { 'price' => 99 } }, error_mode: :rigid) - end - - def test_render_inline_snippet_multiple_spreads - product_drop = Class.new(Liquid::Drop) do - def title - 'Cool Product' - end - end - - template = <<~LIQUID.strip - {% snippet card %} -
{{ title }} - {{ price }} {{ color }}
- {% endsnippet %} - - {% render card, ...defaults, ...product %} - LIQUID - - expected = <<~OUTPUT - - - -
Cool Product - 10
- OUTPUT - - assert_template_result( - expected, - template, - { - 'defaults' => { 'title' => 'Default', 'price' => 10 }, - 'product' => product_drop.new, - }, - error_mode: :rigid, - ) - end - - def test_render_captured_snippet - template = <<~LIQUID - {% assign color_scheme = 'dark' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - {% capture up_header %} - {%- render header, ..., message: 'Welcome!' -%} - {% endcapture %} - - {{ up_header | upcase }} - - {{ header | upcase }} - - {{ header }} - LIQUID - expected = <<~OUTPUT - - - - - - - -
- WELCOME! -
- - - SNIPPETDROP - - SnippetDrop - OUTPUT - - assert_template_result(expected, template, error_mode: :rigid) - end - - def test_render_snippets_as_arguments - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - - {% snippet header %} -
- {{ message }} -
- {% endsnippet %} - - {% snippet main %} - {% assign color_scheme = 'auto' %} - -
- {% render header, ..., message: 'Welcome!' %} -
- {% endsnippet %} - - {% render main, header: header %} - LIQUID - - expected = <<~OUTPUT - - - - - - - - - -
- -
- Welcome! -
- -
- OUTPUT - - assert_template_result(expected, template, error_mode: :rigid) - end - def test_render_inline_snippet_forloop template = <<~LIQUID.strip {% snippet item %} @@ -1482,20 +897,23 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template, error_mode: :rigid) end - def test_render_inline_snippet_inside_loop - template = <<~LIQUID.strip - {% assign color_scheme = 'dark' %} - {% assign array = '1,2,3' | split: ',' %} - - {% for i in array %} + def test_render_captured_snippet + template = <<~LIQUID {% snippet header %} -
- {{ message }} {{ i }} +
+ {{ message }}
{% endsnippet %} - {% endfor %} - {% render header, ..., message: '👉' %} + {% capture up_header %} + {%- render header, message: 'Welcome!' -%} + {% endcapture %} + + {{ up_header | upcase }} + + {{ header | upcase }} + + {{ header }} LIQUID expected = <<~OUTPUT @@ -1503,10 +921,14 @@ class SnippetTest < Minitest::Test +
+ WELCOME! +
-
- 👉#{" "} -
+ + SNIPPETDROP + + SnippetDrop OUTPUT assert_template_result(expected, template, error_mode: :rigid)