rbexy 1.1.0 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -1
- data/README.md +18 -0
- data/lib/rbexy.rb +26 -9
- data/lib/rbexy/ast_transformer.rb +21 -0
- data/lib/rbexy/component.rb +1 -6
- data/lib/rbexy/component_resolver.rb +60 -0
- data/lib/rbexy/configuration.rb +18 -1
- data/lib/rbexy/lexer.rb +20 -13
- data/lib/rbexy/nodes.rb +15 -135
- data/lib/rbexy/nodes/abstract_attr.rb +12 -0
- data/lib/rbexy/nodes/abstract_element.rb +13 -0
- data/lib/rbexy/nodes/abstract_node.rb +58 -0
- data/lib/rbexy/nodes/component_element.rb +69 -0
- data/lib/rbexy/nodes/component_prop.rb +29 -0
- data/lib/rbexy/nodes/declaration.rb +15 -0
- data/lib/rbexy/nodes/expression.rb +15 -0
- data/lib/rbexy/nodes/expression_group.rb +55 -0
- data/lib/rbexy/nodes/html_attr.rb +13 -0
- data/lib/rbexy/nodes/html_element.rb +48 -0
- data/lib/rbexy/nodes/newline.rb +9 -0
- data/lib/rbexy/nodes/raw.rb +23 -0
- data/lib/rbexy/nodes/root.rb +19 -0
- data/lib/rbexy/nodes/text.rb +15 -0
- data/lib/rbexy/nodes/util.rb +9 -0
- data/lib/rbexy/parser.rb +22 -16
- data/lib/rbexy/rails/component_template_resolver.rb +3 -3
- data/lib/rbexy/rails/controller_helper.rb +0 -6
- data/lib/rbexy/rails/engine.rb +1 -7
- data/lib/rbexy/refinements.rb +5 -0
- data/lib/rbexy/refinements/array.rb +9 -0
- data/lib/rbexy/refinements/array/find_map.rb +13 -0
- data/lib/rbexy/refinements/array/insert_between_types.rb +26 -0
- data/lib/rbexy/refinements/array/map_type_when_neighboring_type.rb +26 -0
- data/lib/rbexy/runtime.rb +15 -23
- data/lib/rbexy/template.rb +12 -0
- data/lib/rbexy/version.rb +1 -1
- data/rbexy.gemspec +1 -0
- metadata +41 -11
- data/example.rb +0 -113
- data/lib/rbexy/component_providers/namespaced_rbexy_provider.rb +0 -20
- data/lib/rbexy/component_providers/rbexy_provider.rb +0 -21
- data/lib/rbexy/component_providers/view_component_provider.rb +0 -21
- data/lib/rbexy/component_tag_builder.rb +0 -19
- data/lib/rbexy/hash_mash.rb +0 -15
- data/lib/rbexy/view_context_helper.rb +0 -19
@@ -0,0 +1,58 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Nodes
|
3
|
+
class AbstractNode
|
4
|
+
PrecompileRequired = Class.new(StandardError)
|
5
|
+
|
6
|
+
attr_reader :compile_context
|
7
|
+
|
8
|
+
def precompile
|
9
|
+
[self]
|
10
|
+
end
|
11
|
+
|
12
|
+
def compile
|
13
|
+
raise PrecompileRequired, "#{self.class.name} must be precompiled first"
|
14
|
+
end
|
15
|
+
|
16
|
+
def inject_compile_context(context)
|
17
|
+
@compile_context = context
|
18
|
+
children.each { |c| c.inject_compile_context(context) } if respond_to?(:children)
|
19
|
+
members.each { |c| c.inject_compile_context(context) } if respond_to?(:members)
|
20
|
+
end
|
21
|
+
|
22
|
+
def transform!
|
23
|
+
return unless ast_transformer
|
24
|
+
ast_transformer.transform(self, compile_context)
|
25
|
+
children.each(&:transform!) if respond_to?(:children)
|
26
|
+
members.each(&:transform!) if respond_to?(:members)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def ast_transformer
|
32
|
+
compile_context.ast_transformer
|
33
|
+
end
|
34
|
+
|
35
|
+
def compact(nodes)
|
36
|
+
compacted = []
|
37
|
+
curr_raw = nil
|
38
|
+
|
39
|
+
nodes.each do |node|
|
40
|
+
if node.is_a?(Newline) && curr_raw
|
41
|
+
curr_raw.merge(Raw.new("\n"))
|
42
|
+
elsif node.is_a?(Raw)
|
43
|
+
if !curr_raw
|
44
|
+
curr_raw ||= Raw.new("")
|
45
|
+
compacted << curr_raw
|
46
|
+
end
|
47
|
+
curr_raw.merge(node)
|
48
|
+
else
|
49
|
+
curr_raw = nil
|
50
|
+
compacted << node
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
compacted
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Nodes
|
3
|
+
class ComponentElement < AbstractElement
|
4
|
+
attr_reader :template
|
5
|
+
|
6
|
+
OUTPUT = "@output_buffer.safe_concat(%s);"
|
7
|
+
EXPR_STRING = "%s.html_safe"
|
8
|
+
|
9
|
+
def initialize(*args, template: OUTPUT)
|
10
|
+
super(*args)
|
11
|
+
@template = template
|
12
|
+
end
|
13
|
+
|
14
|
+
def precompile
|
15
|
+
[ComponentElement.new(name, precompile_members, precompile_children)]
|
16
|
+
end
|
17
|
+
|
18
|
+
def compile
|
19
|
+
templates = Rbexy.configuration.component_rendering_templates
|
20
|
+
|
21
|
+
tag = templates[:component] % {
|
22
|
+
component_class: name,
|
23
|
+
view_context: "self",
|
24
|
+
kwargs: compile_members,
|
25
|
+
children_block: children.any? ? templates[:children] % { children: children.map(&:compile).join } : ""
|
26
|
+
}
|
27
|
+
|
28
|
+
if Rbexy.configuration.enable_context
|
29
|
+
tag = "(rbexy_context.push({});#{tag}.tap{rbexy_context.pop})"
|
30
|
+
end
|
31
|
+
|
32
|
+
template % tag
|
33
|
+
end
|
34
|
+
|
35
|
+
def compile_members
|
36
|
+
members.each_with_object("") do |member, result|
|
37
|
+
case member
|
38
|
+
when ExpressionGroup
|
39
|
+
result << "**#{member.compile},"
|
40
|
+
when Newline
|
41
|
+
result << member.compile
|
42
|
+
else
|
43
|
+
result << "#{member.compile},"
|
44
|
+
end
|
45
|
+
end.gsub(/,\z/, "")
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def precompile_members
|
51
|
+
members.map do |node|
|
52
|
+
if node.is_a? ExpressionGroup
|
53
|
+
ExpressionGroup.new(
|
54
|
+
node.members,
|
55
|
+
inner_template: ExpressionGroup::SUB_EXPR,
|
56
|
+
outer_template: ExpressionGroup::SUB_EXPR
|
57
|
+
)
|
58
|
+
else
|
59
|
+
node
|
60
|
+
end
|
61
|
+
end.map(&:precompile).flatten
|
62
|
+
end
|
63
|
+
|
64
|
+
def precompile_children
|
65
|
+
compact(children.map(&:precompile).flatten)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Nodes
|
3
|
+
class ComponentProp < AbstractAttr
|
4
|
+
def precompile
|
5
|
+
[ComponentProp.new(name, precompile_value)]
|
6
|
+
end
|
7
|
+
|
8
|
+
def compile
|
9
|
+
key = ActiveSupport::Inflector.underscore(name)
|
10
|
+
"#{key}: #{value.compile}"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def precompile_value
|
16
|
+
node = value.precompile.first
|
17
|
+
|
18
|
+
case node
|
19
|
+
when Raw
|
20
|
+
Raw.new(node.content, template: Raw::EXPR_STRING)
|
21
|
+
when ExpressionGroup
|
22
|
+
ExpressionGroup.new(node.members, outer_template: ExpressionGroup::SUB_EXPR, inner_template: node.inner_template)
|
23
|
+
else
|
24
|
+
node
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Nodes
|
3
|
+
class ExpressionGroup < AbstractNode
|
4
|
+
using Rbexy::Refinements::Array::MapTypeWhenNeighboringType
|
5
|
+
using Rbexy::Refinements::Array::InsertBetweenTypes
|
6
|
+
|
7
|
+
attr_accessor :members
|
8
|
+
attr_reader :outer_template, :inner_template
|
9
|
+
|
10
|
+
OUTPUT_UNSAFE = "@output_buffer.concat(Rbexy::Runtime.expr_out(%s));"
|
11
|
+
OUTPUT_SAFE = "@output_buffer.safe_concat(Rbexy::Runtime.expr_out(%s));"
|
12
|
+
SUB_EXPR = "%s"
|
13
|
+
SUB_EXPR_OUT = "Rbexy::Runtime.expr_out(%s)"
|
14
|
+
|
15
|
+
def initialize(members, outer_template: OUTPUT_UNSAFE, inner_template: "%s")
|
16
|
+
@members = members
|
17
|
+
@outer_template = outer_template
|
18
|
+
@inner_template = inner_template
|
19
|
+
end
|
20
|
+
|
21
|
+
def precompile
|
22
|
+
[ExpressionGroup.new(precompile_members, outer_template: outer_template, inner_template: inner_template)]
|
23
|
+
end
|
24
|
+
|
25
|
+
def compile
|
26
|
+
outer_template % (inner_template % members.map(&:compile).join)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def precompile_members
|
32
|
+
precompiled = compact(members.map(&:precompile).flatten)
|
33
|
+
|
34
|
+
transformed = precompiled.map do |node|
|
35
|
+
case node
|
36
|
+
when Raw
|
37
|
+
Raw.new(node.content, template: Raw::EXPR_STRING)
|
38
|
+
when ComponentElement
|
39
|
+
ComponentElement.new(node.name, node.members, node.children, template: ComponentElement::EXPR_STRING)
|
40
|
+
when ExpressionGroup
|
41
|
+
ExpressionGroup.new(node.members, outer_template: SUB_EXPR, inner_template: node.inner_template)
|
42
|
+
else
|
43
|
+
node
|
44
|
+
end
|
45
|
+
end.map_type_when_neighboring_type(ExpressionGroup, Raw) do |node|
|
46
|
+
ExpressionGroup.new(node.members, outer_template: SUB_EXPR_OUT, inner_template: node.inner_template)
|
47
|
+
end.insert_between_types(ExpressionGroup, Raw) do
|
48
|
+
Expression.new("+")
|
49
|
+
end.insert_between_types(ComponentElement, Raw) do
|
50
|
+
Expression.new("+")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Nodes
|
3
|
+
class HTMLElement < AbstractElement
|
4
|
+
KNOWN_VOID_ELEMENTS = ActionView::Helpers::TagHelper::TagBuilder::VOID_ELEMENTS.map(&:to_s).to_set
|
5
|
+
|
6
|
+
def precompile
|
7
|
+
nodes = []
|
8
|
+
|
9
|
+
if void? && children.length == 0
|
10
|
+
nodes.concat(precompile_open_tag)
|
11
|
+
else
|
12
|
+
nodes.concat(precompile_open_tag)
|
13
|
+
nodes.concat(children.map(&:precompile).flatten)
|
14
|
+
nodes << Raw.new("</#{name}>")
|
15
|
+
end
|
16
|
+
|
17
|
+
nodes
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def void?
|
23
|
+
KNOWN_VOID_ELEMENTS.include?(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def precompile_open_tag
|
27
|
+
nodes = [Raw.new("<#{name}")]
|
28
|
+
nodes.concat(precompile_members)
|
29
|
+
nodes << Raw.new(">")
|
30
|
+
nodes
|
31
|
+
end
|
32
|
+
|
33
|
+
def precompile_members
|
34
|
+
members.map do |node|
|
35
|
+
if node.is_a? ExpressionGroup
|
36
|
+
ExpressionGroup.new(
|
37
|
+
node.members,
|
38
|
+
inner_template: "Rbexy::Runtime.splat_attrs(%s)",
|
39
|
+
outer_template: ExpressionGroup::OUTPUT_SAFE
|
40
|
+
)
|
41
|
+
else
|
42
|
+
node
|
43
|
+
end
|
44
|
+
end.map(&:precompile).flatten
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Nodes
|
3
|
+
class Raw < AbstractNode
|
4
|
+
attr_reader :content, :template
|
5
|
+
|
6
|
+
OUTPUT = "@output_buffer.safe_concat('%s'.freeze);"
|
7
|
+
EXPR_STRING = "'%s'.html_safe.freeze"
|
8
|
+
|
9
|
+
def initialize(content, template: OUTPUT)
|
10
|
+
@content = content
|
11
|
+
@template = template
|
12
|
+
end
|
13
|
+
|
14
|
+
def compile
|
15
|
+
template % content
|
16
|
+
end
|
17
|
+
|
18
|
+
def merge(other_raw)
|
19
|
+
content << other_raw.content
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Nodes
|
3
|
+
class Root < AbstractNode
|
4
|
+
attr_accessor :children
|
5
|
+
|
6
|
+
def initialize(children)
|
7
|
+
@children = children
|
8
|
+
end
|
9
|
+
|
10
|
+
def precompile
|
11
|
+
Root.new(compact(children.map(&:precompile).flatten))
|
12
|
+
end
|
13
|
+
|
14
|
+
def compile
|
15
|
+
"#{children.map(&:compile).join}@output_buffer.to_s"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/rbexy/parser.rb
CHANGED
@@ -12,7 +12,7 @@ module Rbexy
|
|
12
12
|
|
13
13
|
def parse
|
14
14
|
validate_tokens!
|
15
|
-
Nodes::
|
15
|
+
Nodes::Root.new(parse_tokens)
|
16
16
|
end
|
17
17
|
|
18
18
|
def parse_tokens
|
@@ -26,7 +26,7 @@ module Rbexy
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def parse_token
|
29
|
-
parse_text ||
|
29
|
+
parse_text || parse_newline || parse_expression || parse_tag || parse_declaration
|
30
30
|
end
|
31
31
|
|
32
32
|
def parse_text
|
@@ -37,14 +37,14 @@ module Rbexy
|
|
37
37
|
def parse_expression
|
38
38
|
return unless take(:OPEN_EXPRESSION)
|
39
39
|
|
40
|
-
|
40
|
+
members = []
|
41
41
|
|
42
42
|
eventually!(:CLOSE_EXPRESSION)
|
43
43
|
until take(:CLOSE_EXPRESSION)
|
44
|
-
|
44
|
+
members << (parse_expression_body || parse_tag)
|
45
45
|
end
|
46
46
|
|
47
|
-
Nodes::ExpressionGroup.new(
|
47
|
+
Nodes::ExpressionGroup.new(members)
|
48
48
|
end
|
49
49
|
|
50
50
|
def parse_expression!
|
@@ -60,26 +60,32 @@ module Rbexy
|
|
60
60
|
def parse_tag
|
61
61
|
return unless take(:OPEN_TAG_DEF)
|
62
62
|
|
63
|
-
|
63
|
+
details = take!(:TAG_DETAILS)[1]
|
64
|
+
attr_class = details[:type] == :component ? Nodes::ComponentProp : Nodes::HTMLAttr
|
65
|
+
|
64
66
|
members = []
|
65
|
-
members.concat(take_all(:
|
66
|
-
members.concat(parse_attrs)
|
67
|
+
members.concat(take_all(:NEWLINE).map { Nodes::Newline.new })
|
68
|
+
members.concat(parse_attrs(attr_class))
|
67
69
|
|
68
70
|
take!(:CLOSE_TAG_DEF)
|
69
71
|
|
70
72
|
children = parse_children
|
71
73
|
|
72
|
-
|
74
|
+
if details[:type] == :component
|
75
|
+
Nodes::ComponentElement.new(details[:component_class], members, children)
|
76
|
+
else
|
77
|
+
Nodes::HTMLElement.new(details[:name], members, children)
|
78
|
+
end
|
73
79
|
end
|
74
80
|
|
75
|
-
def parse_attrs
|
81
|
+
def parse_attrs(attr_class)
|
76
82
|
return [] unless take(:OPEN_ATTRS)
|
77
83
|
|
78
84
|
attrs = []
|
79
85
|
|
80
86
|
eventually!(:CLOSE_ATTRS)
|
81
87
|
until take(:CLOSE_ATTRS)
|
82
|
-
attrs << (parse_splat_attr ||
|
88
|
+
attrs << (parse_splat_attr || parse_newline || parse_attr(attr_class))
|
83
89
|
end
|
84
90
|
|
85
91
|
attrs
|
@@ -94,12 +100,12 @@ module Rbexy
|
|
94
100
|
expression
|
95
101
|
end
|
96
102
|
|
97
|
-
def
|
98
|
-
return unless take(:
|
99
|
-
Nodes::
|
103
|
+
def parse_newline
|
104
|
+
return unless take(:NEWLINE)
|
105
|
+
Nodes::Newline.new
|
100
106
|
end
|
101
107
|
|
102
|
-
def parse_attr
|
108
|
+
def parse_attr(attr_class)
|
103
109
|
name = take!(:ATTR_NAME)[1]
|
104
110
|
value = nil
|
105
111
|
|
@@ -111,7 +117,7 @@ module Rbexy
|
|
111
117
|
value = default_empty_attr_value
|
112
118
|
end
|
113
119
|
|
114
|
-
|
120
|
+
attr_class.new(name, value)
|
115
121
|
end
|
116
122
|
|
117
123
|
def parse_children
|