Optional line numbers for liquid errors

This commit is contained in:
Florian Weingarten
2014-09-05 14:12:29 +00:00
parent 2df643ba18
commit 5eff375094
7 changed files with 77 additions and 20 deletions
+1 -1
View File
@@ -66,11 +66,11 @@ require 'liquid/standardfilters'
require 'liquid/condition' require 'liquid/condition'
require 'liquid/module_ex' require 'liquid/module_ex'
require 'liquid/utils' require 'liquid/utils'
require 'liquid/token'
# Load all the tags of the standard library # Load all the tags of the standard library
# #
Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f } Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
require 'liquid/profiler' require 'liquid/profiler'
require 'liquid/profiler/token'
require 'liquid/profiler/hooks' require 'liquid/profiler/hooks'
+1 -1
View File
@@ -144,7 +144,7 @@ module Liquid
rescue MemoryError => e rescue MemoryError => e
raise e raise e
rescue ::StandardError => e rescue ::StandardError => e
output << (context.handle_error(e)) output << (context.handle_error(e, token))
end end
end end
+4 -9
View File
@@ -91,17 +91,12 @@ module Liquid
@interrupts.pop @interrupts.pop
end end
def handle_error(e)
def handle_error(e, token)
e = Liquid::Error.error_with_line_number(e, token)
errors.push(e) errors.push(e)
raise if exception_handler && exception_handler.call(e) raise if exception_handler && exception_handler.call(e)
Liquid::Error.render(e)
case e
when SyntaxError
"Liquid syntax error: #{e.message}"
else
"Liquid error: #{e.message}"
end
end end
def invoke(method, *args) def invoke(method, *args)
+31 -1
View File
@@ -1,5 +1,35 @@
module Liquid 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 ArgumentError < Error; end
class ContextError < Error; end class ContextError < Error; end
+4 -3
View File
@@ -105,6 +105,8 @@ module Liquid
def parse(source, options = {}) def parse(source, options = {})
@options = options @options = options
@profiling = options[:profile] @profiling = options[:profile]
@profiling = options.delete(:profile)
@line_numbers = options.delete(:line_numbers) || @profiling
@root = Document.parse(tokenize(source), DEFAULT_OPTIONS.merge(options)) @root = Document.parse(tokenize(source), DEFAULT_OPTIONS.merge(options))
@warnings = nil @warnings = nil
self self
@@ -197,7 +199,7 @@ module Liquid
end end
result.respond_to?(:join) ? result.join : result result.respond_to?(:join) ? result.join : result
rescue Liquid::MemoryError => e rescue Liquid::MemoryError => e
context.handle_error(e) context.handle_error(e, nil)
ensure ensure
@errors = context.errors @errors = context.errors
end end
@@ -224,7 +226,7 @@ module Liquid
end end
def calculate_line_numbers(raw_tokens) def calculate_line_numbers(raw_tokens)
return raw_tokens unless @profiling return raw_tokens unless @line_numbers
current_line = 1 current_line = 1
raw_tokens.map do |token| raw_tokens.map do |token|
@@ -248,6 +250,5 @@ module Liquid
yield yield
end end
end end
end end
end end
@@ -1,6 +1,5 @@
module Liquid module Liquid
class Token < String class Token < String
attr_reader :line_number attr_reader :line_number
def initialize(content, line_number) def initialize(content, line_number)
@@ -11,6 +10,5 @@ module Liquid
def raw def raw
"<raw>" "<raw>"
end end
end end
end end
+36 -3
View File
@@ -22,6 +22,39 @@ end
class ErrorHandlingTest < Minitest::Test class ErrorHandlingTest < Minitest::Test
include Liquid 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 def test_standard_error
template = Liquid::Template.parse( ' {{ errors.standard_error }} ' ) template = Liquid::Template.parse( ' {{ errors.standard_error }} ' )
assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new) assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
@@ -59,7 +92,7 @@ class ErrorHandlingTest < Minitest::Test
end end
end end
end end
def test_lax_unrecognized_operator def test_lax_unrecognized_operator
template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :lax) template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :lax)
assert_equal ' Liquid error: Unknown operator =! ', template.render 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 # Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
def test_exceptions_propagate def test_exceptions_propagate
assert_raises Exception do assert_raises Exception do
template = Liquid::Template.parse( ' {{ errors.exception }} ' ) template = Liquid::Template.parse('{{ errors.exception }}')
template.render('errors' => ErrorDrop.new) template.render('errors' => ErrorDrop.new)
end end
end end
end # ErrorHandlingTest end