rux 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/Gemfile +1 -1
- data/README.md +88 -0
- 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 +69 -6
- data/spec/spec_helper.rb +19 -32
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fba8846934ea39d67f4705ce5a0ec7a477e65eccc5fc5b63a6ff1f6a2233e9ca
|
4
|
+
data.tar.gz: a27256e117f07fe67fe286bc0730b265bccd91e98e18c6944aa968862b8c0e4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62c36cfb6e05f6379f659e89c6bc3bea79144ff6eae9de80491597686b967daabe6c84b5e55dbf24ef104b9e7267aa186c370c9fae18ba8c1fcf038a3805db20
|
7
|
+
data.tar.gz: 3c7c4a4b5f7ac00a344358df1e02f7bf5850aa76b0974f466b6759973f6b8400700cfc2050e6c9e1debff42ac8ad56eba9e5b018261bfa4e6dfd4b344dbbca70
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# 1.2.0
|
2
|
+
* Improve output safety.
|
3
|
+
- HTML tags are now automatically escaped when they come from Ruby code.
|
4
|
+
* Add fragment support.
|
5
|
+
- Analogous to JSX fragments, eg. `<>foo</>`.
|
6
|
+
* Add keyword argument support in HTML attributes.
|
7
|
+
- Eg. `<div {**kwargs} bar="baz">boo</div>`.
|
8
|
+
* Add ViewComponent slot support.
|
9
|
+
- Works via pseudo components that begin with `With`, eg. `<MySlotComponent><WithItem>Item</WithItem></MySlotComponent>`.
|
10
|
+
* Allow printing `ruxc` results to STDOUT.
|
11
|
+
* Support for unquoted attributes.
|
12
|
+
* Drop explicit support for Ruby versions < 3.
|
13
|
+
|
1
14
|
# 1.1.2
|
2
15
|
* Don't slugify HTML attributes in the tag builder either.
|
3
16
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -148,6 +148,94 @@ end
|
|
148
148
|
|
149
149
|
Notice we were able to embed Ruby within rux within Ruby within rux. Within Ruby. The rux parser supports unlimited levels of nesting, although you'll probably not want to go _too_ crazy.
|
150
150
|
|
151
|
+
## Slots
|
152
|
+
|
153
|
+
Rux fully supports the view_component gem's [slots feature](https://viewcomponent.org/guide/slots.html), which allows a component to expose specific points in the rendered output where the caller can provide their own content. Let's look at a table component that exposes rows and columns via slots:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
class TableComponent < ViewComponent::Base
|
157
|
+
renders_many :rows, RowComponent
|
158
|
+
|
159
|
+
def call
|
160
|
+
<table>
|
161
|
+
{rows.each do |row|
|
162
|
+
<>{row}</>
|
163
|
+
end}
|
164
|
+
</table>
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class RowComponent < ViewComponent::Base
|
169
|
+
renders_many :columns, ColumnComponent
|
170
|
+
|
171
|
+
def call
|
172
|
+
<tr>
|
173
|
+
{columns.each do |column|
|
174
|
+
<>{column}</>
|
175
|
+
end}
|
176
|
+
</tr>
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
class ColumnComponent < ViewComponent::Base
|
181
|
+
def call
|
182
|
+
<td>{content}</td>
|
183
|
+
end
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
Notice the use of rux fragments (analogous to JSX fragments) via the `<></>` syntax. This allows emitting a slot by dropping back to ruby via rux.
|
188
|
+
|
189
|
+
The `TableComponent` might be rendered in an ERB template like so:
|
190
|
+
|
191
|
+
```erb
|
192
|
+
<%= render(TableComponent.new) do |table| %>
|
193
|
+
<% table.with_row do |row| %>
|
194
|
+
<% row.with_column { "Row 1, Col 1" } %>
|
195
|
+
<% row.with_column { "Row 1, Col 2" } %>
|
196
|
+
<% end %>
|
197
|
+
<% table.with_row do |row| %>
|
198
|
+
<% row.with_column { "Row 2, Col 1" } %>
|
199
|
+
<% row.with_column { "Row 2, Col 2" } %>
|
200
|
+
<% end %>
|
201
|
+
<% end %>
|
202
|
+
```
|
203
|
+
|
204
|
+
Notice the slots are "filled in" using the `#with_row` and `#with_column` methods. In rux, these methods become components:
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
<TableComponent>
|
208
|
+
<WithRow>
|
209
|
+
<WithColumn>Row 1, Col 1</WithColumn>
|
210
|
+
<WithColumn>Row 1, Col 2</WithColumn>
|
211
|
+
</WithRow>
|
212
|
+
<WithRow>
|
213
|
+
<WithColumn>Row 2, Col 1</WithColumn>
|
214
|
+
<WithColumn>Row 2, Col 2</WithColumn>
|
215
|
+
</WithRow>
|
216
|
+
</TableComponent>
|
217
|
+
```
|
218
|
+
|
219
|
+
## The `as:` argument
|
220
|
+
|
221
|
+
In ViewComponent, component instances are yielded to the block on `#render`, eg:
|
222
|
+
|
223
|
+
```erb
|
224
|
+
<%= render(MyComponent.new) do |component| %>
|
225
|
+
<%# 'component' is the instance of MyComponent passed to #render above %>
|
226
|
+
<% end %>
|
227
|
+
```
|
228
|
+
|
229
|
+
Most of the time in rux, a reference to the component instance isn't necessary (see the section on slots above). Occasionally however it can be useful to, for example, call methods on the component instance to query its state, etc. Use the `as:` argument to assign the component instance to a local variable that's available inside the tag body:
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
<TableComponent something={value} as={table}>
|
233
|
+
{if table.something
|
234
|
+
# your code here
|
235
|
+
end}
|
236
|
+
</TableComponent>
|
237
|
+
```
|
238
|
+
|
151
239
|
## Keyword Arguments Only
|
152
240
|
|
153
241
|
Any view component that will be rendered by rux must _only_ accept keyword arguments in its constructor. For example:
|
data/bin/ruxc
CHANGED
@@ -14,8 +14,8 @@ class RuxCLI
|
|
14
14
|
end
|
15
15
|
|
16
16
|
options = {
|
17
|
-
|
18
|
-
|
17
|
+
pretty: true,
|
18
|
+
stdout: false
|
19
19
|
}
|
20
20
|
|
21
21
|
if argv.first != '-h' && argv.first != '--help'
|
@@ -33,6 +33,14 @@ class RuxCLI
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
oneline(<<~DESC).tap do |desc|
|
37
|
+
Print results to STDOUT instead of writing files to disk.
|
38
|
+
DESC
|
39
|
+
opts.on('-o', '--stdout', desc) do |stdout|
|
40
|
+
options[:stdout] = stdout
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
36
44
|
opts.on('-h', '--help', 'Prints this help info') do
|
37
45
|
puts opts
|
38
46
|
exit
|
@@ -79,6 +87,10 @@ class RuxCLI
|
|
79
87
|
@options[:pretty]
|
80
88
|
end
|
81
89
|
|
90
|
+
def write_to_stdout?
|
91
|
+
@options[:stdout]
|
92
|
+
end
|
93
|
+
|
82
94
|
private
|
83
95
|
|
84
96
|
def directory?
|
@@ -91,6 +103,11 @@ cli.validate
|
|
91
103
|
|
92
104
|
cli.each_file do |in_file, out_file, rbi_file|
|
93
105
|
rux_file = Rux::File.new(in_file)
|
94
|
-
|
95
|
-
|
106
|
+
|
107
|
+
if cli.write_to_stdout?
|
108
|
+
puts rux_file.to_ruby(pretty: cli.pretty?)
|
109
|
+
else
|
110
|
+
rux_file.write(out_file, pretty: cli.pretty?)
|
111
|
+
puts "Wrote #{out_file}"
|
112
|
+
end
|
96
113
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Rux
|
2
|
+
module AST
|
3
|
+
class AttrNode
|
4
|
+
attr_reader :name, :value, :name_pos
|
5
|
+
attr_accessor :tag_node
|
6
|
+
|
7
|
+
def initialize(name, value, name_pos)
|
8
|
+
@name = name
|
9
|
+
@value = value
|
10
|
+
@name_pos = name_pos
|
11
|
+
end
|
12
|
+
|
13
|
+
def accept(visitor)
|
14
|
+
visitor.visit_attr(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ruby_code?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rux
|
2
|
+
module AST
|
3
|
+
class AttrsNode
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :attrs, :pos
|
7
|
+
|
8
|
+
def initialize(attrs, pos)
|
9
|
+
@attrs = attrs
|
10
|
+
@pos = pos
|
11
|
+
end
|
12
|
+
|
13
|
+
def accept(visitor)
|
14
|
+
visitor.visit_attrs(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def each(&block)
|
18
|
+
attrs.each(&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def empty?
|
22
|
+
attrs.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(name)
|
26
|
+
find { |attr| attr.name == name }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Rux
|
2
|
+
module AST
|
3
|
+
class RubyAttrNode
|
4
|
+
attr_reader :ruby_node
|
5
|
+
attr_accessor :tag_node
|
6
|
+
|
7
|
+
def initialize(ruby_node)
|
8
|
+
@ruby_node = ruby_node
|
9
|
+
end
|
10
|
+
|
11
|
+
def code
|
12
|
+
ruby_node.code
|
13
|
+
end
|
14
|
+
|
15
|
+
def accept(visitor)
|
16
|
+
visitor.visit_attr(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ruby_code?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def name
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/rux/ast/string_node.rb
CHANGED
data/lib/rux/ast/tag_node.rb
CHANGED
@@ -3,9 +3,10 @@ module Rux
|
|
3
3
|
class TagNode
|
4
4
|
attr_reader :name, :attrs, :children
|
5
5
|
|
6
|
-
def initialize(name, attrs)
|
6
|
+
def initialize(name, attrs, pos)
|
7
7
|
@name = name
|
8
8
|
@attrs = attrs
|
9
|
+
@pos = pos
|
9
10
|
@children = []
|
10
11
|
end
|
11
12
|
|
@@ -16,6 +17,14 @@ module Rux
|
|
16
17
|
def component?
|
17
18
|
name.start_with?(/[A-Z]/)
|
18
19
|
end
|
20
|
+
|
21
|
+
def slot_component?
|
22
|
+
name.start_with?("With")
|
23
|
+
end
|
24
|
+
|
25
|
+
def slot_method
|
26
|
+
@slot_method ||= name.gsub(/(?<!^)([A-Z])/) { |x| "_#{x}" }.downcase
|
27
|
+
end
|
19
28
|
end
|
20
29
|
end
|
21
30
|
end
|
data/lib/rux/ast/text_node.rb
CHANGED
data/lib/rux/ast.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
module Rux
|
2
2
|
module AST
|
3
|
-
autoload :
|
4
|
-
autoload :
|
5
|
-
autoload :
|
6
|
-
autoload :
|
7
|
-
autoload :
|
3
|
+
autoload :AttrNode, 'rux/ast/attr_node'
|
4
|
+
autoload :AttrsNode, 'rux/ast/attrs_node'
|
5
|
+
autoload :FragmentNode, 'rux/ast/fragment_node'
|
6
|
+
autoload :ListNode, 'rux/ast/list_node'
|
7
|
+
autoload :RootNode, 'rux/ast/root_node'
|
8
|
+
autoload :RubyAttrNode, 'rux/ast/ruby_attr_node'
|
9
|
+
autoload :RubyNode, 'rux/ast/ruby_node'
|
10
|
+
autoload :StringNode, 'rux/ast/string_node'
|
11
|
+
autoload :TagNode, 'rux/ast/tag_node'
|
12
|
+
autoload :TextNode, 'rux/ast/text_node'
|
8
13
|
end
|
9
14
|
end
|
data/lib/rux/buffer.rb
CHANGED
@@ -1,15 +1,33 @@
|
|
1
1
|
module Rux
|
2
|
+
class SafeString < String
|
3
|
+
def html_safe?
|
4
|
+
true
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
2
8
|
class Buffer
|
3
9
|
def initialize(init_str = '')
|
4
10
|
@string = init_str.dup
|
5
11
|
end
|
6
12
|
|
7
|
-
def
|
8
|
-
|
13
|
+
def append(obj)
|
14
|
+
Array(obj).each do |o|
|
15
|
+
@string << if o.respond_to?(:html_safe?) && o.html_safe?
|
16
|
+
o.to_s
|
17
|
+
else
|
18
|
+
CGI.escapeHTML(o.to_s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def safe_append(obj)
|
24
|
+
Array(obj).each { |o| @string << o.to_s }
|
9
25
|
end
|
10
26
|
|
11
27
|
def to_s
|
12
|
-
@string
|
28
|
+
SafeString.new(@string)
|
13
29
|
end
|
30
|
+
|
31
|
+
alias html_safe to_s
|
14
32
|
end
|
15
33
|
end
|
@@ -1,19 +1,28 @@
|
|
1
1
|
module Rux
|
2
2
|
class DefaultTagBuilder
|
3
|
-
def call(tag_name, attributes = {})
|
4
|
-
|
5
|
-
"<#{tag_name}#{attr_str}>" <<
|
6
|
-
(block_given? ? Array(yield) : []).join <<
|
7
|
-
"</#{tag_name}>"
|
3
|
+
def call(tag_name, attributes = {}, &block)
|
4
|
+
SafeString.new(build(tag_name, attributes, &block))
|
8
5
|
end
|
9
6
|
|
10
7
|
private
|
11
8
|
|
9
|
+
def build(tag_name, attributes = {})
|
10
|
+
attr_str = attributes.empty? ? '' : " #{serialize_attrs(attributes)}"
|
11
|
+
|
12
|
+
"<#{tag_name}#{attr_str}>".tap do |result|
|
13
|
+
if block_given?
|
14
|
+
Array(yield).each { |body| result << body }
|
15
|
+
end
|
16
|
+
|
17
|
+
result << "</#{tag_name}>"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
12
21
|
def serialize_attrs(attributes)
|
13
22
|
''.tap do |result|
|
14
23
|
attributes.each_pair.with_index do |(k, v), idx|
|
15
24
|
result << ' ' unless idx == 0
|
16
|
-
result << "#{k}=\"#{
|
25
|
+
result << "#{k}=\"#{v.to_s.gsub('"', """)}\""
|
17
26
|
end
|
18
27
|
end
|
19
28
|
end
|
data/lib/rux/default_visitor.rb
CHANGED
@@ -2,8 +2,18 @@ require 'cgi'
|
|
2
2
|
|
3
3
|
module Rux
|
4
4
|
class DefaultVisitor < Visitor
|
5
|
+
def initialize
|
6
|
+
@render_stack = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def visit_root(node)
|
10
|
+
visit_list(node.list)
|
11
|
+
end
|
12
|
+
|
5
13
|
def visit_list(node)
|
6
|
-
|
14
|
+
''.tap do |result|
|
15
|
+
node.children.each { |child| result << visit(child) }
|
16
|
+
end
|
7
17
|
end
|
8
18
|
|
9
19
|
def visit_ruby(node)
|
@@ -11,57 +21,121 @@ module Rux
|
|
11
21
|
end
|
12
22
|
|
13
23
|
def visit_string(node)
|
14
|
-
node.
|
24
|
+
case node.quote_type
|
25
|
+
when :single
|
26
|
+
"'#{node.str}'"
|
27
|
+
else
|
28
|
+
"\"#{node.str}\""
|
29
|
+
end
|
15
30
|
end
|
16
31
|
|
17
32
|
def visit_tag(node)
|
18
33
|
''.tap do |result|
|
19
|
-
block_arg = if (as = node.attrs
|
34
|
+
block_arg = if (as = node.attrs.get('as'))
|
20
35
|
visit(as)
|
21
36
|
end
|
22
37
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
38
|
+
block_arg ||= "rux_block_arg#{@render_stack.size}"
|
39
|
+
|
40
|
+
if node.slot_component?
|
41
|
+
result << "(#{parent_render[:block_arg]}.#{node.slot_method}"
|
27
42
|
|
28
|
-
|
43
|
+
unless node.attrs.empty?
|
44
|
+
result << "(#{visit(node.attrs)})"
|
45
|
+
end
|
46
|
+
elsif node.component?
|
29
47
|
result << "render(#{node.name}.new"
|
30
48
|
|
31
49
|
unless node.attrs.empty?
|
32
|
-
result << "(#{
|
50
|
+
result << "(#{visit(node.attrs)})"
|
33
51
|
end
|
52
|
+
|
53
|
+
result << ')'
|
34
54
|
else
|
35
55
|
result << "Rux.tag('#{node.name}'"
|
36
56
|
|
37
57
|
unless node.attrs.empty?
|
38
|
-
result << ", { #{
|
58
|
+
result << ", { #{visit(node.attrs)} }"
|
39
59
|
end
|
60
|
+
|
61
|
+
result << ')'
|
40
62
|
end
|
41
63
|
|
42
|
-
|
64
|
+
@render_stack.push({
|
65
|
+
component_name: node.name,
|
66
|
+
block_arg: block_arg
|
67
|
+
})
|
43
68
|
|
44
|
-
if node.children.size >
|
69
|
+
if node.children.size > 0
|
45
70
|
result << " { "
|
46
|
-
result << "|#{block_arg}| " if block_arg
|
71
|
+
result << "|#{block_arg}| " if block_arg && node.component?
|
47
72
|
result << "Rux.create_buffer.tap { |_rux_buf_| "
|
48
73
|
|
49
74
|
node.children.each do |child|
|
50
|
-
result <<
|
75
|
+
result << append_statement_for(child)
|
51
76
|
end
|
52
77
|
|
53
78
|
result << " }.to_s }"
|
54
|
-
elsif node.children.size == 1
|
55
|
-
result << ' { '
|
56
|
-
result << "|#{block_arg}| " if block_arg
|
57
|
-
result << visit(node.children.first).strip
|
58
|
-
result << ' }'
|
59
79
|
end
|
80
|
+
|
81
|
+
# don't pass instances of ViewComponent::Slot to _rux_buf_#<< by wrapping
|
82
|
+
# the slot setter return value in (retval; nil)
|
83
|
+
if node.slot_component?
|
84
|
+
result << "; nil)"
|
85
|
+
end
|
86
|
+
|
87
|
+
@render_stack.pop
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def append_statement_for(node)
|
92
|
+
if node.is_a?(AST::TextNode)
|
93
|
+
"_rux_buf_.safe_append(#{visit(node).strip});"
|
94
|
+
else
|
95
|
+
"_rux_buf_.append(#{visit(node).strip});"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def visit_attrs(node)
|
100
|
+
visited_attrs = node.attrs.each_with_object([]) do |attr, memo|
|
101
|
+
memo << visit(attr) unless attr.name == "as"
|
102
|
+
end
|
103
|
+
|
104
|
+
visited_attrs.join(", ")
|
105
|
+
end
|
106
|
+
|
107
|
+
def visit_attr(node)
|
108
|
+
if node.ruby_code?
|
109
|
+
node.code
|
110
|
+
else
|
111
|
+
Utils.attr_to_hash_elem(
|
112
|
+
node.name,
|
113
|
+
visit(node.value),
|
114
|
+
slugify: node.tag_node.component?
|
115
|
+
)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def visit_fragment(node)
|
120
|
+
''.tap do |result|
|
121
|
+
result << "Rux.create_buffer.tap { |_rux_buf_| "
|
122
|
+
|
123
|
+
node.children.each do |child|
|
124
|
+
result << append_statement_for(child)
|
125
|
+
end
|
126
|
+
|
127
|
+
result << " }.to_s;"
|
60
128
|
end
|
61
129
|
end
|
62
130
|
|
63
131
|
def visit_text(node)
|
64
132
|
"\"#{CGI.escape_html(node.text)}\""
|
65
133
|
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def parent_render
|
138
|
+
@render_stack.last
|
139
|
+
end
|
66
140
|
end
|
67
141
|
end
|
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,,
|