rbexy 1.0.2 → 2.0.0.beta4

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -1
  3. data/README.md +18 -0
  4. data/lib/rbexy.rb +27 -9
  5. data/lib/rbexy/ast_transformer.rb +21 -0
  6. data/lib/rbexy/component.rb +10 -18
  7. data/lib/rbexy/component_context.rb +18 -0
  8. data/lib/rbexy/component_resolver.rb +60 -0
  9. data/lib/rbexy/configuration.rb +18 -1
  10. data/lib/rbexy/lexer.rb +21 -14
  11. data/lib/rbexy/nodes.rb +15 -135
  12. data/lib/rbexy/nodes/abstract_attr.rb +12 -0
  13. data/lib/rbexy/nodes/abstract_element.rb +13 -0
  14. data/lib/rbexy/nodes/abstract_node.rb +58 -0
  15. data/lib/rbexy/nodes/component_element.rb +69 -0
  16. data/lib/rbexy/nodes/component_prop.rb +29 -0
  17. data/lib/rbexy/nodes/declaration.rb +15 -0
  18. data/lib/rbexy/nodes/expression.rb +15 -0
  19. data/lib/rbexy/nodes/expression_group.rb +55 -0
  20. data/lib/rbexy/nodes/html_attr.rb +13 -0
  21. data/lib/rbexy/nodes/html_element.rb +48 -0
  22. data/lib/rbexy/nodes/newline.rb +9 -0
  23. data/lib/rbexy/nodes/raw.rb +23 -0
  24. data/lib/rbexy/nodes/root.rb +19 -0
  25. data/lib/rbexy/nodes/text.rb +15 -0
  26. data/lib/rbexy/nodes/util.rb +9 -0
  27. data/lib/rbexy/parser.rb +22 -16
  28. data/lib/rbexy/rails/component_template_resolver.rb +3 -3
  29. data/lib/rbexy/rails/controller_helper.rb +5 -4
  30. data/lib/rbexy/rails/engine.rb +1 -11
  31. data/lib/rbexy/refinements.rb +5 -0
  32. data/lib/rbexy/refinements/array.rb +9 -0
  33. data/lib/rbexy/refinements/array/find_map.rb +13 -0
  34. data/lib/rbexy/refinements/array/insert_between_types.rb +26 -0
  35. data/lib/rbexy/refinements/array/map_type_when_neighboring_type.rb +26 -0
  36. data/lib/rbexy/runtime.rb +16 -23
  37. data/lib/rbexy/template.rb +12 -0
  38. data/lib/rbexy/version.rb +1 -1
  39. data/rbexy.gemspec +1 -0
  40. metadata +42 -11
  41. data/example.rb +0 -113
  42. data/lib/rbexy/component_providers/namespaced_rbexy_provider.rb +0 -20
  43. data/lib/rbexy/component_providers/rbexy_provider.rb +0 -21
  44. data/lib/rbexy/component_providers/view_component_provider.rb +0 -21
  45. data/lib/rbexy/component_tag_builder.rb +0 -19
  46. data/lib/rbexy/hash_mash.rb +0 -15
  47. data/lib/rbexy/view_context_helper.rb +0 -23
@@ -0,0 +1,12 @@
1
+ module Rbexy
2
+ module Nodes
3
+ class AbstractAttr < AbstractNode
4
+ attr_accessor :name, :value
5
+
6
+ def initialize(name, value)
7
+ @name = name
8
+ @value = value
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module Rbexy
2
+ module Nodes
3
+ class AbstractElement < AbstractNode
4
+ attr_accessor :name, :members, :children
5
+
6
+ def initialize(name, members, children)
7
+ @name = name
8
+ @members = members || []
9
+ @children = children
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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}.transform_keys { |k| ActiveSupport::Inflector.underscore(k).to_sym },"
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,15 @@
1
+ module Rbexy
2
+ module Nodes
3
+ class Declaration < AbstractNode
4
+ attr_accessor :content
5
+
6
+ def initialize(content)
7
+ @content = content
8
+ end
9
+
10
+ def precompile
11
+ [Raw.new(Util.escape_string(content))]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Rbexy
2
+ module Nodes
3
+ class Expression < AbstractNode
4
+ attr_accessor :content
5
+
6
+ def initialize(content)
7
+ @content = content
8
+ end
9
+
10
+ def compile
11
+ content
12
+ end
13
+ end
14
+ end
15
+ 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,13 @@
1
+ module Rbexy
2
+ module Nodes
3
+ class HTMLAttr < AbstractAttr
4
+ def precompile
5
+ [
6
+ Raw.new(" #{name}=\""),
7
+ value.precompile,
8
+ Raw.new("\"")
9
+ ].flatten
10
+ end
11
+ end
12
+ end
13
+ 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,9 @@
1
+ module Rbexy
2
+ module Nodes
3
+ class Newline < AbstractNode
4
+ def compile
5
+ "\n"
6
+ end
7
+ end
8
+ end
9
+ 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
@@ -0,0 +1,15 @@
1
+ module Rbexy
2
+ module Nodes
3
+ class Text < AbstractNode
4
+ attr_accessor :content
5
+
6
+ def initialize(content)
7
+ @content = content
8
+ end
9
+
10
+ def precompile
11
+ [Raw.new(Util.escape_string(content))]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module Rbexy
2
+ module Nodes
3
+ module Util
4
+ def self.escape_string(str)
5
+ str.gsub('"', '\\"').gsub("'", "\\'")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -12,7 +12,7 @@ module Rbexy
12
12
 
13
13
  def parse
14
14
  validate_tokens!
15
- Nodes::Template.new(parse_tokens)
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 || parse_silent_newline || parse_expression || parse_tag || parse_declaration
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
- statements = []
40
+ members = []
41
41
 
42
42
  eventually!(:CLOSE_EXPRESSION)
43
43
  until take(:CLOSE_EXPRESSION)
44
- statements << (parse_expression_body || parse_tag)
44
+ members << (parse_expression_body || parse_tag)
45
45
  end
46
46
 
47
- Nodes::ExpressionGroup.new(statements)
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
- name = take!(:TAG_NAME)
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(:SILENT_NEWLINE).map { Nodes::SilentNewline.new })
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
- Nodes::XmlNode.new(name[1], members, children)
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 || parse_silent_newline || parse_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 parse_silent_newline
98
- return unless take(:SILENT_NEWLINE)
99
- Nodes::SilentNewline.new
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
- Nodes::XmlAttr.new(name, value)
120
+ attr_class.new(name, value)
115
121
  end
116
122
 
117
123
  def parse_children