fast_haml 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.travis.yml +25 -0
- data/Appraisals +26 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +96 -0
- data/Rakefile +11 -0
- data/benchmark/rendering.rb +29 -0
- data/bin/fast_haml +4 -0
- data/ext/attribute_builder/attribute_builder.c +259 -0
- data/ext/attribute_builder/extconf.rb +3 -0
- data/fast_haml.gemspec +35 -0
- data/gemfiles/rails_4.0.gemfile +9 -0
- data/gemfiles/rails_4.1.gemfile +9 -0
- data/gemfiles/rails_4.2.gemfile +9 -0
- data/gemfiles/rails_edge.gemfile +10 -0
- data/haml_spec_test.rb +22 -0
- data/lib/fast_haml/ast.rb +112 -0
- data/lib/fast_haml/cli.rb +38 -0
- data/lib/fast_haml/compiler.rb +325 -0
- data/lib/fast_haml/element_parser.rb +288 -0
- data/lib/fast_haml/engine.rb +32 -0
- data/lib/fast_haml/filter_compilers/base.rb +29 -0
- data/lib/fast_haml/filter_compilers/cdata.rb +15 -0
- data/lib/fast_haml/filter_compilers/css.rb +15 -0
- data/lib/fast_haml/filter_compilers/escaped.rb +22 -0
- data/lib/fast_haml/filter_compilers/javascript.rb +15 -0
- data/lib/fast_haml/filter_compilers/plain.rb +17 -0
- data/lib/fast_haml/filter_compilers/preserve.rb +30 -0
- data/lib/fast_haml/filter_compilers/ruby.rb +13 -0
- data/lib/fast_haml/filter_compilers.rb +37 -0
- data/lib/fast_haml/filter_parser.rb +54 -0
- data/lib/fast_haml/html.rb +44 -0
- data/lib/fast_haml/indent_tracker.rb +84 -0
- data/lib/fast_haml/line_parser.rb +66 -0
- data/lib/fast_haml/parser.rb +251 -0
- data/lib/fast_haml/parser_utils.rb +17 -0
- data/lib/fast_haml/rails_handler.rb +10 -0
- data/lib/fast_haml/railtie.rb +8 -0
- data/lib/fast_haml/ruby_multiline.rb +23 -0
- data/lib/fast_haml/static_hash_parser.rb +113 -0
- data/lib/fast_haml/syntax_error.rb +10 -0
- data/lib/fast_haml/text_compiler.rb +69 -0
- data/lib/fast_haml/tilt.rb +16 -0
- data/lib/fast_haml/version.rb +3 -0
- data/lib/fast_haml.rb +10 -0
- data/spec/rails/Rakefile +6 -0
- data/spec/rails/app/assets/images/.keep +0 -0
- data/spec/rails/app/assets/javascripts/application.js +13 -0
- data/spec/rails/app/assets/stylesheets/application.css +15 -0
- data/spec/rails/app/controllers/application_controller.rb +5 -0
- data/spec/rails/app/controllers/books_controller.rb +8 -0
- data/spec/rails/app/controllers/concerns/.keep +0 -0
- data/spec/rails/app/helpers/application_helper.rb +2 -0
- data/spec/rails/app/mailers/.keep +0 -0
- data/spec/rails/app/models/.keep +0 -0
- data/spec/rails/app/models/book.rb +9 -0
- data/spec/rails/app/models/concerns/.keep +0 -0
- data/spec/rails/app/views/books/hello.html.haml +2 -0
- data/spec/rails/app/views/books/with_capture.html.haml +4 -0
- data/spec/rails/app/views/books/with_variables.html.haml +4 -0
- data/spec/rails/app/views/layouts/application.html.haml +9 -0
- data/spec/rails/bin/bundle +3 -0
- data/spec/rails/bin/rails +4 -0
- data/spec/rails/bin/rake +4 -0
- data/spec/rails/bin/setup +29 -0
- data/spec/rails/config/application.rb +12 -0
- data/spec/rails/config/boot.rb +3 -0
- data/spec/rails/config/database.yml +25 -0
- data/spec/rails/config/environment.rb +5 -0
- data/spec/rails/config/environments/development.rb +41 -0
- data/spec/rails/config/environments/production.rb +79 -0
- data/spec/rails/config/environments/test.rb +42 -0
- data/spec/rails/config/initializers/assets.rb +11 -0
- data/spec/rails/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails/config/initializers/cookies_serializer.rb +3 -0
- data/spec/rails/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails/config/initializers/inflections.rb +16 -0
- data/spec/rails/config/initializers/mime_types.rb +4 -0
- data/spec/rails/config/initializers/secret_key_base.rb +6 -0
- data/spec/rails/config/initializers/session_store.rb +3 -0
- data/spec/rails/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails/config/locales/en.yml +23 -0
- data/spec/rails/config/routes.rb +7 -0
- data/spec/rails/config/secrets.yml +22 -0
- data/spec/rails/config.ru +4 -0
- data/spec/rails/db/seeds.rb +7 -0
- data/spec/rails/lib/assets/.keep +0 -0
- data/spec/rails/lib/tasks/.keep +0 -0
- data/spec/rails/log/.keep +0 -0
- data/spec/rails/public/404.html +67 -0
- data/spec/rails/public/422.html +67 -0
- data/spec/rails/public/500.html +66 -0
- data/spec/rails/public/favicon.ico +0 -0
- data/spec/rails/public/robots.txt +5 -0
- data/spec/rails/spec/requests/fast_haml_spec.rb +41 -0
- data/spec/rails/vendor/assets/javascripts/.keep +0 -0
- data/spec/rails/vendor/assets/stylesheets/.keep +0 -0
- data/spec/rails_helper.rb +4 -0
- data/spec/render/attribute_spec.rb +209 -0
- data/spec/render/comment_spec.rb +61 -0
- data/spec/render/doctype_spec.rb +62 -0
- data/spec/render/element_spec.rb +165 -0
- data/spec/render/filters/cdata_spec.rb +12 -0
- data/spec/render/filters/css_spec.rb +45 -0
- data/spec/render/filters/escaped_spec.rb +14 -0
- data/spec/render/filters/javascript_spec.rb +44 -0
- data/spec/render/filters/plain_spec.rb +24 -0
- data/spec/render/filters/preserve_spec.rb +25 -0
- data/spec/render/filters/ruby_spec.rb +13 -0
- data/spec/render/filters_spec.rb +11 -0
- data/spec/render/haml_comment_spec.rb +24 -0
- data/spec/render/multiline_spec.rb +39 -0
- data/spec/render/plain_spec.rb +20 -0
- data/spec/render/preserve_spec.rb +8 -0
- data/spec/render/sanitize_spec.rb +36 -0
- data/spec/render/script_spec.rb +74 -0
- data/spec/render/silent_script_spec.rb +97 -0
- data/spec/render/unescape_spec.rb +40 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/tilt_spec.rb +33 -0
- metadata +427 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'rails_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'FastHaml with Rails', type: :request do
|
|
4
|
+
it 'renders views' do
|
|
5
|
+
get '/books/hello'
|
|
6
|
+
expect(response).to be_ok
|
|
7
|
+
expect(response).to render_template('books/hello')
|
|
8
|
+
expect(response).to render_template('layouts/application')
|
|
9
|
+
expect(response.body).to include('<span>Hello, World</span>')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'renders views with variables' do
|
|
13
|
+
get '/books/with_variables?title=nanika'
|
|
14
|
+
expect(response).to be_ok
|
|
15
|
+
expect(response).to render_template('books/with_variables')
|
|
16
|
+
expect(response).to render_template('layouts/application')
|
|
17
|
+
expect(response.body).to include('<p>nanika</p>')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'escapes non-html_safe string' do
|
|
21
|
+
uri = URI.parse('/books/with_variables')
|
|
22
|
+
uri.query = URI.encode_www_form(title: '<script>alert(1)</script>')
|
|
23
|
+
get uri.to_s
|
|
24
|
+
expect(response).to be_ok
|
|
25
|
+
expect(response).to render_template('books/with_variables')
|
|
26
|
+
expect(response).to render_template('layouts/application')
|
|
27
|
+
expect(response.body).to include('<a href="/books/hello">hello</a>')
|
|
28
|
+
expect(response.body).to include('<p><script>alert(1)</script></p>')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'does not escape object which returns html_safe string by to_s' do
|
|
32
|
+
get '/books/with_variables?title=nanika'
|
|
33
|
+
expect(response.body).to include('<span>nanika</span>')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'works with capture method' do
|
|
37
|
+
get '/books/with_capture'
|
|
38
|
+
expect(response).to be_ok
|
|
39
|
+
expect(response.body).to include("<div><div>\n<p>Hello</p>\n</div>\n</div>\n")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'Attributes rendering', type: :render do
|
|
4
|
+
it 'parses attributes' do
|
|
5
|
+
expect(render_string('%span{class: "x"} hello')).to eq(%Q{<span class='x'>hello</span>\n})
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'parses attributes' do
|
|
9
|
+
expect(render_string('%span{class: "x", "old" => 2} hello')).to eq(%Q{<span class='x' old='2'>hello</span>\n})
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'is not element with id attribute' do
|
|
13
|
+
expect(render_string('#{1 + 2}')).to eq("3\n")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'renders dynamic attributes' do
|
|
17
|
+
expect(render_string(%q|%span#main{class: "na#{'ni'}ka"} hello|)).to eq(%Q{<span class='nanika' id='main'>hello</span>\n})
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'renders array class' do
|
|
21
|
+
expect(render_string('%span.c2{class: "c1"}')).to eq("<span class='c1 c2'></span>\n")
|
|
22
|
+
expect(render_string('%span.c2{class: ["c1", "c3"]}')).to eq("<span class='c1 c2 c3'></span>\n")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'merges classes' do
|
|
26
|
+
expect(render_string(<<HAML)).to eq("<span class='c1 c2 content {}' id='main_id1_id3_id2'>hello</span>\n")
|
|
27
|
+
- h1 = {class: 'c1', id: ['id1', 'id3']}
|
|
28
|
+
- h2 = {class: [{}, 'c2'], id: 'id2'}
|
|
29
|
+
%span#main.content{h1, h2} hello
|
|
30
|
+
HAML
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'escapes' do
|
|
34
|
+
expect(render_string(%q|%span{class: "x\"y'z"} hello|)).to eq(%Q{<span class='x"y'z'>hello</span>\n})
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "doesn't parse extra brace" do
|
|
38
|
+
expect(render_string('%span{foo: 1}{bar: 2}')).to eq("<span foo='1'>{bar: 2}</span>\n")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'renders only name if value is true' do
|
|
42
|
+
expect(render_string(%q|%span{foo: true, bar: 1} hello|)).to eq(%Q{<span bar='1' foo>hello</span>\n})
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context 'with xhtml format' do
|
|
46
|
+
it 'renders name="name" if value is true' do
|
|
47
|
+
expect(render_string(%q|%span{foo: true, bar: 1} hello|, format: :xhtml)).to eq(%Q{<span bar='1' foo='foo'>hello</span>\n})
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'renders nested attributes' do
|
|
52
|
+
expect(render_string(%q|%span{foo: {bar: 1+2}} hello|)).to eq(%Q|<span foo='{:bar=>3}'>hello</span>\n|)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'renders code attributes' do
|
|
56
|
+
expect(render_string(<<HAML)).to eq(%Q|<span bar='{:hoge=>:fuga}' baz foo='1'>hello</span>\n|)
|
|
57
|
+
- attrs = { foo: 1, bar: { hoge: :fuga }, baz: true }
|
|
58
|
+
%span{attrs} hello
|
|
59
|
+
HAML
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'renders dstr attributes' do
|
|
63
|
+
expect(render_string(<<HAML)).to eq(%Q|<span data='x{:foo=>1}y'>hello</span>\n|)
|
|
64
|
+
- data = { foo: 1 }
|
|
65
|
+
%span{data: "x\#{data}y"} hello
|
|
66
|
+
HAML
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'renders nested dstr attributes' do
|
|
70
|
+
expect(render_string(<<'HAML')).to eq(%Q|<span foo='{:bar=>"x1y"}'>hello</span>\n|)
|
|
71
|
+
- data = { foo: 1 }
|
|
72
|
+
%span{foo: {bar: "x#{1}y"}} hello
|
|
73
|
+
HAML
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'optimize send case' do
|
|
77
|
+
expect(render_string('%span{foo: {bar: 1+2}} hello')).to eq("<span foo='{:bar=>3}'>hello</span>\n")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'merges static id' do
|
|
81
|
+
expect(render_string('#foo{id: "bar"} baz')).to eq("<div id='foo_bar'>baz</div>\n")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'merges static class' do
|
|
85
|
+
expect(render_string('.foo{class: "bar"} baz')).to eq("<div class='bar foo'>baz</div>\n")
|
|
86
|
+
expect(render_string(<<'HAML')).to eq("<div class='bar foo'>baz</div>\n")
|
|
87
|
+
- bar = 'bar'
|
|
88
|
+
.foo{class: "#{bar}"} baz
|
|
89
|
+
HAML
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'converts underscore to hyphen in data attributes' do
|
|
93
|
+
expect(render_string("%span{data: {foo_bar: 'baz'}}")).to eq("<span data-foo-bar='baz'></span>\n")
|
|
94
|
+
expect(render_string("- h = {foo_bar: 'baz'}\n%span{data: h}")).to eq("<span data-foo-bar='baz'></span>\n")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context 'with unmatched brace' do
|
|
98
|
+
it 'raises error' do
|
|
99
|
+
expect { render_string('%span{foo hello') }.to raise_error(FastHaml::SyntaxError)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'tries to parse next lines' do
|
|
103
|
+
expect(render_string(<<HAML)).to eq("<span bar='2' foo='1'>hello</span>\n")
|
|
104
|
+
%span{foo: 1,
|
|
105
|
+
bar: 2} hello
|
|
106
|
+
HAML
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "doesn't try to parse next lines without trailing comma" do
|
|
110
|
+
expect { render_string(<<HAML) }.to raise_error(FastHaml::SyntaxError)
|
|
111
|
+
%span{foo: 1
|
|
112
|
+
, bar: 2} hello
|
|
113
|
+
HAML
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context 'with data attributes' do
|
|
118
|
+
it 'renders nested attributes' do
|
|
119
|
+
expect(render_string(%q|%span{data: {foo: 1, bar: 'baz', :hoge => :fuga, k1: { k2: 'v3' }}} hello|)).to eq(%Q{<span data-bar='baz' data-foo='1' data-hoge='fuga' data-k1-k2='v3'>hello</span>\n})
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'renders nested dynamic attributes' do
|
|
123
|
+
expect(render_string(%q|%span{data: {foo: "b#{'a'}r"}} hello|)).to eq(%Q{<span data-foo='bar'>hello</span>\n})
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'renders nested attributes' do
|
|
127
|
+
expect(render_string(%q|%span{data: {foo: 1, bar: 2+3}} hello|)).to eq(%Q{<span data-bar='5' data-foo='1'>hello</span>\n})
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'renders nested code attributes' do
|
|
131
|
+
expect(render_string(<<HAML)).to eq(%Q{<span data-bar='2' data-foo='1'>hello</span>\n})
|
|
132
|
+
- data = { foo: 1, bar: 2 }
|
|
133
|
+
%span{data: data} hello
|
|
134
|
+
HAML
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
describe 'with HTML-style attributes' do
|
|
139
|
+
it 'parses simple values' do
|
|
140
|
+
expect(render_string('%span(foo=1 bar=3) hello')).to eq("<span bar='3' foo='1'>hello</span>\n")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'parses variables' do
|
|
144
|
+
expect(render_string(<<HAML)).to eq("<span bar='3' foo='xxx'>hello</span>\n")
|
|
145
|
+
- foo = 'xxx'
|
|
146
|
+
%span(foo=foo bar=3) hello
|
|
147
|
+
HAML
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'parses attributes with old syntax' do
|
|
151
|
+
expect(render_string(<<HAML)).to eq("<span bar='3' foo='foo'>hello</span>\n")
|
|
152
|
+
- foo = 'foo'
|
|
153
|
+
%span(foo=foo){bar: 3} hello
|
|
154
|
+
HAML
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it 'parses multiline attribute list' do
|
|
158
|
+
expect(render_string(<<HAML)).to eq("<span bar='3' foo='1'>hello</span>\n")
|
|
159
|
+
%span(foo=1
|
|
160
|
+
|
|
161
|
+
bar=3) hello
|
|
162
|
+
HAML
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "doesn't parse extra parens" do
|
|
166
|
+
expect(render_string('%span(foo=1)(bar=3) hello')).to eq("<span foo='1'>(bar=3) hello</span>\n")
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it 'parses quoted value' do
|
|
170
|
+
expect(render_string('%span(foo=1 bar="baz") hello')).to eq("<span bar='baz' foo='1'>hello</span>\n")
|
|
171
|
+
expect(render_string("%span(foo=1 bar='baz') hello")).to eq("<span bar='baz' foo='1'>hello</span>\n")
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it 'parses key-only attribute' do
|
|
175
|
+
expect(render_string('%span(foo bar=1) hello')).to eq("<span bar='1' foo>hello</span>\n")
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it 'renders string interpolation' do
|
|
179
|
+
expect(render_string(%q|%span(foo=1 bar="baz#{1 + 2}") hello|)).to eq("<span bar='baz3' foo='1'>hello</span>\n")
|
|
180
|
+
expect(render_string(%q|%span(foo=1 bar='baz#{1 + 2}') hello|)).to eq("<span bar='baz3' foo='1'>hello</span>\n")
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
it 'parses escapes' do
|
|
185
|
+
expect(render_string(%q|%span(foo=1 bar="ba\"z") hello|)).to eq("<span bar='ba"z' foo='1'>hello</span>\n")
|
|
186
|
+
expect(render_string(%q|%span(foo=1 bar='ba\'z') hello|)).to eq("<span bar='ba'z' foo='1'>hello</span>\n")
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it 'raises error when attributes list is unterminated' do
|
|
190
|
+
expect { render_string('%span(foo=1 bar=2') }.to raise_error(FastHaml::SyntaxError)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it 'raises error when key is not alnum' do
|
|
194
|
+
expect { render_string('%span(foo=1 3.14=3) hello') }.to raise_error(FastHaml::SyntaxError)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it 'raises error when value is missing' do
|
|
198
|
+
expect { render_string('%span(foo=1 bar=) hello') }.to raise_error(FastHaml::SyntaxError)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it 'raises error when quote is unterminated' do
|
|
202
|
+
expect { render_string('%span(foo=1 bar="baz) hello') }.to raise_error(FastHaml::SyntaxError)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it 'raises error when string interpolation is unterminated' do
|
|
206
|
+
expect { render_string('%span(foo=1 bar="ba#{1") hello') }.to raise_error(FastHaml::SyntaxError)
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'Comment rendering', type: :render do
|
|
4
|
+
it 'renders html comment' do
|
|
5
|
+
expect(render_string('/ comments')).to eq("<!-- comments -->\n")
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'strips spaces' do
|
|
9
|
+
expect(render_string('/ comments ')).to eq("<!-- comments -->\n")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'renders structured comment' do
|
|
13
|
+
expect(render_string(<<HAML)).to eq("<span>hello</span>\n<!--\ngreat\n-->\n<span>world</span>\n")
|
|
14
|
+
%span hello
|
|
15
|
+
/
|
|
16
|
+
great
|
|
17
|
+
%span world
|
|
18
|
+
HAML
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'renders comment with interpolation' do
|
|
22
|
+
expect(render_string(<<'HAML')).to eq("<span>hello</span>\n<!--\ngreat\n-->\n<span>world</span>\n")
|
|
23
|
+
- comment = 'great'
|
|
24
|
+
%span hello
|
|
25
|
+
/
|
|
26
|
+
#{comment}
|
|
27
|
+
%span world
|
|
28
|
+
HAML
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'renders conditional comment' do
|
|
32
|
+
expect(render_string('/ [if IE] hello')).to eq("<!--[if IE]> hello <![endif]-->\n")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'renders conditional comment with children' do
|
|
36
|
+
expect(render_string(<<HAML)).to eq("<!--[if IE]>\n<span>hello</span>\nworld\n<![endif]-->\n")
|
|
37
|
+
/[if IE]
|
|
38
|
+
%span hello
|
|
39
|
+
world
|
|
40
|
+
HAML
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'parses nested conditional comment' do
|
|
44
|
+
expect(render_string(<<HAML)).to eq("<!--[[if IE]]>\n<span>hello</span>\nworld\n<![endif]-->\n")
|
|
45
|
+
/[[if IE]]
|
|
46
|
+
%span hello
|
|
47
|
+
world
|
|
48
|
+
HAML
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'raises error if conditional comment bracket is unbalanced' do
|
|
52
|
+
expect { render_string('/[[if IE]') }.to raise_error(FastHaml::SyntaxError)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'raises error if both comment text and children are given' do
|
|
56
|
+
expect { render_string(<<HAML) }.to raise_error(FastHaml::SyntaxError)
|
|
57
|
+
/ hehehe
|
|
58
|
+
%span hello
|
|
59
|
+
HAML
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'Doctype rendering', type: :render do
|
|
4
|
+
it 'ignores 1 or 2 exclamation marks' do
|
|
5
|
+
expect(render_string('!')).to eq("!\n")
|
|
6
|
+
expect(render_string('!!')).to eq("!!\n")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
context 'with html format' do
|
|
10
|
+
it 'renders html5 doctype by default' do
|
|
11
|
+
expect(render_string('!!!')).to eq("<!DOCTYPE html>\n")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'renders xml doctype (silent)' do
|
|
15
|
+
expect(render_string('!!! xml')).to eq("\n")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context 'with html4 format' do
|
|
20
|
+
it 'renders transitional doctype by default' do
|
|
21
|
+
expect(render_string('!!!', format: :html4)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">\n|)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'renders frameset doctype' do
|
|
25
|
+
expect(render_string('!!! frameset', format: :html4)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">\n|)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'renders strict doctype' do
|
|
29
|
+
expect(render_string('!!! strict', format: :html4)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">\n|)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context 'with xhtml format' do
|
|
34
|
+
it 'renders transitional doctype by default' do
|
|
35
|
+
expect(render_string('!!!', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n|)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'renders xml doctype' do
|
|
39
|
+
expect(render_string('!!! xml', format: :xhtml)).to eq("<?xml version='1.0' encoding='utf-8' ?>\n")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'renders xhtml 1.1 doctype' do
|
|
43
|
+
expect(render_string('!!! 1.1', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n|)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'renders xhtml mobile doctype' do
|
|
47
|
+
expect(render_string('!!! mobile', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">\n|)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'renders xhtml basic doctype' do
|
|
51
|
+
expect(render_string('!!! basic', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">\n|)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'renders xhtml frameset doctype' do
|
|
55
|
+
expect(render_string('!!! frameset', format: :xhtml)).to eq(%Q|<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">\n|)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'renders html 5 doctype with xhtml syntax' do
|
|
59
|
+
expect(render_string('!!! 5', format: :xhtml)).to eq(%Q|<!DOCTYPE html>\n|)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'Element rendering', type: :render do
|
|
4
|
+
it 'parses one-line element' do
|
|
5
|
+
expect(render_string('%span hello')).to eq("<span>hello</span>\n")
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it 'parses multi-line element' do
|
|
9
|
+
expect(render_string(<<HAML)).to eq("<span>\nhello\n</span>\n")
|
|
10
|
+
%span
|
|
11
|
+
hello
|
|
12
|
+
HAML
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'parses nested elements' do
|
|
16
|
+
expect(render_string(<<HAML)).to eq("<span>\n<b>\nhello\n</b>\n<i>\n<small>world</small>\n</i>\n</span>\n")
|
|
17
|
+
%span
|
|
18
|
+
%b
|
|
19
|
+
hello
|
|
20
|
+
%i
|
|
21
|
+
%small world
|
|
22
|
+
HAML
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'parses multi-line texts' do
|
|
26
|
+
expect(render_string(<<HAML)).to eq("<span>\n<b>\nhello\nworld\n</b>\n</span>\n")
|
|
27
|
+
%span
|
|
28
|
+
%b
|
|
29
|
+
hello
|
|
30
|
+
world
|
|
31
|
+
HAML
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'skips empty lines' do
|
|
35
|
+
expect(render_string(<<HAML)).to eq("<span>\n<b>\nhello\n</b>\n</span>\n")
|
|
36
|
+
%span
|
|
37
|
+
|
|
38
|
+
%b
|
|
39
|
+
|
|
40
|
+
hello
|
|
41
|
+
|
|
42
|
+
HAML
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'raises error if indent is wrong' do
|
|
46
|
+
expect { render_string(<<HAML) }.to raise_error(FastHaml::IndentTracker::IndentMismatch)
|
|
47
|
+
%div
|
|
48
|
+
%div
|
|
49
|
+
%div
|
|
50
|
+
%div
|
|
51
|
+
HAML
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'parses classes' do
|
|
55
|
+
expect(render_string('%span.foo.bar hello')).to eq(%Q{<span class='foo bar'>hello</span>\n})
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'parses id' do
|
|
59
|
+
expect(render_string('%span#foo-bar hello')).to eq(%Q{<span id='foo-bar'>hello</span>\n})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'parses classes and id' do
|
|
63
|
+
expect(render_string('%span.foo#foo-bar.bar hello')).to eq(%Q{<span class='foo bar' id='foo-bar'>hello</span>\n})
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context 'with invalid tag name' do
|
|
67
|
+
it 'raises error' do
|
|
68
|
+
expect { render_string('%.foo') }.to raise_error(FastHaml::SyntaxError)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'parses #' do
|
|
73
|
+
expect(render_string('#main')).to eq(%Q{<div id='main'></div>\n})
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'parses .' do
|
|
77
|
+
expect(render_string('.wrapper.main')).to eq(%Q{<div class='wrapper main'></div>\n})
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'parses Ruby multiline' do
|
|
81
|
+
expect(render_string(<<HAML)).to eq("<div>\n<span>2+3i</span>\n</div>\n")
|
|
82
|
+
%div
|
|
83
|
+
%span= Complex(2,
|
|
84
|
+
3)
|
|
85
|
+
HAML
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'parses string interpolation' do
|
|
89
|
+
expect(render_string(%q|%span hello <span> #{'</span>'} </span>|)).to eq("<span>hello <span> </span> </span></span>\n")
|
|
90
|
+
expect(render_string(<<'HAML')).to eq("<span>\nhello <span> </span> </span>\n</span>\n")
|
|
91
|
+
%span
|
|
92
|
+
hello <span> #{{text: '</span>'}[:text]} </span>
|
|
93
|
+
HAML
|
|
94
|
+
expect(render_string(<<'HAML')).to eq("<span>\nhello <span> </span> </span>\n</span>\n")
|
|
95
|
+
- @var = '</span>'
|
|
96
|
+
%span
|
|
97
|
+
hello <span> #@var </span>
|
|
98
|
+
HAML
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'parses string interpolation with multibyte characters' do
|
|
102
|
+
expect(render_string(%q|#{'日本語'} にほんご|)).to eq("日本語 にほんご\n")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'recognized escaped string interpolation' do
|
|
106
|
+
expect(render_string(%q|%p hello \#{1 + 2}|)).to eq("<p>hello \#{1 + 2}</p>\n")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'parses self-closing tag' do
|
|
110
|
+
expect(render_string('%p/')).to eq("<p>\n")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'renders some attributes as self-closing by default' do
|
|
114
|
+
expect(render_string('%meta{"http-equiv" => "Content-Type", :content => "text/html"}')).to eq("<meta content='text/html' http-equiv='Content-Type'>\n")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'parses nuke-inner-whitespace (<)' do
|
|
118
|
+
expect(render_string(<<HAML)).to eq("<blockquote><div>\nFoo!\n</div></blockquote>\n")
|
|
119
|
+
%blockquote<
|
|
120
|
+
%div
|
|
121
|
+
Foo!
|
|
122
|
+
HAML
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it 'renders pre tag as nuke-inner-whitespace by default' do
|
|
126
|
+
expect(render_string(<<HAML)).to eq("<pre>hello\nworld</pre>\n")
|
|
127
|
+
%pre
|
|
128
|
+
hello
|
|
129
|
+
world
|
|
130
|
+
HAML
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'parses nuke-outer-whitespace (>)' do
|
|
134
|
+
expect(render_string(<<HAML)).to eq("<img><span>hello</span><img>\n")
|
|
135
|
+
%img
|
|
136
|
+
%span> hello
|
|
137
|
+
%img
|
|
138
|
+
HAML
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it 'parses nuke-whitespaces' do
|
|
142
|
+
expect(render_string(<<HAML)).to eq("<img><pre>foo\nbar</pre><img>\n")
|
|
143
|
+
%img
|
|
144
|
+
%pre><
|
|
145
|
+
foo
|
|
146
|
+
bar
|
|
147
|
+
%img
|
|
148
|
+
HAML
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'parses == syntax' do
|
|
152
|
+
expect(render_string('%p== =#{1+2}hello')).to eq("<p>=3hello</p>\n")
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it 'raises error if self-closing tag have text' do
|
|
156
|
+
expect { render_string('%p/ hello') }.to raise_error(FastHaml::SyntaxError)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it 'raises error if self-closing tag have children' do
|
|
160
|
+
expect { render_string(<<HAML) }.to raise_error(FastHaml::SyntaxError)
|
|
161
|
+
%p/
|
|
162
|
+
hello
|
|
163
|
+
HAML
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'CDATA filter rendering', type: :render do
|
|
4
|
+
it 'renders CDATA filter' do
|
|
5
|
+
expect(render_string(<<'HAML')).to eq("<![CDATA[\n hello\n world\n <span>hello</span>\n]]>\n")
|
|
6
|
+
:cdata
|
|
7
|
+
hello
|
|
8
|
+
#{'world'}
|
|
9
|
+
<span>hello</span>
|
|
10
|
+
HAML
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'CSS filter rendering', type: :render do
|
|
4
|
+
it 'renders css filter' do
|
|
5
|
+
expect(render_string(<<HAML)).to eq("<div>\n<style>\n html { font-size: 12px; }\n</style>\n<span>hello</span>\n</div>\n")
|
|
6
|
+
%div
|
|
7
|
+
:css
|
|
8
|
+
html { font-size: 12px; }
|
|
9
|
+
%span hello
|
|
10
|
+
HAML
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'keeps indent' do
|
|
14
|
+
expect(render_string(<<HAML)).to eq("<div>\n<style>\n html {\n font-size: 12px;\n }\n</style>\n<span>hello</span>\n</div>\n")
|
|
15
|
+
%div
|
|
16
|
+
:css
|
|
17
|
+
html {
|
|
18
|
+
font-size: 12px;
|
|
19
|
+
}
|
|
20
|
+
%span hello
|
|
21
|
+
HAML
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'ignores empty filter' do
|
|
25
|
+
expect(render_string(<<HAML)).to eq("<div>\n<span>hello</span>\n</div>\n")
|
|
26
|
+
%div
|
|
27
|
+
:css
|
|
28
|
+
%span hello
|
|
29
|
+
HAML
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'parses string interpolation' do
|
|
33
|
+
expect(render_string(<<'HAML')).to eq("<style>\n html { font-size: 12px; }\n</style>\n")
|
|
34
|
+
:css
|
|
35
|
+
html { font-size: #{10 + 2}px; }
|
|
36
|
+
HAML
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "doesn't escape in string interpolation" do
|
|
40
|
+
expect(render_string(<<'HAML')).to eq("<style>\n <span/>\n</style>\n")
|
|
41
|
+
:css
|
|
42
|
+
#{'<span/>'}
|
|
43
|
+
HAML
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'Escaped filter rendering', type: :render do
|
|
4
|
+
it' renders escaped filter' do
|
|
5
|
+
expect(render_string(<<'HAML')).to eq("<span>start</span>\nhello\n <p>world</p>\n<span>hello</span>\n\n<span>end</span>\n")
|
|
6
|
+
%span start
|
|
7
|
+
:escaped
|
|
8
|
+
hello
|
|
9
|
+
#{'<p>world</p>'}
|
|
10
|
+
<span>hello</span>
|
|
11
|
+
%span end
|
|
12
|
+
HAML
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'JavaScript filter renderiong', type: :render do
|
|
4
|
+
it 'renders javascript filter' do
|
|
5
|
+
expect(render_string(<<HAML)).to eq("<div>\n<script>\n alert('hello');\n</script>\n<span>world</span>\n</div>\n")
|
|
6
|
+
%div
|
|
7
|
+
:javascript
|
|
8
|
+
alert('hello');
|
|
9
|
+
%span world
|
|
10
|
+
HAML
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'keeps indent' do
|
|
14
|
+
expect(render_string(<<HAML)).to eq("<div>\n<script>\n alert('hello');\n \n alert('world');\n</script>\n</div>\n")
|
|
15
|
+
%div
|
|
16
|
+
:javascript
|
|
17
|
+
alert('hello');
|
|
18
|
+
|
|
19
|
+
alert('world');
|
|
20
|
+
HAML
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'ignores empty filter' do
|
|
24
|
+
expect(render_string(<<HAML)).to eq("<div>\n<span>world</span>\n</div>\n")
|
|
25
|
+
%div
|
|
26
|
+
:javascript
|
|
27
|
+
%span world
|
|
28
|
+
HAML
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'parses string interpolation' do
|
|
32
|
+
expect(render_string(<<'HAML')).to eq("<script>\n var x = 3;\n</script>\n")
|
|
33
|
+
:javascript
|
|
34
|
+
var x = #{1 + 2};
|
|
35
|
+
HAML
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "doesn't escape in string interpolation" do
|
|
39
|
+
expect(render_string(<<'HAML')).to eq("<script>\n <span/>\n</script>\n")
|
|
40
|
+
:javascript
|
|
41
|
+
#{'<span/>'}
|
|
42
|
+
HAML
|
|
43
|
+
end
|
|
44
|
+
end
|