From db350c54ff24f2bb9c6878f2d85348fb9d34f9ba Mon Sep 17 00:00:00 2001 From: Julia Boutin Date: Thu, 23 Oct 2025 13:06:49 -0600 Subject: [PATCH] Allow render tag to recognize drops that respond to to_partial --- example/server/templates/index.liquid | 5 ++-- lib/liquid/locales/en.yml | 1 + lib/liquid/snippet_drop.rb | 9 ++++-- lib/liquid/tags/render.rb | 35 ++++++++---------------- lib/liquid/tags/snippet.rb | 2 +- test/integration/tags/render_tag_test.rb | 12 ++++++-- 6 files changed, 34 insertions(+), 30 deletions(-) diff --git a/example/server/templates/index.liquid b/example/server/templates/index.liquid index e18cc306..4872aa84 100644 --- a/example/server/templates/index.liquid +++ b/example/server/templates/index.liquid @@ -1,5 +1,6 @@

Hello world!

-

It is {{ date }}

+

It is {{date}}

-

Check out the Products screen

+ +

Check out the Products screen

diff --git a/lib/liquid/locales/en.yml b/lib/liquid/locales/en.yml index 31eec5fd..7a9fb71e 100644 --- a/lib/liquid/locales/en.yml +++ b/lib/liquid/locales/en.yml @@ -31,5 +31,6 @@ variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}" argument: include: "Argument error in tag 'include' - Illegal template name" + render: "Argument error in tag 'render' - Dynamically chosen templates are not allowed" disabled: tag: "usage is not allowed in this context" diff --git a/lib/liquid/snippet_drop.rb b/lib/liquid/snippet_drop.rb index fb48d068..11648826 100644 --- a/lib/liquid/snippet_drop.rb +++ b/lib/liquid/snippet_drop.rb @@ -2,11 +2,16 @@ module Liquid class SnippetDrop < Drop - attr_reader :body + attr_reader :body, :name - def initialize(body) + def initialize(body, name) super() @body = body + @name = name + end + + def to_partial + @body end def to_s diff --git a/lib/liquid/tags/render.rb b/lib/liquid/tags/render.rb index 55ab7d3f..c337f4da 100644 --- a/lib/liquid/tags/render.rb +++ b/lib/liquid/tags/render.rb @@ -47,40 +47,30 @@ module Liquid end def render_tag(context, output) - template_name = @template_name_expr - is_inline = template_name.is_a?(VariableLookup) - is_file = template_name.is_a?(String) + template = context.evaluate(@template_name_expr) - if is_inline - template_name = template_name.name - snippet_drop = context[template_name] - raise ::ArgumentError unless snippet_drop.is_a?(Liquid::SnippetDrop) - - partial = snippet_drop.body + if template.respond_to?(:to_partial) + partial = template.to_partial + template_name = template.name + elsif @template_name_expr.is_a?(String) + partial = PartialCache.load(template, context: context, parse_context: parse_context) + template_name = partial.name else - raise ::ArgumentError unless is_file - - partial = PartialCache.load(template_name, context: context, parse_context: parse_context) + raise ::ArgumentError, parse_context.locale.t("errors.argument.render") end context_variable_name = @alias_name || template_name.split('/').last render_partial_func = ->(var, forloop) { - inner_context = context.new_isolated_subcontext - - if is_file - inner_context.template_name = partial.name - inner_context.partial = true - end - - inner_context['forloop'] = forloop if forloop + inner_context = context.new_isolated_subcontext + inner_context.template_name = template_name + inner_context.partial = true + inner_context['forloop'] = forloop if forloop @attributes.each do |key, value| inner_context[key] = context.evaluate(value) end - inner_context[context_variable_name] = var unless var.nil? - partial.render_to_output_buffer(inner_context, output) forloop&.send(:increment!) } @@ -113,7 +103,6 @@ module Liquid key = p.consume p.consume(:colon) @attributes[key] = safe_parse_expression(p) - p.consume?(:comma) # optional comma end diff --git a/lib/liquid/tags/snippet.rb b/lib/liquid/tags/snippet.rb index cf6ef756..2d49cdf6 100644 --- a/lib/liquid/tags/snippet.rb +++ b/lib/liquid/tags/snippet.rb @@ -25,7 +25,7 @@ module Liquid end def render_to_output_buffer(context, output) - snippet_drop = SnippetDrop.new(@body) + snippet_drop = SnippetDrop.new(@body, @to) context.scopes.last[@to] = snippet_drop snippet_size = @body.nodelist.sum { |node| node.to_s.bytesize } diff --git a/test/integration/tags/render_tag_test.rb b/test/integration/tags/render_tag_test.rb index e4ce0c29..4dedaaae 100644 --- a/test/integration/tags/render_tag_test.rb +++ b/test/integration/tags/render_tag_test.rb @@ -101,10 +101,11 @@ class RenderTagTest < Minitest::Test end end - def test_dynamically_choosen_templates_are_not_allowed - assert_raises(::ArgumentError) do + def test_dynamically_chosen_templates_are_not_allowed + error = assert_raises(::ArgumentError) do Template.parse('{% assign name = "snippet" %}{% render name %}').render! end + assert_equal("Argument error in tag 'render' - Dynamically chosen templates are not allowed", error.message) end def test_rigid_parsing_errors @@ -296,6 +297,13 @@ class RenderTagTest < Minitest::Test ) end + def test_render_tag_with_snippet_drop + assert_template_result( + "Hello from snippet", + "{% snippet my_snippet %}Hello from snippet{% endsnippet %}{% render my_snippet %}", + ) + end + def test_render_tag_renders_error_with_template_name assert_template_result( 'Liquid error (foo line 1): standard error',