1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

Begin making error messages DRY.

See issue #558
This commit is contained in:
Norman Clarke 2012-06-18 18:51:31 -03:00
parent 0b851a97a4
commit ea3a5275e5
6 changed files with 129 additions and 105 deletions

View file

@ -262,7 +262,7 @@ END
if ["maruku", "textile"].include?(name)
raise Error.new("To use the \"#{name}\" filter, please install the haml-contrib gem.", @node.line - 1)
else
raise Error.new("Filter \"#{name}\" is not defined.", @node.line - 1)
raise Error.new(Error.message(:filter_not_defined, name), @node.line - 1)
end
end
filter.internal_compile(self, @node.value[:text])

View file

@ -1,6 +1,37 @@
module Haml
# An exception raised by Haml code.
class Error < StandardError
MESSAGES = {
:illegal_nesting_header => "Illegal nesting: nesting within a header command is illegal.",
:illegal_nesting_plain => "Illegal nesting: nesting within plain text is illegal.",
:illegal_nesting_content => "Illegal nesting: nesting within a tag that already has content is illegal.",
:illegal_nesting_self_closing => "Illegal nesting: nesting within a self-closing tag is illegal.",
:invalid_tag => 'Invalid tag: "%%s".',
:illegal_element => "Illegal element: classes and ids must have values.",
:no_ruby_code => "There's no Ruby code for %s to evaluate.",
:self_closing_content => "Self-closing tags can't have content.",
:invalid_filter_name => 'Invalid filter name ":%s".',
:filter_not_defined => 'Filter "%s" is not defined.',
:indenting_at_start => "Indenting at the beginning of the document is illegal.",
:illegal_nesting_line => "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
:invalid_attribute_list => 'Invalid attribute list: %s.',
:unbalanced_brackets => 'Unbalanced brackets.',
:no_end => <<-END
You don't need to use "- end" in Haml. Un-indent to close a block:
- if foo?
%strong Foo!
- else
Not foo.
%p This line is un-indented, so it isn't part of the "if" block
END
}
def self.message(key, *args)
string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
(args.empty? ? string : string % args).rstrip
end
# The line of the template on which the error occurred.
#
# @return [Fixnum]

View file

@ -486,7 +486,7 @@ MESSAGE
if flags.include?(:/)
raise Error.new("Self-closing tags can't have content.") if text
raise Error.new("Illegal nesting: nesting within a self-closing tag is illegal.") if block
raise Error.new(Error.message(:illegal_nesting_self_closing)) if block
end
tag = "<#{name}#{attributes}>"
@ -506,7 +506,7 @@ MESSAGE
end
if text
raise Error.new("Illegal nesting: content can't be both given to haml_tag :#{name} and nested within it.")
raise Error.new(Error.message(:illegal_nesting_line, name))
end
if flags.include?(:<)

View file

@ -91,7 +91,7 @@ module Haml
@indentation = nil
@line = next_line
raise SyntaxError.new("Indenting at the beginning of the document is illegal.", @line.index) if @line.tabs != 0
raise SyntaxError.new(Error.message(:indenting_at_start), @line.index) if @line.tabs != 0
while next_line
process_indent(@line) unless @line.text.empty?
@ -238,7 +238,7 @@ END
def plain(text, escape_html = nil)
if block_opened?
raise SyntaxError.new("Illegal nesting: nesting within plain text is illegal.", @next_line.index)
raise SyntaxError.new(Error.message(:illegal_nesting_plain), @next_line.index)
end
unless contains_interpolation?(text)
@ -250,7 +250,7 @@ END
end
def script(text, escape_html = nil, preserve = false)
raise SyntaxError.new("There's no Ruby code for = to evaluate.") if text.empty?
raise SyntaxError.new(Error.message(:no_ruby_code, '=')) if text.empty?
text = handle_ruby_multiline(text)
escape_html = @options[:escape_html] if escape_html.nil?
@ -259,21 +259,14 @@ END
end
def flat_script(text, escape_html = nil)
raise SyntaxError.new("There's no Ruby code for ~ to evaluate.") if text.empty?
raise SyntaxError.new(Error.message(:no_ruby_code, '~')) if text.empty?
script(text, escape_html, :preserve)
end
def silent_script(text)
return haml_comment(text[2..-1]) if text[1] == SILENT_COMMENT
raise SyntaxError.new(<<END.rstrip, @index - 1) if text[1..-1].strip == "end"
You don't need to use "- end" in Haml. Un-indent to close a block:
- if foo?
%strong Foo!
- else
Not foo.
%p This line is un-indented, so it isn't part of the "if" block
END
raise SyntaxError.new(Error.message(:no_end), @index - 1) if text[1..-1].strip == "end"
text = handle_ruby_multiline(text)
keyword = block_keyword(text)
@ -346,12 +339,12 @@ END
attributes_list.compact!
raise SyntaxError.new("Illegal nesting: nesting within a self-closing tag is illegal.", @next_line.index) if block_opened? && self_closing
raise SyntaxError.new("There's no Ruby code for #{action} to evaluate.", last_line - 1) if parse && value.empty?
raise SyntaxError.new("Self-closing tags can't have content.", last_line - 1) if self_closing && !value.empty?
raise SyntaxError.new(Error.message(:illegal_nesting_self_closing), @next_line.index) if block_opened? && self_closing
raise SyntaxError.new(Error.message(:no_ruby_code, action), last_line - 1) if parse && value.empty?
raise SyntaxError.new(Error.message(:self_closing_content), last_line - 1) if self_closing && !value.empty?
if block_opened? && !value.empty? && !is_ruby_multiline?(value)
raise SyntaxError.new("Illegal nesting: content can't be both given on the same line as %#{tag_name} and nested within it.", @next_line.index)
raise SyntaxError.new(Error.message(:illegal_nesting_line, tag_name), @next_line.index)
end
self_closing ||= !!(!block_opened? && value.empty? && @options[:autoclose].any? {|t| t === tag_name})
@ -379,7 +372,7 @@ END
conditional << ">" if conditional
if block_opened? && !line.empty?
raise SyntaxError.new('Illegal nesting: nesting within a tag that already has content is illegal.', @next_line.index)
raise SyntaxError.new(Haml::Error.message(:illegal_nesting_content), @next_line.index)
end
ParseNode.new(:comment, @index, :conditional => conditional, :text => line)
@ -387,13 +380,13 @@ END
# Renders an XHTML doctype or XML shebang.
def doctype(line)
raise SyntaxError.new("Illegal nesting: nesting within a header command is illegal.", @next_line.index) if block_opened?
raise SyntaxError.new(Error.message(:illegal_nesting_header), @next_line.index) if block_opened?
version, type, encoding = line[3..-1].strip.downcase.scan(DOCTYPE_REGEX)[0]
ParseNode.new(:doctype, @index, :version => version, :type => type, :encoding => encoding)
end
def filter(name)
raise Error.new("Invalid filter name \":#{name}\".") unless name =~ /^\w+$/
raise Error.new(Error.message(:invalid_filter_name, name)) unless name =~ /^\w+$/
@filter_buffer = String.new
@ -473,10 +466,14 @@ END
# Parses a line into tag_name, attributes, attributes_hash, object_ref, action, value
def parse_tag(line)
raise SyntaxError.new("Invalid tag: \"#{line}\".") unless match = line.scan(/%([-:\w]+)([-:\w\.\#]*)(.*)/)[0]
match = line.scan(/%([-:\w]+)([-:\w\.\#]*)(.*)/)[0]
raise SyntaxError.new(Error.message(:invalid_tag, line)) unless match
tag_name, attributes, rest = match
raise SyntaxError.new("Illegal element: classes and ids must have values.") if attributes =~ /[\.#](\.|#|\z)/
if attributes =~ /[\.#](\.|#|\z)/
raise SyntaxError.new(Error.message(:illegal_element))
end
new_attributes_hash = old_attributes_hash = last_line = nil
object_ref = "nil"
@ -522,7 +519,7 @@ END
begin
attributes_hash, rest = balance(line, ?{, ?})
rescue SyntaxError => e
if line.strip[-1] == ?, && e.message == "Unbalanced brackets."
if line.strip[-1] == ?, && e.message == Error.message(:unbalanced_brackets)
line << "\n" << @next_line.text
last_line += 1
next_line
@ -549,7 +546,7 @@ END
if name == false
text = (Haml::Util.balance(line, ?(, ?)) || [line]).first
raise Haml::SyntaxError.new("Invalid attribute list: #{text.inspect}.", last_line - 1)
raise Haml::SyntaxError.new(Error.message(:invalid_attribute_list, text.inspect), last_line - 1)
end
attributes[name] = value
scanner.scan(/\s*/)
@ -697,7 +694,7 @@ END
def balance(*args)
res = Haml::Util.balance(*args)
return res if res
raise SyntaxError.new("Unbalanced brackets.")
raise SyntaxError.new(Error.message(:unbalanced_brackets))
end
def block_opened?

View file

@ -7,85 +7,77 @@ class EngineTest < MiniTest::Unit::TestCase
# if so, the second element should be the line number that should be reported for the error.
# If this isn't provided, the tests will assume the line number should be the last line of the document.
EXCEPTION_MAP = {
"!!!\n a" => "Illegal nesting: nesting within a header command is illegal.",
"a\n b" => "Illegal nesting: nesting within plain text is illegal.",
"/ a\n b" => "Illegal nesting: nesting within a tag that already has content is illegal.",
"% a" => 'Invalid tag: "% a".',
"%p a\n b" => "Illegal nesting: content can't be both given on the same line as %p and nested within it.",
"%p=" => "There's no Ruby code for = to evaluate.",
"%p~" => "There's no Ruby code for ~ to evaluate.",
"~" => "There's no Ruby code for ~ to evaluate.",
"=" => "There's no Ruby code for = to evaluate.",
"%p/\n a" => "Illegal nesting: nesting within a self-closing tag is illegal.",
":a\n b" => ['Filter "a" is not defined.', 1],
":a= b" => 'Invalid filter name ":a= b".',
"." => "Illegal element: classes and ids must have values.",
".#" => "Illegal element: classes and ids must have values.",
".{} a" => "Illegal element: classes and ids must have values.",
".() a" => "Illegal element: classes and ids must have values.",
".= a" => "Illegal element: classes and ids must have values.",
"%p..a" => "Illegal element: classes and ids must have values.",
"%a/ b" => "Self-closing tags can't have content.",
"%p{:a => 'b',\n:c => 'd'}/ e" => ["Self-closing tags can't have content.", 2],
"%p{:a => 'b',\n:c => 'd'}=" => ["There's no Ruby code for = to evaluate.", 2],
"%p.{:a => 'b',\n:c => 'd'} e" => ["Illegal element: classes and ids must have values.", 1],
"%p{:a => 'b',\n:c => 'd',\n:e => 'f'}\n%p/ a" => ["Self-closing tags can't have content.", 4],
"!!!\n a" => error(:illegal_nesting_header),
"a\n b" => error(:illegal_nesting_plain),
"/ a\n b" => error(:illegal_nesting_content),
"% a" => error(:invalid_tag, 'a'),
"%p a\n b" => error(:illegal_nesting_line, 'p'),
"%p=" => error(:no_ruby_code, '='),
"%p~" => error(:no_ruby_code, '~'),
"~" => error(:no_ruby_code, '~'),
"=" => error(:no_ruby_code, '='),
"%p/\n a" => error(:illegal_nesting_self_closing),
":a\n b" => [error(:filter_not_defined, 'a'), 1],
":a= b" => error(:invalid_filter_name, 'a= b'),
"." => error(:illegal_element),
".#" => error(:illegal_element),
".{} a" => error(:illegal_element),
".() a" => error(:illegal_element),
".= a" => error(:illegal_element),
"%p..a" => error(:illegal_element),
"%a/ b" => error(:self_closing_content),
" %p foo" => error(:indenting_at_start),
" %p foo" => error(:indenting_at_start),
"- end" => error(:no_end),
"%p{:a => 'b',\n:c => 'd'}/ e" => [error(:self_closing_content), 2],
"%p{:a => 'b',\n:c => 'd'}=" => [error(:no_ruby_code, '='), 2],
"%p.{:a => 'b',\n:c => 'd'} e" => [error(:illegal_element), 1],
"%p{:a => 'b',\n:c => 'd',\n:e => 'f'}\n%p/ a" => [error(:self_closing_content), 4],
"%p{:a => 'b',\n:c => 'd',\n:e => 'f'}\n- raise 'foo'" => ["foo", 4],
"%p{:a => 'b',\n:c => raise('foo'),\n:e => 'f'}" => ["foo", 2],
"%p{:a => 'b',\n:c => 'd',\n:e => raise('foo')}" => ["foo", 3],
" %p foo" => "Indenting at the beginning of the document is illegal.",
" %p foo" => "Indenting at the beginning of the document is illegal.",
"- end" => <<MESSAGE.rstrip,
You don't need to use "- end" in Haml. Un-indent to close a block:
- if foo?
%strong Foo!
- else
Not foo.
%p This line is un-indented, so it isn't part of the "if" block
MESSAGE
" \n\t\n %p foo" => ["Indenting at the beginning of the document is illegal.", 3],
"\n\n %p foo" => ["Indenting at the beginning of the document is illegal.", 3],
"%p\n foo\n foo" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 3],
"%p\n foo\n%p\n foo" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 4],
"%p\n\t\tfoo\n\tfoo" => ["Inconsistent indentation: 1 tab was used for indentation, but the rest of the document was indented using 2 tabs.", 3],
"%p\n foo\n foo" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 3],
"%p\n foo\n %p\n bar" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 4],
"%p\n :plain\n bar\n \t baz" => ['Inconsistent indentation: " \t " was used for indentation, but the rest of the document was indented using 2 spaces.', 4],
"%p\n foo\n%p\n bar" => ["The line was indented 2 levels deeper than the previous line.", 4],
"%p\n foo\n %p\n bar" => ["The line was indented 3 levels deeper than the previous line.", 4],
"%p\n \tfoo" => ["Indentation can't use both tabs and spaces.", 2],
"%p(" => "Invalid attribute list: \"(\".",
"%p(foo=\nbar)" => ["Invalid attribute list: \"(foo=\".", 1],
"%p(foo=)" => "Invalid attribute list: \"(foo=)\".",
"%p(foo 'bar')" => "Invalid attribute list: \"(foo 'bar')\".",
"%p(foo 'bar'\nbaz='bang')" => ["Invalid attribute list: \"(foo 'bar'\".", 1],
"%p(foo='bar'\nbaz 'bang'\nbip='bop')" => ["Invalid attribute list: \"(foo='bar' baz 'bang'\".", 2],
"%p{'foo' => 'bar' 'bar' => 'baz'}" => :compile,
"%p{:foo => }" => :compile,
"%p{=> 'bar'}" => :compile,
"%p{'foo => 'bar'}" => :compile,
"%p{:foo => 'bar}" => :compile,
"%p{:foo => 'bar\"}" => :compile,
"%p{:a => 'b',\n:c => raise('foo'),\n:e => 'f'}" => ["foo", 2],
"%p{:a => 'b',\n:c => 'd',\n:e => raise('foo')}" => ["foo", 3],
" \n\t\n %p foo" => [error(:indenting_at_start), 3],
"\n\n %p foo" => [error(:indenting_at_start), 3],
"%p\n foo\n foo" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 3],
"%p\n foo\n%p\n foo" => ["Inconsistent indentation: 1 space was used for indentation, but the rest of the document was indented using 2 spaces.", 4],
"%p\n\t\tfoo\n\tfoo" => ["Inconsistent indentation: 1 tab was used for indentation, but the rest of the document was indented using 2 tabs.", 3],
"%p\n foo\n foo" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 3],
"%p\n foo\n %p\n bar" => ["Inconsistent indentation: 3 spaces were used for indentation, but the rest of the document was indented using 2 spaces.", 4],
"%p\n :plain\n bar\n \t baz" => ['Inconsistent indentation: " \t " was used for indentation, but the rest of the document was indented using 2 spaces.', 4],
"%p\n foo\n%p\n bar" => ["The line was indented 2 levels deeper than the previous line.", 4],
"%p\n foo\n %p\n bar" => ["The line was indented 3 levels deeper than the previous line.", 4],
"%p\n \tfoo" => ["Indentation can't use both tabs and spaces.", 2],
"%p(" => error(:invalid_attribute_list, '"("'),
"%p(foo=)" => error(:invalid_attribute_list, '"(foo=)"'),
"%p(foo 'bar')" => error(:invalid_attribute_list, '"(foo \'bar\')"'),
"%p(foo=\nbar)" => [error(:invalid_attribute_list, '"(foo="'), 1],
"%p(foo 'bar'\nbaz='bang')" => [error(:invalid_attribute_list, '"(foo \'bar\'"'), 1],
"%p(foo='bar'\nbaz 'bang'\nbip='bop')" => [error(:invalid_attribute_list, '"(foo=\'bar\' baz \'bang\'"'), 2],
"%p{'foo' => 'bar' 'bar' => 'baz'}" => :compile,
"%p{:foo => }" => :compile,
"%p{=> 'bar'}" => :compile,
"%p{'foo => 'bar'}" => :compile,
"%p{:foo => 'bar}" => :compile,
"%p{:foo => 'bar\"}" => :compile,
# Regression tests
"- raise 'foo'\n\n\n\nbar" => ["foo", 1],
"= 'foo'\n-raise 'foo'" => ["foo", 2],
"\n\n\n- raise 'foo'" => ["foo", 4],
"%p foo |\n bar |\n baz |\nbop\n- raise 'foo'" => ["foo", 5],
"foo\n\n\n bar" => ["Illegal nesting: nesting within plain text is illegal.", 4],
"%p/\n\n bar" => ["Illegal nesting: nesting within a self-closing tag is illegal.", 3],
"%p foo\n\n bar" => ["Illegal nesting: content can't be both given on the same line as %p and nested within it.", 3],
"/ foo\n\n bar" => ["Illegal nesting: nesting within a tag that already has content is illegal.", 3],
"!!!\n\n bar" => ["Illegal nesting: nesting within a header command is illegal.", 3],
"foo\n:ruby\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
"foo\n:erb\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
"foo\n:plain\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
"foo\n:plain\n 1\n 2\n 3\n4\n- raise 'foo'" => ["foo", 7],
"foo\n:plain\n 1\n 2\n 3\#{''}\n- raise 'foo'" => ["foo", 6],
"foo\n:plain\n 1\n 2\n 3\#{''}\n4\n- raise 'foo'" => ["foo", 7],
"foo\n:plain\n 1\n 2\n \#{raise 'foo'}" => ["foo", 5],
"= raise 'foo'\nfoo\nbar\nbaz\nbang" => ["foo", 1],
"- case 1\n\n- when 1\n - raise 'foo'" => ["foo", 4],
"foo\n\n\n bar" => [error(:illegal_nesting_plain), 4],
"%p/\n\n bar" => [error(:illegal_nesting_self_closing), 3],
"%p foo\n\n bar" => [error(:illegal_nesting_line, 'p'), 3],
"/ foo\n\n bar" => [error(:illegal_nesting_content), 3],
"!!!\n\n bar" => [error(:illegal_nesting_header), 3],
"- raise 'foo'\n\n\n\nbar" => ["foo", 1],
"= 'foo'\n-raise 'foo'" => ["foo", 2],
"\n\n\n- raise 'foo'" => ["foo", 4],
"%p foo |\n bar |\n baz |\nbop\n- raise 'foo'" => ["foo", 5],
"foo\n:ruby\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
"foo\n:erb\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
"foo\n:plain\n 1\n 2\n 3\n- raise 'foo'" => ["foo", 6],
"foo\n:plain\n 1\n 2\n 3\n4\n- raise 'foo'" => ["foo", 7],
"foo\n:plain\n 1\n 2\n 3\#{''}\n- raise 'foo'" => ["foo", 6],
"foo\n:plain\n 1\n 2\n 3\#{''}\n4\n- raise 'foo'" => ["foo", 7],
"foo\n:plain\n 1\n 2\n \#{raise 'foo'}" => ["foo", 5],
"= raise 'foo'\nfoo\nbar\nbaz\nbang" => ["foo", 1],
"- case 1\n\n- when 1\n - raise 'foo'" => ["foo", 4],
}
User = Struct.new('User', :id)
@ -1206,7 +1198,7 @@ HAML
def test_unbalanced_brackets
render('foo #{1 + 5} foo #{6 + 7 bar #{8 + 9}')
rescue Haml::SyntaxError => e
assert_equal("Unbalanced brackets.", e.message)
assert_equal(Haml::Error.message(:unbalanced_brackets), e.message)
end
def test_balanced_conditional_comments

View file

@ -72,6 +72,10 @@ class MiniTest::Unit::TestCase
flunk "Expected exception #{klass}, none raised"
end
def self.error(*args)
Haml::Error.message(*args)
end
end
$VERBOSE = true