Optional line numbers for liquid errors

This commit is contained in:
Florian Weingarten
2014-08-14 16:11:21 +00:00
parent c2663258be
commit 627ef9e29d
7 changed files with 76 additions and 20 deletions

View File

@@ -66,11 +66,11 @@ require 'liquid/standardfilters'
require 'liquid/condition'
require 'liquid/module_ex'
require 'liquid/utils'
require 'liquid/token'
# Load all the tags of the standard library
#
Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
require 'liquid/profiler'
require 'liquid/profiler/token'
require 'liquid/profiler/hooks'

View File

@@ -144,7 +144,7 @@ module Liquid
rescue MemoryError => e
raise e
rescue ::StandardError => e
output << (context.handle_error(e))
output << (context.handle_error(e, token))
end
end

View File

@@ -91,17 +91,12 @@ module Liquid
@interrupts.pop
end
def handle_error(e)
def handle_error(e, token)
e = Liquid::Error.error_with_line_number(e, token)
errors.push(e)
raise if exception_handler && exception_handler.call(e)
case e
when SyntaxError
"Liquid syntax error: #{e.message}"
else
"Liquid error: #{e.message}"
end
Liquid::Error.render(e)
end
def invoke(method, *args)

View File

@@ -1,5 +1,35 @@
module Liquid
class Error < ::StandardError; end
class Error < ::StandardError
attr_accessor :line_number
def self.render(e)
msg = if e.is_a?(Liquid::Error) && e.line_number
"#{e.line_number}: #{e.message}"
else
e.message
end
case e
when SyntaxError
"Liquid syntax error: #{msg}"
else
"Liquid error: #{msg}"
end
end
def self.error_with_line_number(e, token)
if e.is_a?(Liquid::Error)
e.set_line_number_from_token(token)
end
e
end
def set_line_number_from_token(token)
return unless token.respond_to?(:line_number)
self.line_number = token.line_number
end
end
class ArgumentError < Error; end
class ContextError < Error; end

View File

@@ -104,6 +104,7 @@ module Liquid
# Returns self for easy chaining
def parse(source, options = {})
@profiling = options.delete(:profile)
@line_numbers = options.delete(:line_numbers) || @profiling
@root = Document.parse(tokenize(source), DEFAULT_OPTIONS.merge(options))
@warnings = nil
self
@@ -196,7 +197,7 @@ module Liquid
end
result.respond_to?(:join) ? result.join : result
rescue Liquid::MemoryError => e
context.handle_error(e)
context.handle_error(e, nil)
ensure
@errors = context.errors
end
@@ -223,7 +224,7 @@ module Liquid
end
def calculate_line_numbers(raw_tokens)
return raw_tokens unless @profiling
return raw_tokens unless @line_numbers
current_line = 1
raw_tokens.map do |token|
@@ -247,6 +248,5 @@ module Liquid
yield
end
end
end
end

View File

@@ -1,6 +1,5 @@
module Liquid
class Token < String
attr_reader :line_number
def initialize(content, line_number)
@@ -11,6 +10,5 @@ module Liquid
def raw
"<raw>"
end
end
end

View File

@@ -22,6 +22,39 @@ end
class ErrorHandlingTest < Minitest::Test
include Liquid
def test_templates_parsed_with_line_numbers_renders_them_in_errors
template = <<-LIQUID
Hello,
{{ errors.standard_error }} will raise a standard error.
Bla bla test.
{{ errors.syntax_error }} will raise a syntax error.
This is an argument error: {{ errors.argument_error }}
Bla.
LIQUID
expected = <<-TEXT
Hello,
Liquid error: 3: standard error will raise a standard error.
Bla bla test.
Liquid syntax error: 7: syntax error will raise a syntax error.
This is an argument error: Liquid error: 9: argument error
Bla.
TEXT
output = Liquid::Template.parse(template, line_numbers: true).render('errors' => ErrorDrop.new)
assert_equal expected, output
end
def test_standard_error
template = Liquid::Template.parse( ' {{ errors.standard_error }} ' )
assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
@@ -59,7 +92,7 @@ class ErrorHandlingTest < Minitest::Test
end
end
end
def test_lax_unrecognized_operator
template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :lax)
assert_equal ' Liquid error: Unknown operator =! ', template.render
@@ -91,8 +124,8 @@ class ErrorHandlingTest < Minitest::Test
# Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
def test_exceptions_propagate
assert_raises Exception do
template = Liquid::Template.parse( ' {{ errors.exception }} ' )
template = Liquid::Template.parse('{{ errors.exception }}')
template.render('errors' => ErrorDrop.new)
end
end
end # ErrorHandlingTest
end