From 1eca707c4a40a035b924500726720dbffd5c4214 Mon Sep 17 00:00:00 2001 From: Julia Boutin Date: Fri, 26 Sep 2025 17:46:44 -0600 Subject: [PATCH] Update inline snippets syntax Previously, inline snippets syntax looked a bit different, they: - used strings as tag identifiers - defined tag arguments {% snippet "input" |type| %} This PR updates snippets to better reflect the currently proposed syntax Co-authored-by: Orlando Qiu --- lib/liquid/tags/render.rb | 14 +- lib/liquid/tags/snippet.rb | 29 +--- test/integration/tags/snippet_test.rb | 216 +++++++++++++++++++++++--- 3 files changed, 205 insertions(+), 54 deletions(-) diff --git a/lib/liquid/tags/render.rb b/lib/liquid/tags/render.rb index 5bff6073..c142caca 100644 --- a/lib/liquid/tags/render.rb +++ b/lib/liquid/tags/render.rb @@ -54,15 +54,15 @@ module Liquid # Inline snippets take precedence over external snippets if (inline_snippet = context.registers[:inline_snippet][template_name]) inner_context = context.new_isolated_subcontext - snippet_body = inline_snippet[:body] - snippet_args = inline_snippet[:args] - # Validate and set the arguments in the inner context - @attributes.each do |key, value| - unless snippet_args.include?(key) - raise Liquid::ArgumentError, "Invalid argument `#{key}` for snippet `#{template_name}`" - end + context.scopes.each do |scope| + scope.each do |key, value| + inner_context[key] = value + end + end + + @attributes.each do |key, value| inner_context[key] = context.evaluate(value) end diff --git a/lib/liquid/tags/snippet.rb b/lib/liquid/tags/snippet.rb index 72c6c282..7f907274 100644 --- a/lib/liquid/tags/snippet.rb +++ b/lib/liquid/tags/snippet.rb @@ -1,28 +1,25 @@ # frozen_string_literal: true module Liquid - # @liquid_public_docs # @liquid_type tag # @liquid_category theme # @liquid_name snippet # @liquid_summary - # Creates a new inline snippet using a string value as the identifier. + # Creates a new inline snippet. # @liquid_description # You can create inline snippets to make your Liquid code more modular. # @liquid_syntax - # {% snippet "input" %} + # {% snippet input %} # value # {% endsnippet %} + class Snippet < Block - SYNTAX = /(#{QuotedString})(?:\s*\|\s*([\w\s,]+)\s*\|)?/o + SYNTAX = /(#{VariableSignature}+)/o + def initialize(tag_name, markup, options) super - if markup =~ SYNTAX @to = Regexp.last_match(1) - args = Regexp.last_match(2) - - @args = args ? args.split(/\s*,\s*/) : [] else raise SyntaxError, options[:locale].t("errors.syntax.snippet") end @@ -30,22 +27,10 @@ module Liquid def render(context) context.registers[:inline_snippet] ||= {} - context.registers[:inline_snippet][snippet_id] = { - body: snippet_body, - args: @args, + context.registers[:inline_snippet][@to] = { + body: @body, } '' end - - private - - def snippet_id - @to[1, @to.size - 2] - end - - def snippet_body - body = @body - body - end end end diff --git a/test/integration/tags/snippet_test.rb b/test/integration/tags/snippet_test.rb index fd1cbcdb..24586227 100644 --- a/test/integration/tags/snippet_test.rb +++ b/test/integration/tags/snippet_test.rb @@ -7,7 +7,7 @@ class SnippetTest < Minitest::Test def test_valid_inline_snippet template = <<~LIQUID.strip - {% snippet "input" %} + {% snippet input %} Hey {% endsnippet %} LIQUID @@ -16,20 +16,9 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template) end - def test_invalid_inline_snippet - template = <<~LIQUID.strip - {% snippet input %} - Hey - {% endsnippet %} - LIQUID - expected = "Syntax Error in 'snippet' - Valid syntax: snippet [quoted string]" - - assert_match_syntax_error(expected, template) - end - def test_render_inline_snippet template = <<~LIQUID.strip - {% snippet "hey" %} + {% snippet hey %} Hey {% endsnippet %} @@ -45,11 +34,11 @@ class SnippetTest < Minitest::Test def test_render_multiple_inline_snippets template = <<~LIQUID.strip - {% snippet "input" %} + {% snippet input %} {% endsnippet %} - {% snippet "banner" %} + {% snippet banner %} Welcome to my store! @@ -74,7 +63,7 @@ class SnippetTest < Minitest::Test def test_render_inline_snippet_with_argument template = <<~LIQUID.strip - {% snippet "input" |type| %} + {% snippet input %} {% endsnippet %} @@ -88,9 +77,36 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template) end + def test_render_inline_snippet_with_doc_tag + template = <<~LIQUID.strip + {% snippet input %} + {% doc %} + @param {string} type - Input type. + {% enddoc %} + + + {% endsnippet %} + + {%- render "input", type: "text" -%} + LIQUID + expected = <<~OUTPUT + + + + + OUTPUT + + assert_template_result(expected, template) + end + def test_render_inline_snippet_with_multiple_arguments template = <<~LIQUID.strip - {% snippet "input" |type, value| %} + {% snippet input %} + {% doc %} + @param {string} type - Input type. + @param {string} value - Input value. + {% enddoc %} + {% endsnippet %} @@ -98,6 +114,8 @@ class SnippetTest < Minitest::Test LIQUID expected = <<~OUTPUT + + OUTPUT @@ -106,24 +124,25 @@ class SnippetTest < Minitest::Test def test_render_inline_snippets_using_same_argument_name template = <<~LIQUID.strip - {% snippet "input" |type| %} + {% snippet input %} {% endsnippet %} - {% snippet "inputs" |type, value| %} - + {% snippet inputs %} + {% endsnippet %} {%- render "input", type: "text" -%} {%- render "inputs", type: "password", value: "pass" -%} LIQUID + expected = <<~OUTPUT - + OUTPUT assert_template_result(expected, template) @@ -131,7 +150,12 @@ class SnippetTest < Minitest::Test def test_render_inline_snippet_empty_string_when_missing_argument template = <<~LIQUID.strip - {% snippet "input" |type| %} + {% snippet input %} + {% doc %} + @param {string} type - Input type. + @param {string} value - Input value. + {% enddoc %} + {% endsnippet %} @@ -139,6 +163,8 @@ class SnippetTest < Minitest::Test LIQUID expected = <<~OUTPUT + + OUTPUT @@ -147,7 +173,12 @@ class SnippetTest < Minitest::Test def test_render_inline_snippet_shouldnt_leak_context template = <<~LIQUID.strip - {% snippet "input" |type, value| %} + {% snippet input %} + {% doc %} + @param {string} type - Input type. + @param {string} value - Input value. + {% enddoc %} + {% endsnippet %} @@ -158,6 +189,8 @@ class SnippetTest < Minitest::Test LIQUID expected = <<~OUTPUT + + OUTPUT @@ -167,10 +200,15 @@ class SnippetTest < Minitest::Test def test_render_multiple_inline_snippets_without_leaking_context template = <<~LIQUID.strip - {% snippet "input" |type| %} + {% snippet input %} + {% doc %} + @param {string} type - Input type. + {% enddoc %} + {% endsnippet %} - {% snippet "no_leak" %} + + {% snippet no_leak %} {% endsnippet %} @@ -180,6 +218,9 @@ class SnippetTest < Minitest::Test expected = <<~OUTPUT + + + @@ -187,4 +228,129 @@ class SnippetTest < Minitest::Test assert_template_result(expected, template) end + + def test_render_parent_context_variable + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + + {% snippet header %} + {% doc %} + @param {string} message - Message. + {% enddoc %} + +
+ {{ message }} +
+ {% endsnippet %} + + {%- render "header", message: "Welcome to my site" -%} + LIQUID + expected = <<~OUTPUT + + + + + +
+ Welcome to my site +
+ OUTPUT + + assert_template_result(expected, template) + end + + def test_deeply_nested_snippets + template = <<~LIQUID.strip + {% assign color_scheme = 'first-color' %} + {% snippet first %} + {% assign color_scheme = 'second-color' %} + {% snippet second %} + {% assign color_scheme = 'third-color' %} + {% snippet third %} +
+ This is a header +
+ {% endsnippet %} + {%- render "third" -%} + {% endsnippet %} + {%- render "second" -%} + {% endsnippet %} + + {%- render "first" -%} + LIQUID + expected = <<~OUTPUT + + + + + + +
+ This is a header +
+ OUTPUT + + assert_template_result(expected, template) + end + + def test_render_snippet_with_variables_in_both_scopes + template = <<~LIQUID.strip + {% assign color_scheme = 'dark' %} + + {% snippet header %} + {% assign color_scheme = 'light' %} +
+ {{ message }} +
+ {% endsnippet %} + + {{ color_scheme }} + + {%- render "header", message: 'Welcome to my site' -%} + LIQUID + expected = <<~OUTPUT + + + + + dark + +
+ Welcome to my site +
+ OUTPUT + + assert_template_result(expected, template) + end + + # def test_render_snippets_as_arguments + # template = <<~LIQUID.strip + # {% assign color_scheme = 'dark' %} + + # {% snippet main_header %} + # {% assign color_scheme = 'auto' %} + + #
+ # {%- render "header", message: 'Welcome to my site' -%} + #
+ # {% endsnippet %} + + # {% snippet header %} + #
+ # {{ message }} + #
+ # {% endsnippet %} + + # {%- render "main_header", header: header -%} + # LIQUID + # expected = <<~OUTPUT + #
+ #
+ # Welcome to my site + #
+ #
+ # OUTPUT + + # assert_template_result(expected, template) + # end end