From 47fbff07fa9184dfbedd9ad536fb192378f27c3f Mon Sep 17 00:00:00 2001 From: Chris AtLee Date: Tue, 18 Jan 2022 18:08:43 -0500 Subject: [PATCH] WIP - Support {% render obj %} --- lib/liquid.rb | 1 + lib/liquid/locales/en.yml | 1 + lib/liquid/renderabledrop.rb | 9 ++++++ lib/liquid/tags/render.rb | 37 ++++++++++++++++++++---- test/integration/tags/render_tag_test.rb | 5 ++-- 5 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 lib/liquid/renderabledrop.rb diff --git a/lib/liquid.rb b/lib/liquid.rb index 28337943..4849c7c1 100644 --- a/lib/liquid.rb +++ b/lib/liquid.rb @@ -64,6 +64,7 @@ require 'liquid/strainer_template' require 'liquid/expression' require 'liquid/context' require 'liquid/parser_switching' +require 'liquid/renderabledrop' require 'liquid/tag' require 'liquid/tag/disabler' require 'liquid/tag/disableable' diff --git a/lib/liquid/locales/en.yml b/lib/liquid/locales/en.yml index eb35d868..33adc9b7 100644 --- a/lib/liquid/locales/en.yml +++ b/lib/liquid/locales/en.yml @@ -24,5 +24,6 @@ render: "Syntax error in tag 'render' - Template name must be a quoted string" argument: include: "Argument error in tag 'include' - Illegal template name" + render: "Argument error in tag 'render' - Illegal template name" disabled: tag: "usage is not allowed in this context" diff --git a/lib/liquid/renderabledrop.rb b/lib/liquid/renderabledrop.rb new file mode 100644 index 00000000..2894ac39 --- /dev/null +++ b/lib/liquid/renderabledrop.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Liquid + class RenderableDrop < Drop + def render(_context, _output) + raise NotImplementedError, "render must be implemented for #{self.class.name}" + end + end +end diff --git a/lib/liquid/tags/render.rb b/lib/liquid/tags/render.rb index 6380249c..295fbd6a 100644 --- a/lib/liquid/tags/render.rb +++ b/lib/liquid/tags/render.rb @@ -3,7 +3,20 @@ module Liquid class Render < Tag FOR = 'for' - SYNTAX = /(#{QuotedString}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o + SYNTAX = %r{ + ( + ## for {% render "snippet" %} + #{Liquid::QuotedString}+ | + ## for {% render block %} + \A#{Liquid::VariableSegment}+ + ) + ## for {% render "snippet" with product as p %} + ## or {% render "snippet" for products p %} + (\s+(with|#{Liquid::Render::FOR})\s+(#{Liquid::QuotedFragment}+))? + (\s+(?:as)\s+(#{Liquid::VariableSegment}+))? + ## variables passed into the tag (e.g. {% render "snippet", var1: value1, var2: value2 %} + ## are not matched by this regex and are handled by .initialize + }xo disable_tags "include" @@ -14,13 +27,13 @@ module Liquid raise SyntaxError, options[:locale].t("errors.syntax.render") unless markup =~ SYNTAX - template_name = Regexp.last_match(1) + @template_name = Regexp.last_match(1) with_or_for = Regexp.last_match(3) variable_name = Regexp.last_match(4) @alias_name = Regexp.last_match(6) @variable_name_expr = variable_name ? parse_expression(variable_name) : nil - @template_name_expr = parse_expression(template_name) + @template_name_expr = parse_expression(@template_name) @for = (with_or_for == FOR) @attributes = {} @@ -34,9 +47,21 @@ module Liquid end def render_tag(context, output) - # Though we evaluate this here we will only ever parse it as a string literal. - template_name = context.evaluate(@template_name_expr) - raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name + render_target = context.evaluate(@template_name_expr) + raise ArgumentError, options[:locale].t("errors.argument.render") unless render_target + + # Check to see if this is a renderable drop + if render_target.is_a?(Liquid::RenderableDrop) + return render_target.render(context, output) + end + + # Otherwise it must be a quoted string + unless /#{Liquid::QuotedString}+/.match?(@template_name) + output << "" + return + end + + template_name = render_target partial = PartialCache.load( template_name, diff --git a/test/integration/tags/render_tag_test.rb b/test/integration/tags/render_tag_test.rb index 1af22b9b..c17de80d 100644 --- a/test/integration/tags/render_tag_test.rb +++ b/test/integration/tags/render_tag_test.rb @@ -75,9 +75,8 @@ class RenderTagTest < Minitest::Test def test_dynamically_choosen_templates_are_not_allowed Liquid::Template.file_system = StubFileSystem.new('snippet' => 'should not be rendered') - assert_raises(Liquid::SyntaxError) do - Liquid::Template.parse("{% assign name = 'snippet' %}{% render name %}") - end + assert_equal("", + Liquid::Template.parse("{% assign name = 'snippet' %}{% render name %}").render!) end def test_include_tag_caches_second_read_of_same_partial