From 4854bbcfb19ebc11d9145aa5199542b474962a80 Mon Sep 17 00:00:00 2001 From: Peter Menhart Date: Tue, 23 Apr 2024 22:01:09 -0400 Subject: [PATCH] Permit variables in Liquid-style tag 'include with' (#299) * Only String literals were permitted as parameter of `include with 'param'` * Now it is possible to reference objects: `include with var` --- ruby/cases_include_with_syntax_with.rb | 2 ++ .../antlr4/liquid/parser/v4/LiquidParser.g4 | 2 +- src/main/java/liqp/parser/v4/NodeVisitor.java | 6 ++--- .../java/liqp/parser/v4/LiquidParserTest.java | 10 ++++++++ src/test/java/liqp/tags/IncludeTest.java | 23 ++++++++++++++++++- 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/ruby/cases_include_with_syntax_with.rb b/ruby/cases_include_with_syntax_with.rb index 7ebb8467..e3dabf13 100755 --- a/ruby/cases_include_with_syntax_with.rb +++ b/ruby/cases_include_with_syntax_with.rb @@ -32,6 +32,8 @@ else Liquid::Template.file_system = Liquid::LocalFileSystem.new("cases_variable_inside_import/_includes", "%s.liquid") assertEqual("color: 'red'", render({"var" => "there"}, "{% include 'color' with 'red' %}")) + assertEqual("color: 'blue'", render({"clr" => "blue"}, "{% include 'color' with clr %}")) + assertEqual("color: 'yellow'", render({}, "{% assign clr = 'yellow' %}{% include 'color' with clr %}")) # liquid variable evaluate assertEqual("there", render({"var" => "there", "tmpl" => "include_read_var"}, "{% include tmpl %}")) end diff --git a/src/main/antlr4/liquid/parser/v4/LiquidParser.g4 b/src/main/antlr4/liquid/parser/v4/LiquidParser.g4 index ee467c34..35e9a4c6 100644 --- a/src/main/antlr4/liquid/parser/v4/LiquidParser.g4 +++ b/src/main/antlr4/liquid/parser/v4/LiquidParser.g4 @@ -202,7 +202,7 @@ capture_tag ; include_tag - : {isLiquidStyleInclude()}? TagStart liquid=Include expr (With Str)? TagEnd + : {isLiquidStyleInclude()}? TagStart liquid=Include expr (With expr)? TagEnd | {isJekyllStyleInclude()}? TagStart jekyll=Include file_name_or_output (jekyll_include_params)* TagEnd ; diff --git a/src/main/java/liqp/parser/v4/NodeVisitor.java b/src/main/java/liqp/parser/v4/NodeVisitor.java index 35262820..2b45ab27 100644 --- a/src/main/java/liqp/parser/v4/NodeVisitor.java +++ b/src/main/java/liqp/parser/v4/NodeVisitor.java @@ -458,10 +458,10 @@ public LNode visitInclude_tag(Include_tagContext ctx) { if (ctx.jekyll != null) { return getJekyllIncludeInsertionNode("include", ctx.file_name_or_output(), ctx.jekyll_include_params()); } else if (ctx.liquid != null) { - if (ctx.Str() != null) { - return new InsertionNode(insertions.get("include"), visit(ctx.expr()), new AtomNode(strip(ctx.Str().getText()))); + if (ctx.With() != null) { + return new InsertionNode(insertions.get("include"), visit(ctx.expr(0)), visit(ctx.expr(1))); } else { - return new InsertionNode(insertions.get("include"), visit(ctx.expr())); + return new InsertionNode(insertions.get("include"), visit(ctx.expr(0))); } } throw new LiquidException("Unknown syntax of `Include` tag", ctx); diff --git a/src/test/java/liqp/parser/v4/LiquidParserTest.java b/src/test/java/liqp/parser/v4/LiquidParserTest.java index 6ecf8bfc..964fbc4d 100644 --- a/src/test/java/liqp/parser/v4/LiquidParserTest.java +++ b/src/test/java/liqp/parser/v4/LiquidParserTest.java @@ -373,6 +373,16 @@ public void testInclude_tag() { equalTo(array("{%", "include", "some-file.extwithmu", "%}")) ); + assertThat( + texts("{% include some-file.ext with mu %}", "include_tag", true), + equalTo(array("{%", "include", "some-file.ext", "with", "mu", "%}")) + ); + + assertThat( + texts("{% include 'some-file.ext' with 'mu' %}", "include_tag", true), + equalTo(array("{%", "include", "'some-file.ext'", "with", "'mu'", "%}")) + ); + assertThat( texts("{% include {{variable}} %}", "include_tag", false), equalTo(array("{%", "include", "{{variable}}", "%}")) diff --git a/src/test/java/liqp/tags/IncludeTest.java b/src/test/java/liqp/tags/IncludeTest.java index 8b1ecd88..7524bb59 100644 --- a/src/test/java/liqp/tags/IncludeTest.java +++ b/src/test/java/liqp/tags/IncludeTest.java @@ -28,7 +28,9 @@ public void renderTest() throws RecognitionException { "{% include 'color' with 'red' %}\n" + "{% include 'color' with 'blue' %}\n" + "{% assign shape = 'square' %}\n" + - "{% include 'color' with 'red' %}"; + "{% include 'color' with 'red' %}\n" + + "{%- assign cVar = 'yellow' %}\n" + + "{% include 'color' with cVar %}"; Template template = TemplateParser.DEFAULT.parse(source); @@ -43,6 +45,8 @@ public void renderTest() throws RecognitionException { "shape: 'circle'\n" + "\n" + "color: 'red'\n" + + "shape: 'square'\n" + + "color: 'yellow'\n" + "shape: 'square'")); } @@ -107,6 +111,23 @@ public void renderWithShouldWorkInLiquid() throws RecognitionException { assertEquals("color: 'red'\nshape: ''", render); } + @Test + public void renderWithVariableShouldWorkInLiquid() throws RecognitionException { + TemplateParser parser = new TemplateParser.Builder() // + .withFlavor(Flavor.LIQUID) + .withShowExceptionsFromInclude(true) + .withErrorMode(TemplateParser.ErrorMode.STRICT) + .build(); + + Template template = parser.parse("{% assign c = 'blue' %}{% include 'color' with c %}"); + String render = template.render(); + assertEquals("color: 'blue'\nshape: ''", render); + + template = parser.parse("{% include 'color' with theme.color %}"); + render = template.render("{ \"theme\": {\"color\": \"orange\"}}"); + assertEquals("color: 'orange'\nshape: ''", render); + } + @Test public void renderTestWithIncludeDirectorySpecifiedInContextLiquidFlavor() throws Exception { File jekyll = new File(new File("").getAbsolutePath(), "src/test/jekyll");