rux 1.1.1 → 1.2.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 +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -1
- data/README.md +91 -1
- data/bin/ruxc +21 -4
- data/lib/rux/ast/attr_node.rb +22 -0
- data/lib/rux/ast/attrs_node.rb +30 -0
- data/lib/rux/ast/fragment_node.rb +15 -0
- data/lib/rux/ast/root_node.rb +19 -0
- data/lib/rux/ast/ruby_attr_node.rb +28 -0
- data/lib/rux/ast/string_node.rb +4 -2
- data/lib/rux/ast/tag_node.rb +10 -1
- data/lib/rux/ast/text_node.rb +2 -1
- data/lib/rux/ast.rb +10 -5
- data/lib/rux/buffer.rb +21 -3
- data/lib/rux/default_tag_builder.rb +15 -6
- data/lib/rux/default_visitor.rb +93 -19
- data/lib/rux/lex/states.csv +22 -11
- data/lib/rux/parser.rb +102 -24
- data/lib/rux/ruby_lexer.rb +1 -0
- data/lib/rux/rux_lexer.rb +15 -3
- data/lib/rux/version.rb +1 -1
- data/lib/rux/visitor.rb +16 -0
- data/spec/parser/attributes_spec.rb +97 -0
- data/spec/parser/fragment_spec.rb +47 -0
- data/spec/parser/html_safety_spec.rb +13 -0
- data/spec/parser/slot_spec.rb +40 -0
- data/spec/parser/tag_spec.rb +174 -0
- data/spec/parser_spec.rb +17 -242
- data/spec/render_spec.rb +89 -6
- data/spec/spec_helper.rb +19 -22
- data/spec/support/components/args_component.rb +14 -0
- data/spec/support/components/data_component.rb +11 -0
- data/spec/support/components/table_component.rb +23 -0
- data/spec/support/components/test_component.rb +7 -0
- data/spec/support/view_component/base.rb +59 -0
- metadata +17 -2
data/lib/rux/lex/states.csv
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
,[<],[a-zA-Z0-9_-_---:-:],[>],[/],(space),[=],"[""]",['],"[^""]",[^'],[{],[}],(default)
|
2
2
|
start,tag_open_test,,,,,,,,,,literal_ruby_code_start,,literal_body
|
3
|
-
tag_open_test,,tag_open_start[0]
|
3
|
+
tag_open_test,,tag_open_start[0],fragment_open,tag_close_start,,,,,,,,,
|
4
4
|
tag_open_start*,,tag_open_body,,,,,,,,,,,
|
5
5
|
tag_open_body,,tag_open_body,tag_open[0],tag_self_closing[0],tag_open[0],,,,,,,,
|
6
6
|
tag_open*,,,tag_open_end,,attribute_spaces_body,,,,,,,,
|
7
7
|
tag_open_end*,,,,,,,,,,,,,
|
8
|
-
tag_close_start*,,tag_close_body
|
8
|
+
tag_close_start*,,tag_close_body,fragment_close,,,,,,,,,,
|
9
9
|
tag_close_body,,tag_close_body,tag_close[0],,tag_close[0],,,,,,,,
|
10
10
|
tag_close*,,,tag_close_end,tag_self_closing[0],tag_close_spaces_body,,,,,,,,
|
11
11
|
tag_close_spaces_body,,,tag_close_spaces[0],,tag_close_spaces_body,,,,,,,,
|
@@ -14,23 +14,34 @@ tag_close_end*,,,,,,,,,,,,,
|
|
14
14
|
tag_self_closing*,,,,tag_self_closing_start,,,,,,,,,
|
15
15
|
tag_self_closing_start,,,tag_self_closing_end,,,,,,,,,,
|
16
16
|
tag_self_closing_end*,,,,,,,,,,,,,
|
17
|
+
fragment_open*,,,,,,,,,,,,,
|
18
|
+
fragment_close*,,,,,,,,,,,,,
|
17
19
|
,,,,,,,,,,,,,
|
18
|
-
attribute_spaces_body,,attribute_spaces[0],attribute_spaces[0],attribute_spaces[0],attribute_spaces_body
|
19
|
-
attribute_spaces*,,attribute_name_body,tag_close_start,tag_self_closing_start
|
20
|
+
attribute_spaces_body,,attribute_spaces[0],attribute_spaces[0],attribute_spaces[0],attribute_spaces_body,,,,,,attribute_spaces[0],,
|
21
|
+
attribute_spaces*,,attribute_name_body,tag_close_start,tag_self_closing_start,,,,,,,attribute_ruby_code_start,,
|
20
22
|
attribute_name_body,,attribute_name_body,attribute_name[0],attribute_name[0],attribute_name[0],attribute_name[0],,,,,,,
|
21
23
|
attribute_name*,,,tag_open_end,tag_self_closing_start,attribute_equals_spaces_body,attribute_equals,,,,,,,
|
22
24
|
attribute_equals_spaces_body,,,attribute_equals_spaces[0],,attribute_equals_spaces_body,attribute_equals_spaces[0],,,,,,,attribute_equals_spaces[0]
|
23
25
|
attribute_equals_spaces*,,,tag_open_end,tag_self_closing_start,,attribute_equals,,,,,,,attribute_name_body
|
24
|
-
attribute_equals*,,
|
25
|
-
attribute_value_spaces_body,,attribute_value_spaces[0]
|
26
|
-
attribute_value_spaces*,,
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
attribute_equals*,,attribute_value_uq_body,,,attribute_value_spaces_body[0],,attribute_value_dq_start,attribute_value_sq_start,,,attribute_value_ruby_code_start,,
|
27
|
+
attribute_value_spaces_body,,attribute_value_spaces[0],attribute_value_spaces[0],attribute_value_spaces[0],attribute_value_spaces_body,,attribute_value_spaces[0],attribute_value_spaces[0],,,attribute_value_spaces[0],,
|
28
|
+
attribute_value_spaces*,,attribute_value_uq_body,tag_open_end,tag_self_closing_start,,,attribute_value_dq_start,attribute_value_sq_start,,,attribute_value_ruby_code_start,,
|
29
|
+
attribute_value_dq_start*,,,,,,,,,attribute_value_dq_body,,,,
|
30
|
+
attribute_value_sq_start*,,,,,,,,,,attribute_value_sq_body,,,
|
31
|
+
attribute_value_dq_body,,,,,,,attribute_dq_value[0],,attribute_value_dq_body,,,,
|
32
|
+
attribute_value_sq_body,,,,,,,,attribute_sq_value[0],,attribute_value_sq_body,,,
|
33
|
+
attribute_value_uq_body,,attribute_value_uq_body,attribute_uq_value[0],attrIbute_uq_value[0],attribute_uq_value[0],,,,,,,,
|
34
|
+
attribute_value_dq_end*,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
35
|
+
attribute_value_sq_end*,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
30
36
|
attribute_value_ruby_code_start*,,,,,,,,,,,,,attribute_value_ruby_code
|
31
37
|
attribute_value_ruby_code*,,,,,,,,,,,,attribute_value_ruby_code_end,
|
32
38
|
attribute_value_ruby_code_end*,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
33
|
-
|
39
|
+
attribute_dq_value*,,,,,,,attribute_value_dq_end,,,,,,
|
40
|
+
attribute_sq_value*,,,,,,,,attribute_value_sq_end,,,,,
|
41
|
+
attribute_uq_value*,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
42
|
+
attribute_ruby_code_start*,,,,,,,,,,,,,attribute_ruby_code[0]
|
43
|
+
attribute_ruby_code*,,,,,,,,,,,,attribute_ruby_code_end,
|
44
|
+
attribute_ruby_code_end*,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,,,,,,,
|
34
45
|
,,,,,,,,,,,,,
|
35
46
|
literal_body,literal[0],,,,,,,,,,literal[0],,literal_body
|
36
47
|
literal*,,,,,,,,,,,literal_ruby_code_start,,
|
data/lib/rux/parser.rb
CHANGED
@@ -26,6 +26,12 @@ module Rux
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def parse
|
29
|
+
AST::RootNode.new(list)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def list
|
29
35
|
curlies = 1
|
30
36
|
children = []
|
31
37
|
|
@@ -34,9 +40,9 @@ module Rux
|
|
34
40
|
break unless type
|
35
41
|
|
36
42
|
case type
|
37
|
-
when :tLCURLY, :tLBRACE, :tRUX_LITERAL_RUBY_CODE_START
|
43
|
+
when :tLCURLY, :tLBRACE, :tRUX_LITERAL_RUBY_CODE_START, :tRUX_ATTRIBUTE_RUBY_CODE_START
|
38
44
|
curlies += 1
|
39
|
-
when :tRCURLY, :tRBRACE, :tRUX_LITERAL_RUBY_CODE_END
|
45
|
+
when :tRCURLY, :tRBRACE, :tRUX_LITERAL_RUBY_CODE_END, :tRUX_ATTRIBUTE_RUBY_CODE_END
|
40
46
|
curlies -= 1
|
41
47
|
end
|
42
48
|
|
@@ -46,6 +52,8 @@ module Rux
|
|
46
52
|
children << rb
|
47
53
|
elsif type_of(current) == :tRUX_TAG_OPEN_START
|
48
54
|
children << tag
|
55
|
+
elsif type_of(current) == :tRUX_FRAGMENT_OPEN
|
56
|
+
children << fragment
|
49
57
|
else
|
50
58
|
raise UnexpectedTokenError,
|
51
59
|
'expected ruby code or the start of a rux tag but found '\
|
@@ -56,8 +64,6 @@ module Rux
|
|
56
64
|
AST::ListNode.new(children)
|
57
65
|
end
|
58
66
|
|
59
|
-
private
|
60
|
-
|
61
67
|
def ruby
|
62
68
|
ruby_start = pos_of(current).begin_pos
|
63
69
|
|
@@ -79,7 +85,7 @@ module Rux
|
|
79
85
|
|
80
86
|
if pos_of(current).begin_pos != ruby_start
|
81
87
|
AST::RubyNode.new(
|
82
|
-
@lexer.source_buffer.source[ruby_start...(pos_of(current).
|
88
|
+
@lexer.source_buffer.source[ruby_start...(pos_of(current).begin_pos)]
|
83
89
|
)
|
84
90
|
end
|
85
91
|
end
|
@@ -93,7 +99,8 @@ module Rux
|
|
93
99
|
attrs = attributes
|
94
100
|
maybe_consume(:tRUX_ATTRIBUTE_SPACES)
|
95
101
|
maybe_consume(:tRUX_TAG_OPEN_END)
|
96
|
-
tag_node = AST::TagNode.new(tag_name, attrs)
|
102
|
+
tag_node = AST::TagNode.new(tag_name, attrs, tag_pos)
|
103
|
+
attrs.each { |attr_node| attr_node.tag_node = tag_node }
|
97
104
|
|
98
105
|
if is?(:tRUX_TAG_SELF_CLOSING_END)
|
99
106
|
consume(:tRUX_TAG_SELF_CLOSING_END)
|
@@ -103,12 +110,7 @@ module Rux
|
|
103
110
|
@stack.push(tag_name)
|
104
111
|
|
105
112
|
until is?(:tRUX_TAG_CLOSE_START)
|
106
|
-
|
107
|
-
lit = literal
|
108
|
-
tag_node.children << lit if lit
|
109
|
-
else
|
110
|
-
tag_node.children << tag
|
111
|
-
end
|
113
|
+
populate_next_child(tag_node)
|
112
114
|
end
|
113
115
|
|
114
116
|
consume(:tRUX_TAG_CLOSE_START)
|
@@ -131,44 +133,106 @@ module Rux
|
|
131
133
|
tag_node
|
132
134
|
end
|
133
135
|
|
136
|
+
def populate_next_child(node)
|
137
|
+
if is?(:tRUX_LITERAL, :tRUX_LITERAL_RUBY_CODE_START)
|
138
|
+
lit = literal
|
139
|
+
node.children << lit if lit
|
140
|
+
else
|
141
|
+
node.children << tag
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
134
145
|
def attributes
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
146
|
+
pos = pos_of(current)
|
147
|
+
|
148
|
+
attrs = [].tap do |attrs|
|
149
|
+
loop do
|
150
|
+
attr = case type_of(current)
|
151
|
+
when :tRUX_ATTRIBUTE_NAME
|
152
|
+
attribute
|
153
|
+
when :tRUX_ATTRIBUTE_RUBY_CODE_START
|
154
|
+
ruby_attribute
|
155
|
+
end
|
139
156
|
|
140
157
|
maybe_consume(:tRUX_ATTRIBUTE_SPACES)
|
158
|
+
|
159
|
+
if attr
|
160
|
+
attrs << attr
|
161
|
+
else
|
162
|
+
break
|
163
|
+
end
|
141
164
|
end
|
142
165
|
end
|
166
|
+
|
167
|
+
AST::AttrsNode.new(attrs, pos)
|
143
168
|
end
|
144
169
|
|
145
170
|
def attribute
|
146
|
-
maybe_consume(:tRUX_ATTRIBUTE_SPACES)
|
147
171
|
attr_name = text_of(current)
|
172
|
+
attr_pos = pos_of(current)
|
148
173
|
consume(:tRUX_ATTRIBUTE_NAME)
|
149
174
|
maybe_consume(:tRUX_ATTRIBUTE_EQUALS_SPACES)
|
150
175
|
|
151
176
|
attr_value = if maybe_consume(:tRUX_ATTRIBUTE_EQUALS)
|
152
177
|
maybe_consume(:tRUX_ATTRIBUTE_VALUE_SPACES)
|
153
|
-
attribute_value
|
178
|
+
attribute_value.tap do
|
179
|
+
maybe_consume(:tRUX_ATTRIBUTE_VALUE_SPACES)
|
180
|
+
end
|
154
181
|
else
|
155
182
|
# if no equals sign, assume boolean attribute
|
156
|
-
AST::StringNode.new(
|
183
|
+
AST::StringNode.new('true', :none, nil)
|
157
184
|
end
|
158
185
|
|
159
|
-
|
186
|
+
AST::AttrNode.new(attr_name, attr_value, attr_pos)
|
187
|
+
end
|
188
|
+
|
189
|
+
def ruby_attribute
|
190
|
+
consume(:tRUX_ATTRIBUTE_RUBY_CODE_START)
|
191
|
+
|
192
|
+
AST::RubyAttrNode.new(ruby).tap do
|
193
|
+
consume(:tRUX_ATTRIBUTE_RUBY_CODE_END)
|
194
|
+
end
|
160
195
|
end
|
161
196
|
|
162
197
|
def attribute_value
|
163
198
|
if is?(:tRUX_ATTRIBUTE_VALUE_RUBY_CODE_START)
|
164
199
|
attr_ruby_code
|
165
200
|
else
|
166
|
-
|
167
|
-
|
201
|
+
case type_of(current)
|
202
|
+
when :tRUX_ATTRIBUTE_VALUE_DQ_START
|
203
|
+
attribute_value_dq
|
204
|
+
when :tRUX_ATTRIBUTE_VALUE_SQ_START
|
205
|
+
attribute_value_sq
|
206
|
+
when :tRUX_ATTRIBUTE_UQ_VALUE
|
207
|
+
attribute_value_uq
|
168
208
|
end
|
169
209
|
end
|
170
210
|
end
|
171
211
|
|
212
|
+
def attribute_value_dq
|
213
|
+
consume(:tRUX_ATTRIBUTE_VALUE_DQ_START)
|
214
|
+
|
215
|
+
AST::StringNode.new(text_of(current), :double, pos_of(current)).tap do
|
216
|
+
consume(:tRUX_ATTRIBUTE_DQ_VALUE)
|
217
|
+
consume(:tRUX_ATTRIBUTE_VALUE_DQ_END)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def attribute_value_sq
|
222
|
+
consume(:tRUX_ATTRIBUTE_VALUE_SQ_START)
|
223
|
+
|
224
|
+
AST::StringNode.new(text_of(current), :single, pos_of(current)).tap do
|
225
|
+
consume(:tRUX_ATTRIBUTE_SQ_VALUE)
|
226
|
+
consume(:tRUX_ATTRIBUTE_VALUE_SQ_END)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def attribute_value_uq
|
231
|
+
AST::StringNode.new(text_of(current), :none, pos_of(current)).tap do
|
232
|
+
consume(:tRUX_ATTRIBUTE_UQ_VALUE)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
172
236
|
def attr_ruby_code
|
173
237
|
consume(:tRUX_ATTRIBUTE_VALUE_RUBY_CODE_START)
|
174
238
|
|
@@ -177,13 +241,27 @@ module Rux
|
|
177
241
|
end
|
178
242
|
end
|
179
243
|
|
244
|
+
def fragment
|
245
|
+
consume(:tRUX_FRAGMENT_OPEN)
|
246
|
+
|
247
|
+
AST::FragmentNode.new.tap do |fragment_node|
|
248
|
+
until is?(:tRUX_TAG_CLOSE_START)
|
249
|
+
populate_next_child(fragment_node)
|
250
|
+
end
|
251
|
+
|
252
|
+
consume(:tRUX_TAG_CLOSE_START)
|
253
|
+
consume(:tRUX_FRAGMENT_CLOSE)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
180
257
|
def literal
|
181
258
|
if is?(:tRUX_LITERAL_RUBY_CODE_START)
|
182
259
|
literal_ruby_code
|
183
260
|
else
|
184
261
|
lit = squeeze_lit(text_of(current))
|
262
|
+
pos = pos_of(current)
|
185
263
|
consume(:tRUX_LITERAL)
|
186
|
-
AST::TextNode.new(lit) unless lit.empty?
|
264
|
+
AST::TextNode.new(lit, pos) unless lit.empty?
|
187
265
|
end
|
188
266
|
end
|
189
267
|
|
@@ -197,7 +275,7 @@ module Rux
|
|
197
275
|
def literal_ruby_code
|
198
276
|
consume(:tRUX_LITERAL_RUBY_CODE_START)
|
199
277
|
|
200
|
-
|
278
|
+
list.tap do
|
201
279
|
consume(:tRUX_LITERAL_RUBY_CODE_END)
|
202
280
|
end
|
203
281
|
end
|
data/lib/rux/ruby_lexer.rb
CHANGED
data/lib/rux/rux_lexer.rb
CHANGED
@@ -2,6 +2,17 @@ require 'csv'
|
|
2
2
|
|
3
3
|
module Rux
|
4
4
|
class RuxLexer
|
5
|
+
RUBY_CODE_STATES = [
|
6
|
+
# ruby code in attributes, eg. <div {**kwargs}>
|
7
|
+
:tRUX_ATTRIBUTE_RUBY_CODE,
|
8
|
+
|
9
|
+
# ruby code in attribute values, eg. <div foo={bar}>
|
10
|
+
:tRUX_ATTRIBUTE_VALUE_RUBY_CODE,
|
11
|
+
|
12
|
+
# ruby code in tag bodies, eg. <div>{foo}</div>
|
13
|
+
:tRUX_LITERAL_RUBY_CODE
|
14
|
+
].freeze
|
15
|
+
|
5
16
|
class << self
|
6
17
|
# See: https://docs.google.com/spreadsheets/d/11ikKuySIKoaj-kFIfhlzebUwH31cRt_1flGjWfk7RMg
|
7
18
|
def state_table
|
@@ -87,11 +98,13 @@ module Rux
|
|
87
98
|
case state
|
88
99
|
when :tRUX_TAG_OPEN, :tRUX_TAG_SELF_CLOSING
|
89
100
|
tag_stack.push(text)
|
101
|
+
when :tRUX_FRAGMENT_OPEN
|
102
|
+
tag_stack.push(:__fragment)
|
90
103
|
when :tRUX_TAG_CLOSE
|
91
104
|
tag_stack.pop
|
92
105
|
when :tRUX_TAG_CLOSE_END
|
93
106
|
break if tag_stack.empty?
|
94
|
-
when :tRUX_TAG_SELF_CLOSING_END
|
107
|
+
when :tRUX_TAG_SELF_CLOSING_END, :tRUX_FRAGMENT_CLOSE
|
95
108
|
tag_stack.pop
|
96
109
|
break if tag_stack.empty?
|
97
110
|
end
|
@@ -150,8 +163,7 @@ module Rux
|
|
150
163
|
# bodies. Eventually I'd like to also allow passing a Ruby hash to
|
151
164
|
# dynamically specify attributes, but we're not there yet.
|
152
165
|
def ruby_code?(state)
|
153
|
-
state
|
154
|
-
state == :tRUX_LITERAL_RUBY_CODE
|
166
|
+
RUBY_CODE_STATES.include?(state)
|
155
167
|
end
|
156
168
|
end
|
157
169
|
end
|
data/lib/rux/version.rb
CHANGED
data/lib/rux/visitor.rb
CHANGED
@@ -4,6 +4,10 @@ module Rux
|
|
4
4
|
node.accept(self)
|
5
5
|
end
|
6
6
|
|
7
|
+
def visit_root(node)
|
8
|
+
visit_children(node)
|
9
|
+
end
|
10
|
+
|
7
11
|
def visit_list(node)
|
8
12
|
visit_children(node)
|
9
13
|
end
|
@@ -20,6 +24,18 @@ module Rux
|
|
20
24
|
visit_children(node)
|
21
25
|
end
|
22
26
|
|
27
|
+
def visit_attrs(node)
|
28
|
+
visit_children(node)
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit_attr(node)
|
32
|
+
visit_children(node)
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_fragment(node)
|
36
|
+
visit_children(node)
|
37
|
+
end
|
38
|
+
|
23
39
|
def visit_text(node)
|
24
40
|
visit_children(node)
|
25
41
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'attributes', type: :parser do
|
4
|
+
it 'handles single-quoted rux attributes' do
|
5
|
+
expect(compile("<Hello foo='bar' />")).to eq(
|
6
|
+
'render(Hello.new(foo: "bar"))'
|
7
|
+
)
|
8
|
+
|
9
|
+
expect(compile("<Hello foo='bar'></Hello>")).to eq(
|
10
|
+
'render(Hello.new(foo: "bar"))'
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'handles double-quoted rux attributes' do
|
15
|
+
expect(compile('<Hello foo="bar" />')).to eq(
|
16
|
+
'render(Hello.new(foo: "bar"))'
|
17
|
+
)
|
18
|
+
|
19
|
+
expect(compile('<Hello foo="bar"></Hello>')).to eq(
|
20
|
+
'render(Hello.new(foo: "bar"))'
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'handles unquoted rux attributes' do
|
25
|
+
expect(compile('<Hello foo=bar />')).to eq(
|
26
|
+
'render(Hello.new(foo: "bar"))'
|
27
|
+
)
|
28
|
+
|
29
|
+
expect(compile('<Hello foo=bar></Hello>')).to eq(
|
30
|
+
'render(Hello.new(foo: "bar"))'
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'handles non-uniform spacing between attributes' do
|
35
|
+
expect(compile('<Hello foo="bar" baz= "boo" bix ="bit" />')).to eq(
|
36
|
+
'render(Hello.new(foo: "bar", baz: "boo", bix: "bit"))'
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'handles boolean attributes' do
|
41
|
+
expect(compile('<Hello disabled />')).to eq(
|
42
|
+
'render(Hello.new(disabled: "true"))'
|
43
|
+
)
|
44
|
+
|
45
|
+
expect(compile('<Hello disabled/>')).to eq(
|
46
|
+
'render(Hello.new(disabled: "true"))'
|
47
|
+
)
|
48
|
+
|
49
|
+
expect(compile('<Hello disabled></Hello>')).to eq(
|
50
|
+
'render(Hello.new(disabled: "true"))'
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'converts dashes to underscores in attribute keys' do
|
55
|
+
expect(compile('<Hello foo-bar="baz" />')).to eq(
|
56
|
+
'render(Hello.new(foo_bar: "baz"))'
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'handles simple ruby statements in attributes' do
|
61
|
+
expect(compile('<Hello foo={true} />')).to eq(
|
62
|
+
'render(Hello.new(foo: true))'
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'handles ruby hashes in attributes' do
|
67
|
+
expect(compile('<Hello foo={{ foo: "bar", baz: "boo" }} />')).to eq(
|
68
|
+
'render(Hello.new(foo: { foo: "bar", baz: "boo" }))'
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'handles ruby code with curly braces in attributes' do
|
73
|
+
expect(compile('<Hello foo={[1, 2, 3].map { |n| n * 2 }} />')).to eq(<<~RUBY.strip)
|
74
|
+
render(Hello.new(foo: [1, 2, 3].map { |n|
|
75
|
+
n * 2
|
76
|
+
}))
|
77
|
+
RUBY
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'slugifies ruby arguments' do
|
81
|
+
code = <<~RUX
|
82
|
+
<Hello data-foo="bar" />
|
83
|
+
RUX
|
84
|
+
expect(compile(code)).to eq(<<~RUBY.strip)
|
85
|
+
render(Hello.new(data_foo: "bar"))
|
86
|
+
RUBY
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'does not slugify HTML attributes' do
|
90
|
+
code = <<~RUX
|
91
|
+
<div data-foo="bar" />
|
92
|
+
RUX
|
93
|
+
expect(compile(code)).to eq(<<~RUBY.strip)
|
94
|
+
Rux.tag("div", { :"data-foo" => "bar" })
|
95
|
+
RUBY
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'fragments', type: :parser do
|
4
|
+
it 'allows fragments' do
|
5
|
+
code = <<~RUX
|
6
|
+
<>
|
7
|
+
<div>Foo 1</div>
|
8
|
+
<div>Foo 2</div>
|
9
|
+
</>
|
10
|
+
RUX
|
11
|
+
expect(compile(code)).to eq(<<~RUBY.strip)
|
12
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
13
|
+
_rux_buf_.append(Rux.tag("div") {
|
14
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
15
|
+
_rux_buf_.safe_append("Foo 1")
|
16
|
+
}.to_s
|
17
|
+
})
|
18
|
+
_rux_buf_.append(Rux.tag("div") {
|
19
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
20
|
+
_rux_buf_.safe_append("Foo 2")
|
21
|
+
}.to_s
|
22
|
+
})
|
23
|
+
}.to_s
|
24
|
+
RUBY
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'allows fragments nested inside ruby code' do
|
28
|
+
code = <<~RUX
|
29
|
+
<table>
|
30
|
+
{rows.map do |row|
|
31
|
+
<>{row}</>
|
32
|
+
end}
|
33
|
+
</table>
|
34
|
+
RUX
|
35
|
+
expect(compile(code)).to eq(<<~RUBY.strip)
|
36
|
+
Rux.tag("table") {
|
37
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
38
|
+
_rux_buf_.append(rows.map { |row|
|
39
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
40
|
+
_rux_buf_.append(row)
|
41
|
+
}.to_s
|
42
|
+
})
|
43
|
+
}.to_s
|
44
|
+
}
|
45
|
+
RUBY
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'html safety', type: :parser do
|
4
|
+
it 'escapes HTML entities in strings' do
|
5
|
+
expect(compile('<Hello>"foo"</Hello>')).to eq(<<~RUBY.strip)
|
6
|
+
render(Hello.new) { |rux_block_arg0|
|
7
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
8
|
+
_rux_buf_.safe_append(""foo"")
|
9
|
+
}.to_s
|
10
|
+
}
|
11
|
+
RUBY
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'slots', type: :parser do
|
4
|
+
it 'correctly transforms slot components into slot methods' do
|
5
|
+
code = <<~RUX
|
6
|
+
<TableComponent>
|
7
|
+
<WithRow>
|
8
|
+
<WithColumn>Foo 1</WithColumn>
|
9
|
+
</WithRow>
|
10
|
+
<WithRow>
|
11
|
+
<WithColumn>Foo 2</WithColumn>
|
12
|
+
</WithRow>
|
13
|
+
</TableComponent>
|
14
|
+
RUX
|
15
|
+
expect(compile(code)).to eq(<<~RUBY.strip)
|
16
|
+
render(TableComponent.new) { |rux_block_arg0|
|
17
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
18
|
+
_rux_buf_.append((rux_block_arg0.with_row { |rux_block_arg1|
|
19
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
20
|
+
_rux_buf_.append((rux_block_arg1.with_column { |rux_block_arg2|
|
21
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
22
|
+
_rux_buf_.safe_append("Foo 1")
|
23
|
+
}.to_s
|
24
|
+
}; nil))
|
25
|
+
}.to_s
|
26
|
+
}; nil))
|
27
|
+
_rux_buf_.append((rux_block_arg0.with_row { |rux_block_arg1|
|
28
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
29
|
+
_rux_buf_.append((rux_block_arg1.with_column { |rux_block_arg2|
|
30
|
+
Rux.create_buffer.tap { |_rux_buf_|
|
31
|
+
_rux_buf_.safe_append("Foo 2")
|
32
|
+
}.to_s
|
33
|
+
}; nil))
|
34
|
+
}.to_s
|
35
|
+
}; nil))
|
36
|
+
}.to_s
|
37
|
+
}
|
38
|
+
RUBY
|
39
|
+
end
|
40
|
+
end
|