slim 2.0.3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +7 -8
- data/.yardopts +1 -1
- data/CHANGES +23 -0
- data/Gemfile +2 -1
- data/LICENSE +1 -1
- data/README.jp.md +497 -268
- data/README.md +505 -278
- data/Rakefile +14 -2
- data/benchmarks/view.haml +2 -2
- data/doc/include.md +20 -0
- data/doc/jp/include.md +20 -0
- data/doc/jp/logic_less.md +137 -0
- data/doc/jp/smart.md +102 -0
- data/doc/jp/translator.md +28 -0
- data/doc/smart.md +102 -0
- data/lib/slim/command.rb +8 -2
- data/lib/slim/controls.rb +4 -3
- data/lib/slim/do_inserter.rb +1 -1
- data/lib/slim/embedded.rb +26 -22
- data/lib/slim/end_inserter.rb +1 -1
- data/lib/slim/engine.rb +3 -2
- data/lib/slim/filter.rb +2 -2
- data/lib/slim/grammar.rb +3 -1
- data/lib/slim/include.rb +54 -0
- data/lib/slim/interpolation.rb +1 -1
- data/lib/slim/parser.rb +61 -46
- data/lib/slim/smart.rb +8 -0
- data/lib/slim/smart/escaper.rb +42 -0
- data/lib/slim/smart/filter.rb +96 -0
- data/lib/slim/smart/parser.rb +33 -0
- data/lib/slim/splat/builder.rb +3 -3
- data/lib/slim/splat/filter.rb +2 -2
- data/lib/slim/translator.rb +2 -2
- data/lib/slim/version.rb +1 -1
- data/slim.gemspec +1 -1
- data/test/core/helper.rb +5 -3
- data/test/core/test_code_blocks.rb +11 -0
- data/test/core/test_code_escaping.rb +14 -4
- data/test/core/test_embedded_engines.rb +26 -0
- data/test/core/test_html_structure.rb +39 -0
- data/test/core/test_parser_errors.rb +8 -25
- data/test/core/test_pretty.rb +15 -0
- data/test/core/test_ruby_errors.rb +0 -10
- data/test/include/files/recursive.slim +1 -0
- data/test/include/files/slimfile.slim +3 -0
- data/test/include/files/subdir/test.slim +1 -0
- data/test/include/files/textfile +1 -0
- data/test/include/test_include.rb +23 -0
- data/test/literate/TESTS.md +17 -2
- data/test/rails/test/test_slim.rb +1 -1
- data/test/smart/test_smart_text.rb +300 -0
- data/test/translator/test_translator.rb +7 -6
- metadata +22 -4
data/lib/slim/smart.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'slim'
|
2
|
+
require 'slim/smart/filter'
|
3
|
+
require 'slim/smart/escaper'
|
4
|
+
require 'slim/smart/parser'
|
5
|
+
|
6
|
+
Slim::Engine.replace(Slim::Parser, Slim::Smart::Parser, :file, :tabsize, :shortcut, :default_tag, :attr_delims, :attr_list_delims, :code_attr_delims, :implicit_text)
|
7
|
+
Slim::Engine.after(Slim::Smart::Parser, Slim::Smart::Filter, :smart_text, :smart_text_end_chars, :smart_text_begin_chars)
|
8
|
+
Slim::Engine.after(Slim::Interpolation, Slim::Smart::Escaper, :smart_text_escaping)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Slim
|
2
|
+
module Smart
|
3
|
+
# Perform smart entity escaping in the
|
4
|
+
# expressions `[:slim, :text, type, Expression]`.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class Escaper < ::Slim::Filter
|
8
|
+
define_options :smart_text_escaping => true
|
9
|
+
|
10
|
+
def call(exp)
|
11
|
+
if options[:smart_text_escaping]
|
12
|
+
super
|
13
|
+
else
|
14
|
+
exp
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_slim_text(type, content)
|
19
|
+
[:escape, type != :verbatim, [:slim, :text, type, compile(content)]]
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_static(string)
|
23
|
+
# Prevent obvious &foo; and Ӓ and ÿ entities from escaping.
|
24
|
+
block = [:multi]
|
25
|
+
until string.empty?
|
26
|
+
case string
|
27
|
+
when /\A&(\w+|#x[0-9a-f]+|#\d+);/i
|
28
|
+
# Entity.
|
29
|
+
block << [:escape, false, [:static, $&]]
|
30
|
+
string = $'
|
31
|
+
when /\A&?[^&]*/
|
32
|
+
# Other text.
|
33
|
+
block << [:static, $&]
|
34
|
+
string = $'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
block
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Slim
|
2
|
+
module Smart
|
3
|
+
# Perform newline processing in the
|
4
|
+
# expressions `[:slim, :text, type, Expression]`.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class Filter < ::Slim::Filter
|
8
|
+
define_options :smart_text => true,
|
9
|
+
:smart_text_end_chars => '([{',
|
10
|
+
:smart_text_begin_chars => ',.;:!?)]}'
|
11
|
+
|
12
|
+
def initialize(opts = {})
|
13
|
+
super
|
14
|
+
@active = @prepend = @append = false
|
15
|
+
@prepend_re = /\A#{chars_re(options[:smart_text_begin_chars])}/
|
16
|
+
@append_re = /#{chars_re(options[:smart_text_end_chars])}\Z/
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(exp)
|
20
|
+
if options[:smart_text]
|
21
|
+
super
|
22
|
+
else
|
23
|
+
exp
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_multi(*exps)
|
28
|
+
# The [:multi] blocks serve two purposes.
|
29
|
+
# On outer level, they collect the building blocks like
|
30
|
+
# tags, verbatim text, and implicit/explicit text.
|
31
|
+
# Within a text block, they collect the individual
|
32
|
+
# lines in [:slim, :interpolate, string] blocks.
|
33
|
+
#
|
34
|
+
# Our goal here is to decide when we want to prepend and
|
35
|
+
# append newlines to those individual interpolated lines.
|
36
|
+
# We basically want the text to come out as it was originally entered,
|
37
|
+
# while removing newlines next to the enclosing tags.
|
38
|
+
#
|
39
|
+
# On outer level, we choose to prepend every time, except
|
40
|
+
# right after the opening tag or after other text block.
|
41
|
+
# We also use the append flag to recognize the last expression
|
42
|
+
# before the closing tag, as we don't want to append newline there.
|
43
|
+
#
|
44
|
+
# Within text block, we prepend only before the first line unless
|
45
|
+
# the outer level tells us not to, and we append only after the last line,
|
46
|
+
# unless the outer level tells us it is the last line before the closing tag.
|
47
|
+
# Of course, this is later subject to the special begin/end characters
|
48
|
+
# which may further suppress the newline at the corresponding line boundary.
|
49
|
+
# Also note that the lines themselves are already correctly separated by newlines,
|
50
|
+
# so we don't have to worry about that at all.
|
51
|
+
block = [:multi]
|
52
|
+
prev = nil
|
53
|
+
last_exp = exps.reject{ |exp| exp.first == :newline }.last unless @active && @append
|
54
|
+
exps.each do |exp|
|
55
|
+
@append = exp.equal?(last_exp)
|
56
|
+
if @active
|
57
|
+
@prepend = false if prev
|
58
|
+
else
|
59
|
+
@prepend = prev && ( prev.first != :slim || prev[1] != :text )
|
60
|
+
end
|
61
|
+
block << compile(exp)
|
62
|
+
prev = exp unless exp.first == :newline
|
63
|
+
end
|
64
|
+
block
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_slim_text(type, content)
|
68
|
+
@active = type != :verbatim
|
69
|
+
[:slim, :text, type, compile(content)]
|
70
|
+
ensure
|
71
|
+
@active = false
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_slim_text_inline(content)
|
75
|
+
# Inline text is not wrapped in multi block, so set it up as if it was.
|
76
|
+
@prepend = false
|
77
|
+
@append = true
|
78
|
+
on_slim_text(:inline, content)
|
79
|
+
end
|
80
|
+
|
81
|
+
def on_slim_interpolate(string)
|
82
|
+
if @active
|
83
|
+
string = "\n" + string if @prepend && string !~ @prepend_re
|
84
|
+
string += "\n" if @append && string !~ @append_re
|
85
|
+
end
|
86
|
+
[:slim, :interpolate, string]
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def chars_re(string)
|
92
|
+
Regexp.union(string.split(//))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Slim
|
2
|
+
module Smart
|
3
|
+
class Parser < ::Slim::Parser
|
4
|
+
define_options :implicit_text => true
|
5
|
+
|
6
|
+
def initialize(opts = {})
|
7
|
+
super
|
8
|
+
word_re = options[:implicit_text] ? LC_WORD_RE : WORD_RE
|
9
|
+
attr_keys = Regexp.union(@attr_shortcut.keys.sort_by {|k| -k.size } )
|
10
|
+
@attr_shortcut_re = /\A(#{attr_keys}+)((?:#{WORD_RE}|-)*)/
|
11
|
+
tag_keys = Regexp.union((@tag_shortcut.keys - @attr_shortcut.keys).sort_by {|k| -k.size } )
|
12
|
+
@tag_re = /\A(?:#{attr_keys}(?=-*#{WORD_RE})|#{tag_keys}|\*(?=[^\s]+)|(#{word_re}(?:#{word_re}|:|-)*#{word_re}|#{word_re}+))/
|
13
|
+
end
|
14
|
+
|
15
|
+
def unknown_line_indicator
|
16
|
+
if @line =~ /\A>( ?)/
|
17
|
+
# Found explicit text block.
|
18
|
+
@stacks.last << [:slim, :text, :explicit, parse_text_block($', @indents.last + $1.size + 1)]
|
19
|
+
else
|
20
|
+
unless options[:implicit_text]
|
21
|
+
syntax_error! 'Illegal shortcut' if @line =~ @attr_shortcut_re
|
22
|
+
super
|
23
|
+
end
|
24
|
+
# Found implicit smart text block.
|
25
|
+
if line = @lines.first
|
26
|
+
indent = ( line =~ /\A\s*\Z/ ? @indents.last + 1 : get_indent(line) )
|
27
|
+
end
|
28
|
+
@stacks.last << [:slim, :text, :implicit, parse_text_block(@line, indent)]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/slim/splat/builder.rb
CHANGED
@@ -10,11 +10,11 @@ module Slim
|
|
10
10
|
def code_attr(name, escape, value)
|
11
11
|
if delim = @options[:merge_attrs][name]
|
12
12
|
value = Array === value ? value.join(delim) : value.to_s
|
13
|
-
attr(name, escape ? Temple::Utils.
|
13
|
+
attr(name, escape ? Temple::Utils.escape_html_safe(value, @options[:use_html_safe]) : value) unless value.empty?
|
14
14
|
elsif @options[:hyphen_attrs].include?(name) && Hash === value
|
15
15
|
hyphen_attr(name, escape, value)
|
16
16
|
elsif value != false && value != nil
|
17
|
-
attr(name, value != true && escape ? Temple::Utils.
|
17
|
+
attr(name, value != true && escape ? Temple::Utils.escape_html_safe(value, @options[:use_html_safe]) : value)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -69,7 +69,7 @@ module Slim
|
|
69
69
|
hyphen_attr("#{name}-#{n.to_s.gsub('_', '-')}", escape, v)
|
70
70
|
end
|
71
71
|
else
|
72
|
-
attr(name, value != true && escape ? Temple::Utils.
|
72
|
+
attr(name, value != true && escape ? Temple::Utils.escape_html_safe(value, @options[:use_html_safe]) : value)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
data/lib/slim/splat/filter.rb
CHANGED
@@ -2,9 +2,9 @@ module Slim
|
|
2
2
|
module Splat
|
3
3
|
# @api private
|
4
4
|
class Filter < ::Slim::Filter
|
5
|
-
OPTIONS = [:merge_attrs, :attr_quote, :sort_attrs, :default_tag, :hyphen_attrs, :format]
|
5
|
+
OPTIONS = [:merge_attrs, :attr_quote, :sort_attrs, :default_tag, :hyphen_attrs, :format, :use_html_safe]
|
6
6
|
define_options OPTIONS
|
7
|
-
|
7
|
+
set_default_options :hyphen_attrs => %w(data aria), :use_html_safe => ''.respond_to?(:html_safe?)
|
8
8
|
|
9
9
|
def call(exp)
|
10
10
|
@splat_options = nil
|
data/lib/slim/translator.rb
CHANGED
data/lib/slim/version.rb
CHANGED
data/slim.gemspec
CHANGED
@@ -18,6 +18,6 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = %w(lib)
|
20
20
|
|
21
|
-
s.add_runtime_dependency('temple', ['~> 0.6.
|
21
|
+
s.add_runtime_dependency('temple', ['~> 0.6.9'])
|
22
22
|
s.add_runtime_dependency('tilt', ['>= 1.3.3', '< 2.1'])
|
23
23
|
end
|
data/test/core/helper.rb
CHANGED
@@ -46,6 +46,8 @@ class TestSlim < MiniTest::Unit::TestCase
|
|
46
46
|
raise 'Syntax error expected'
|
47
47
|
rescue Slim::Parser::SyntaxError => ex
|
48
48
|
assert_equal message, ex.message
|
49
|
+
message =~ /([^\s]+), Line (\d+)/
|
50
|
+
assert_backtrace ex, "#{$1}:#{$2}"
|
49
51
|
end
|
50
52
|
|
51
53
|
def assert_ruby_error(error, from, source, options = {})
|
@@ -58,12 +60,12 @@ class TestSlim < MiniTest::Unit::TestCase
|
|
58
60
|
def assert_backtrace(ex, from)
|
59
61
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
60
62
|
# HACK: Rubinius stack trace sometimes has one entry more
|
61
|
-
if ex.backtrace[0] !~ /^#{Regexp.escape from}
|
62
|
-
ex.backtrace[1] =~ /([^\s]+:\d+)
|
63
|
+
if ex.backtrace[0] !~ /^#{Regexp.escape from}/
|
64
|
+
ex.backtrace[1] =~ /([^\s]+:\d+)/
|
63
65
|
assert_equal from, $1
|
64
66
|
end
|
65
67
|
else
|
66
|
-
ex.backtrace[0] =~ /([^\s]+:\d+)
|
68
|
+
ex.backtrace[0] =~ /([^\s]+:\d+)/
|
67
69
|
assert_equal from, $1
|
68
70
|
end
|
69
71
|
end
|
@@ -21,6 +21,17 @@ p
|
|
21
21
|
assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
|
22
22
|
end
|
23
23
|
|
24
|
+
def test_render_variable_ending_with_do
|
25
|
+
source = %q{
|
26
|
+
- appelido=10
|
27
|
+
p= appelido
|
28
|
+
- appelido
|
29
|
+
}
|
30
|
+
|
31
|
+
assert_html '<p>10</p>', source
|
32
|
+
end
|
33
|
+
|
34
|
+
|
24
35
|
def test_render_with_output_code_within_block
|
25
36
|
source = %q{
|
26
37
|
p
|
@@ -45,10 +45,9 @@ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>.".html_safe
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
# splat ignores html_safe? for now
|
49
48
|
def test_render_splat_with_html_safe_true
|
50
49
|
source = %q{
|
51
|
-
p *{ :title => '&'.html_safe }
|
50
|
+
p *{ :title => '&'.html_safe }
|
52
51
|
}
|
53
52
|
|
54
53
|
with_html_safe do
|
@@ -56,13 +55,24 @@ p *{ :title => '&'.html_safe }
|
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
58
|
+
def test_render_splat_with_html_safe_false
|
59
|
+
source = %q{
|
60
|
+
p *{ :title => '&' }
|
61
|
+
}
|
62
|
+
|
63
|
+
with_html_safe do
|
64
|
+
assert_html "<p title=\"&\"></p>", source, :use_html_safe => true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
59
69
|
def test_render_attribute_with_html_safe_true
|
60
70
|
source = %q{
|
61
|
-
p title=('&'.html_safe)
|
71
|
+
p title=('&'.html_safe)
|
62
72
|
}
|
63
73
|
|
64
74
|
with_html_safe do
|
65
|
-
assert_html "<p title=\"
|
75
|
+
assert_html "<p title=\"&\"></p>", source, :use_html_safe => true
|
66
76
|
end
|
67
77
|
end
|
68
78
|
|
@@ -69,6 +69,17 @@ creole:
|
|
69
69
|
assert_html "<h1>head1</h1><h2>head2</h2>", source
|
70
70
|
end
|
71
71
|
|
72
|
+
def test_render_with_creole_one_line
|
73
|
+
source = %q{
|
74
|
+
creole: Hello **world**,
|
75
|
+
we can write one-line embedded markup now!
|
76
|
+
= Headline
|
77
|
+
Text
|
78
|
+
.nested: creole: **Strong**
|
79
|
+
}
|
80
|
+
assert_html '<p>Hello <strong>world</strong>, we can write one-line embedded markup now!</p><h1>Headline</h1><p>Text</p><div class="nested"><p><strong>Strong</strong></p></div>', source
|
81
|
+
end
|
82
|
+
|
72
83
|
def test_render_with_org
|
73
84
|
# HACK: org-ruby registers itself in Tilt
|
74
85
|
require 'org-ruby'
|
@@ -113,6 +124,21 @@ p Hi
|
|
113
124
|
assert_html %{<script type="text/javascript">$(function() {});\n\n\nalert('hello')</script><p>Hi</p>}, source
|
114
125
|
end
|
115
126
|
|
127
|
+
def test_render_with_opal
|
128
|
+
begin
|
129
|
+
# HACK: org-ruby registers itself in Tilt
|
130
|
+
require 'opal'
|
131
|
+
rescue LoadError
|
132
|
+
return
|
133
|
+
end
|
134
|
+
|
135
|
+
source = %q{
|
136
|
+
opal:
|
137
|
+
puts 'hello from opal'
|
138
|
+
}
|
139
|
+
assert_match '$puts("hello from opal")', render(source)
|
140
|
+
end
|
141
|
+
|
116
142
|
def test_render_with_javascript_with_tabs
|
117
143
|
source = "javascript:\n\t$(function() {});\n\talert('hello')\np Hi"
|
118
144
|
assert_html "<script type=\"text/javascript\">$(function() {});\nalert('hello')</script><p>Hi</p>", source
|
@@ -14,6 +14,15 @@ html
|
|
14
14
|
assert_html '<html><head><title>Simple Test Title</title></head><body><p>Hello World, meet Slim.</p></body></html>', source
|
15
15
|
end
|
16
16
|
|
17
|
+
def test_relaxed_indentation_of_first_line
|
18
|
+
source = %q{
|
19
|
+
p
|
20
|
+
.content
|
21
|
+
}
|
22
|
+
|
23
|
+
assert_html "<p><div class=\"content\"></div></p>", source
|
24
|
+
end
|
25
|
+
|
17
26
|
def test_html_tag_with_text_and_empty_line
|
18
27
|
source = %q{
|
19
28
|
p Hello
|
@@ -209,6 +218,17 @@ p#test class="paragraph" This is line one.
|
|
209
218
|
assert_html "<p class=\"paragraph\" id=\"test\">This is line one.\nThis is line two.</p>", source
|
210
219
|
end
|
211
220
|
|
221
|
+
def test_relaxed_text_indentation
|
222
|
+
source = %q{
|
223
|
+
p
|
224
|
+
| text block
|
225
|
+
text
|
226
|
+
line3
|
227
|
+
}
|
228
|
+
|
229
|
+
assert_html "<p>text block\ntext\n line3</p>", source
|
230
|
+
end
|
231
|
+
|
212
232
|
def test_output_code_with_leading_spaces
|
213
233
|
source = %q{
|
214
234
|
p= hello_world
|
@@ -311,6 +331,25 @@ closed/
|
|
311
331
|
assert_html '<closed />', source, :format => :xhtml
|
312
332
|
end
|
313
333
|
|
334
|
+
def test_custom_attr_list_delims_option
|
335
|
+
source = %q{
|
336
|
+
p { foo="bar" x=(1+1) }
|
337
|
+
p < x=(1+1) > Hello
|
338
|
+
}
|
339
|
+
|
340
|
+
assert_html '<p foo="bar" x="2"></p><p>< x=(1+1) > Hello</p>', source
|
341
|
+
assert_html '<p foo="bar" x="2"></p><p>< x=(1+1) > Hello</p>', source, :attr_list_delims => {'{' => '}'}
|
342
|
+
assert_html '<p>{ foo="bar" x=(1+1) }</p><p x="2">Hello</p>', source, :attr_list_delims => {'<' => '>'}, :code_attr_delims => { '(' => ')' }
|
343
|
+
end
|
344
|
+
|
345
|
+
def test_closed_tag
|
346
|
+
source = %q{
|
347
|
+
closed/
|
348
|
+
}
|
349
|
+
|
350
|
+
assert_html '<closed />', source, :format => :xhtml
|
351
|
+
end
|
352
|
+
|
314
353
|
def test_attributs_with_parens_and_spaces
|
315
354
|
source = %q{label{ for='filter' }= hello_world}
|
316
355
|
assert_html '<label for="filter">Hello World from @env</label>', source
|
@@ -19,40 +19,23 @@ doctype 5
|
|
19
19
|
assert_syntax_error "Unexpected indentation\n (__TEMPLATE__), Line 3, Column 2\n div Invalid\n ^\n", source
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
22
|
+
def test_malformed_indentation
|
23
23
|
source = %q{
|
24
24
|
p
|
25
|
-
|
26
|
-
|
27
|
-
}
|
28
|
-
|
29
|
-
assert_syntax_error "Text line not indented deep enough.\nThe first text line defines the necessary text indentation.\n (__TEMPLATE__), Line 4, Column 3\n text\n ^\n", source
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_unexpected_text_indentation_in_tag
|
33
|
-
source = %q{
|
34
|
-
ul
|
35
|
-
li List1
|
36
|
-
ul
|
37
|
-
li a
|
38
|
-
li b
|
39
|
-
li List2
|
40
|
-
ul
|
41
|
-
li a
|
42
|
-
li b
|
25
|
+
div Valid
|
26
|
+
div Invalid
|
43
27
|
}
|
44
28
|
|
45
|
-
assert_syntax_error "
|
29
|
+
assert_syntax_error "Malformed indentation\n (__TEMPLATE__), Line 4, Column 1\n div Invalid\n ^\n", source
|
46
30
|
end
|
47
31
|
|
48
|
-
def
|
32
|
+
def test_malformed_indentation2
|
49
33
|
source = %q{
|
50
|
-
p
|
51
34
|
div Valid
|
52
35
|
div Invalid
|
53
36
|
}
|
54
37
|
|
55
|
-
assert_syntax_error "Malformed indentation\n (__TEMPLATE__), Line
|
38
|
+
assert_syntax_error "Malformed indentation\n (__TEMPLATE__), Line 3, Column 1\n div Invalid\n ^\n", source
|
56
39
|
end
|
57
40
|
|
58
41
|
def test_unknown_line_indicator
|
@@ -76,13 +59,13 @@ p
|
|
76
59
|
assert_syntax_error "Expected closing delimiter )\n (__TEMPLATE__), Line 3, Column 33\n img(src=\"img.jpg\" title={title}\n ^\n", source
|
77
60
|
end
|
78
61
|
|
79
|
-
def
|
62
|
+
def test_missing_quote_unexpected_end
|
80
63
|
source = %q{
|
81
64
|
p
|
82
65
|
img(src="img.jpg
|
83
66
|
}
|
84
67
|
|
85
|
-
|
68
|
+
assert_syntax_error "Unexpected end of file\n (__TEMPLATE__), Line 3, Column 0\n \n ^\n", source
|
86
69
|
end
|
87
70
|
|
88
71
|
def test_expected_closing_attribute_delimiter
|