lib/liquid/block_body.rb in liquid-4.0.0 vs lib/liquid/block_body.rb in liquid-4.0.1

- old
+ new

@@ -1,9 +1,10 @@ module Liquid class BlockBody FullToken = /\A#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*?)#{WhitespaceControl}?#{TagEnd}\z/om ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om + WhitespaceOrNothing = /\A\s*\z/ TAGSTART = "{%".freeze VARSTART = "{{".freeze attr_reader :nodelist @@ -13,42 +14,39 @@ end def parse(tokenizer, parse_context) parse_context.line_number = tokenizer.line_number while token = tokenizer.shift - unless token.empty? - case - when token.start_with?(TAGSTART) - whitespace_handler(token, parse_context) - if token =~ FullToken - tag_name = $1 - markup = $2 - # fetch the tag from registered blocks - if tag = registered_tags[tag_name] - new_tag = tag.parse(tag_name, markup, tokenizer, parse_context) - @blank &&= new_tag.blank? - @nodelist << new_tag - else - # end parsing if we reach an unknown tag and let the caller decide - # determine how to proceed - return yield tag_name, markup - end - else - raise_missing_tag_terminator(token, parse_context) - end - when token.start_with?(VARSTART) - whitespace_handler(token, parse_context) - @nodelist << create_variable(token, parse_context) - @blank = false - else - if parse_context.trim_whitespace - token.lstrip! - end - parse_context.trim_whitespace = false - @nodelist << token - @blank &&= !!(token =~ /\A\s*\z/) + next if token.empty? + case + when token.start_with?(TAGSTART) + whitespace_handler(token, parse_context) + unless token =~ FullToken + raise_missing_tag_terminator(token, parse_context) end + tag_name = $1 + markup = $2 + # fetch the tag from registered blocks + unless tag = registered_tags[tag_name] + # end parsing if we reach an unknown tag and let the caller decide + # determine how to proceed + return yield tag_name, markup + end + new_tag = tag.parse(tag_name, markup, tokenizer, parse_context) + @blank &&= new_tag.blank? + @nodelist << new_tag + when token.start_with?(VARSTART) + whitespace_handler(token, parse_context) + @nodelist << create_variable(token, parse_context) + @blank = false + else + if parse_context.trim_whitespace + token.lstrip! + end + parse_context.trim_whitespace = false + @nodelist << token + @blank &&= !!(token =~ WhitespaceOrNothing) end parse_context.line_number = tokenizer.line_number end yield nil, nil @@ -70,51 +68,56 @@ def render(context) output = [] context.resource_limits.render_score += @nodelist.length - @nodelist.each do |token| - # Break out if we have any unhanded interrupts. - break if context.interrupt? - - begin + idx = 0 + while node = @nodelist[idx] + case node + when String + check_resources(context, node) + output << node + when Variable + render_node_to_output(node, output, context) + when Block + render_node_to_output(node, output, context, node.blank?) + break if context.interrupt? # might have happened in a for-block + when Continue, Break # If we get an Interrupt that means the block must stop processing. An # Interrupt is any command that stops block execution such as {% break %} # or {% continue %} - if token.is_a?(Continue) || token.is_a?(Break) - context.push_interrupt(token.interrupt) - break - end - - node_output = render_node(token, context) - - unless token.is_a?(Block) && token.blank? - output << node_output - end - rescue MemoryError => e - raise e - rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e - context.handle_error(e, token.line_number, token.raw) - output << nil - rescue ::StandardError => e - output << context.handle_error(e, token.line_number, token.raw) + context.push_interrupt(node.interrupt) + break + else # Other non-Block tags + render_node_to_output(node, output, context) end + idx += 1 end output.join end private - def render_node(node, context) - node_output = (node.respond_to?(:render) ? node.render(context) : node) + def render_node_to_output(node, output, context, skip_output = false) + node_output = node.render(context) node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s + check_resources(context, node_output) + output << node_output unless skip_output + rescue MemoryError => e + raise e + rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e + context.handle_error(e, node.line_number) + output << nil + rescue ::StandardError => e + line_number = node.is_a?(String) ? nil : node.line_number + output << context.handle_error(e, line_number) + end + def check_resources(context, node_output) context.resource_limits.render_length += node_output.length - if context.resource_limits.reached? - raise MemoryError.new("Memory limits exceeded".freeze) - end - node_output + return unless context.resource_limits.reached? + raise MemoryError.new("Memory limits exceeded".freeze) end def create_variable(token, parse_context) token.scan(ContentOfVariable) do |content| markup = content.first