Handle BlockBody#blank? at parse time (#1287)

This commit is contained in:
Dylan Thacker-Smith
2020-09-03 11:07:13 -04:00
committed by GitHub
parent b08bcf00ac
commit 3b486425b0
6 changed files with 39 additions and 11 deletions
+21 -3
View File
@@ -130,6 +130,26 @@ module Liquid
@blank @blank
end end
# Remove blank strings in the block body for a control flow tag (e.g. `if`, `for`, `case`, `unless`)
# with a blank body.
#
# For example, in a conditional assignment like the following
#
# ```
# {% if size > max_size %}
# {% assign size = max_size %}
# {% endif %}
# ```
#
# we assume the intention wasn't to output the blank spaces in the `if` tag's block body, so this method
# will remove them to reduce the render output size.
#
# Note that it is now preferred to use the `liquid` tag for this use case.
def remove_blank_strings
raise "remove_blank_strings only support being called on a blank block body" unless @blank
@nodelist.reject! { |node| node.instance_of?(String) }
end
def render(context) def render(context)
render_to_output_buffer(context, +'') render_to_output_buffer(context, +'')
end end
@@ -143,10 +163,8 @@ module Liquid
if node.instance_of?(String) if node.instance_of?(String)
output << node output << node
elsif node.instance_of?(Variable)
render_node(context, output, node)
else else
render_node(context, node.blank? ? +'' : output, node) render_node(context, output, node)
# If we get an Interrupt that means the block must stop processing. An # If we get an Interrupt that means the block must stop processing. An
# Interrupt is any command that stops block execution such as {% break %} # Interrupt is any command that stops block execution such as {% break %}
# or {% continue %}. These tags may also occur through Block or Include tags. # or {% continue %}. These tags may also occur through Block or Include tags.
+3 -4
View File
@@ -25,10 +25,9 @@ module Liquid
end end
def render_to_output_buffer(context, output) def render_to_output_buffer(context, output)
previous_output_size = output.bytesize capture_output = render(context)
super context.scopes.last[@to] = capture_output
context.scopes.last[@to] = output context.resource_limits.assign_score += capture_output.bytesize
context.resource_limits.assign_score += (output.bytesize - previous_output_size)
output output
end end
+3
View File
@@ -21,6 +21,9 @@ module Liquid
def parse(tokens) def parse(tokens)
body = BlockBody.new body = BlockBody.new
body = @blocks.last.attachment while parse_body(body, tokens) body = @blocks.last.attachment while parse_body(body, tokens)
if blank?
@blocks.each { |condition| condition.attachment.remove_blank_strings }
end
end end
def nodelist def nodelist
+7 -2
View File
@@ -59,8 +59,13 @@ module Liquid
end end
def parse(tokens) def parse(tokens)
return unless parse_body(@for_block, tokens) if parse_body(@for_block, tokens)
parse_body(@else_block, tokens) parse_body(@else_block, tokens)
end
if blank?
@for_block.remove_blank_strings
@else_block&.remove_blank_strings
end
end end
def nodelist def nodelist
+3
View File
@@ -31,6 +31,9 @@ module Liquid
def parse(tokens) def parse(tokens)
while parse_body(@blocks.last.attachment, tokens) while parse_body(@blocks.last.attachment, tokens)
end end
if blank?
@blocks.each { |condition| condition.attachment.remove_blank_strings }
end
end end
def unknown_tag(tag, markup, tokens) def unknown_tag(tag, markup, tokens)
+2 -2
View File
@@ -176,7 +176,7 @@ class TemplateTest < Minitest::Test
end end
def test_resource_limits_hash_in_template_gets_updated_even_if_no_limits_are_set def test_resource_limits_hash_in_template_gets_updated_even_if_no_limits_are_set
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}") t = Template.parse("{% for a in (1..100) %}x{% assign foo = 1 %} {% endfor %}")
t.render! t.render!
assert(t.resource_limits.assign_score > 0) assert(t.resource_limits.assign_score > 0)
assert(t.resource_limits.render_score > 0) assert(t.resource_limits.render_score > 0)
@@ -215,7 +215,7 @@ class TemplateTest < Minitest::Test
def test_default_resource_limits_unaffected_by_render_with_context def test_default_resource_limits_unaffected_by_render_with_context
context = Context.new context = Context.new
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}") t = Template.parse("{% for a in (1..100) %}x{% assign foo = 1 %} {% endfor %}")
t.render!(context) t.render!(context)
assert(context.resource_limits.assign_score > 0) assert(context.resource_limits.assign_score > 0)
assert(context.resource_limits.render_score > 0) assert(context.resource_limits.render_score > 0)