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.
@@ -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