rux 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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],,tag_close_start,,,,,,,,,
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*,,attribute_uq_body,,,attribute_value_spaces_body[0],,attribute_dq_body,attribute_sq_body,,,attribute_value_ruby_code_start,,
25
- attribute_value_spaces_body,,attribute_value_spaces[0],,,attribute_value_spaces_body,,attribute_value_spaces[0],attribute_value_spaces[0],,,attribute_value_spaces[0],,
26
- attribute_value_spaces*,,attribute_uq_body,,,,,attribute_dq_body,attribute_sq_body,,,attribute_value_ruby_code_start,,
27
- attribute_dq_body,,,,,,,attribute_value,,attribute_dq_body,,,,
28
- attribute_sq_body,,,,,,,,attribute_value,,attribute_sq_body,,,
29
- attribute_uq_body,,attribute_uq_body,,,attribute_value,,,,,,,,
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
- attribute_value*,,attribute_name,tag_open_end,tag_self_closing_start,attribute_spaces_body,,attribute_value_ending,attribute_value_ending,,,,,
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).end_pos - 1)]
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
- if is?(:tRUX_LITERAL, :tRUX_LITERAL_RUBY_CODE_START)
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
- {}.tap do |attrs|
136
- while is?(:tRUX_ATTRIBUTE_NAME)
137
- key, value = attribute
138
- attrs[key] = value
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("\"true\"")
183
+ AST::StringNode.new('true', :none, nil)
157
184
  end
158
185
 
159
- [attr_name, attr_value]
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
- AST::StringNode.new(text_of(current)).tap do
167
- consume(:tRUX_ATTRIBUTE_VALUE)
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
- parse.tap do |res|
278
+ list.tap do
201
279
  consume(:tRUX_LITERAL_RUBY_CODE_END)
202
280
  end
203
281
  end
@@ -117,6 +117,7 @@ module Rux
117
117
 
118
118
  def at_lt?
119
119
  is?(@rux_token_queue[1], :tLT) && (
120
+ is?(@rux_token_queue[2], :tGT) ||
120
121
  is?(@rux_token_queue[2], :tCONSTANT) ||
121
122
  is?(@rux_token_queue[2], :tIDENTIFIER)
122
123
  )
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 == :tRUX_ATTRIBUTE_VALUE_RUBY_CODE ||
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
@@ -1,3 +1,3 @@
1
1
  module Rux
2
- VERSION = '1.1.1'
2
+ VERSION = '1.2.0'
3
3
  end
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("&quot;foo&quot;")
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