rbexy 1.1.0 → 2.0.0.beta5
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -1
- data/README.md +19 -1
- data/lib/rbexy.rb +26 -9
- data/lib/rbexy/ast_transformer.rb +21 -0
- data/lib/rbexy/cache_component.rb +17 -0
- data/lib/rbexy/component.rb +35 -21
- data/lib/rbexy/component_resolver.rb +60 -0
- data/lib/rbexy/configuration.rb +18 -1
- data/lib/rbexy/lexer.rb +21 -14
- 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.rb +2 -0
- data/lib/rbexy/rails/component_template_resolver.rb +59 -4
- data/lib/rbexy/rails/controller_helper.rb +0 -6
- data/lib/rbexy/rails/engine.rb +3 -7
- data/lib/rbexy/rails/rbx_dependency_tracker.rb +37 -0
- 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 +43 -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,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
|
data/lib/rbexy/rails.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module Rbexy
|
2
2
|
autoload :Component, "rbexy/component"
|
3
|
+
autoload :CacheComponent, "rbexy/cache_component"
|
3
4
|
|
4
5
|
module Rails
|
5
6
|
autoload :Engine, "rbexy/rails/engine"
|
6
7
|
autoload :ControllerHelper, "rbexy/rails/controller_helper"
|
7
8
|
autoload :ComponentTemplateResolver, "rbexy/rails/component_template_resolver"
|
9
|
+
autoload :RbxDependencyTracker, "rbexy/rails/rbx_dependency_tracker"
|
8
10
|
end
|
9
11
|
end
|
@@ -1,8 +1,14 @@
|
|
1
|
-
require "
|
1
|
+
require "active_support/digest"
|
2
2
|
|
3
3
|
module Rbexy
|
4
4
|
module Rails
|
5
5
|
class ComponentTemplateResolver < ActionView::FileSystemResolver
|
6
|
+
COMMENT_SYNTAX = {
|
7
|
+
rbx: "# %s",
|
8
|
+
erb: "<%# %s %>",
|
9
|
+
html: "<!-- %s -->"
|
10
|
+
}
|
11
|
+
|
6
12
|
# Rails 6 requires us to override `_find_all` in order to hook
|
7
13
|
def _find_all(name, prefix, partial, details, key, locals)
|
8
14
|
find_templates(name, prefix, partial, details, locals)
|
@@ -14,22 +20,71 @@ module Rbexy
|
|
14
20
|
return [] unless name.is_a? Rbexy::Component::TemplatePath
|
15
21
|
|
16
22
|
templates_path = File.join(@path, prefix, name)
|
23
|
+
component_name = prefix.present? ? File.join(prefix, name) : name
|
24
|
+
virtual_path = Rbexy::Component::TemplatePath.new(component_name)
|
25
|
+
|
17
26
|
extensions = details[:handlers].join(",")
|
27
|
+
templates = find_rbx_templates(templates_path, extensions, component_name, virtual_path)
|
28
|
+
|
29
|
+
if templates.none?
|
30
|
+
templates = find_call_component_cachebuster_templates(templates_path, component_name, virtual_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
templates
|
34
|
+
end
|
18
35
|
|
36
|
+
def find_rbx_templates(templates_path, extensions, component_name, virtual_path)
|
19
37
|
Dir["#{templates_path}.*{#{extensions}}"].map do |template_path|
|
20
38
|
source = File.binread(template_path)
|
21
|
-
|
22
|
-
|
39
|
+
extension = File.extname(template_path)[1..-1]
|
40
|
+
handler = ActionView::Template.handler_for_extension(extension)
|
23
41
|
|
24
42
|
ActionView::Template.new(
|
25
|
-
source,
|
43
|
+
"#{source}#{component_class_cachebuster(component_name, extension)}",
|
26
44
|
template_path,
|
27
45
|
handler,
|
46
|
+
format: extension.to_sym,
|
28
47
|
locals: [],
|
29
48
|
virtual_path: virtual_path
|
30
49
|
)
|
31
50
|
end
|
32
51
|
end
|
52
|
+
|
53
|
+
def find_call_component_cachebuster_templates(templates_path, component_name, virtual_path)
|
54
|
+
component_class = find_component_class(component_name)
|
55
|
+
return [] unless component_class && component_class.call_component?
|
56
|
+
|
57
|
+
[
|
58
|
+
ActionView::Template.new(
|
59
|
+
cachebuster_digest_as_comment(component_class.component_file_location, :rbx),
|
60
|
+
"#{templates_path}.rbexycall",
|
61
|
+
ActionView::Template.handler_for_extension(:rbx),
|
62
|
+
format: :rbx,
|
63
|
+
locals: [],
|
64
|
+
virtual_path: virtual_path
|
65
|
+
)
|
66
|
+
]
|
67
|
+
end
|
68
|
+
|
69
|
+
def component_class_cachebuster(component_name, template_format)
|
70
|
+
component_class = find_component_class(component_name)
|
71
|
+
return unless component_class
|
72
|
+
|
73
|
+
cachebuster_digest_as_comment(component_class.component_file_location, template_format)
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_component_class(component_name)
|
77
|
+
Rbexy::ComponentResolver.try_constantize { component_name.classify.constantize }
|
78
|
+
end
|
79
|
+
|
80
|
+
def cachebuster_digest_as_comment(filename, format)
|
81
|
+
source = File.binread(filename)
|
82
|
+
digest = ActiveSupport::Digest.hexdigest(source)
|
83
|
+
|
84
|
+
comment_template = COMMENT_SYNTAX[format.to_sym] || COMMENT_SYNTAX[:html]
|
85
|
+
comment = comment_template % digest
|
86
|
+
"\n#{comment}"
|
87
|
+
end
|
33
88
|
end
|
34
89
|
end
|
35
90
|
end
|
@@ -1,16 +1,10 @@
|
|
1
|
-
require "active_support/concern"
|
2
|
-
|
3
1
|
module Rbexy
|
4
2
|
module Rails
|
5
3
|
module ControllerHelper
|
6
4
|
extend ActiveSupport::Concern
|
7
5
|
include ComponentContext
|
8
6
|
|
9
|
-
def rbexy_component_provider; end
|
10
|
-
|
11
7
|
included do
|
12
|
-
helper ViewContextHelper
|
13
|
-
helper_method :rbexy_component_provider
|
14
8
|
helper_method :rbexy_context, :create_context, :use_context
|
15
9
|
end
|
16
10
|
|
data/lib/rbexy/rails/engine.rb
CHANGED
@@ -1,24 +1,20 @@
|
|
1
1
|
require "rbexy/rails"
|
2
|
+
require "action_view/dependency_tracker"
|
2
3
|
|
3
4
|
module Rbexy
|
4
5
|
module Rails
|
5
6
|
class Engine < ::Rails::Engine
|
6
7
|
initializer "rbexy" do |app|
|
7
|
-
template_handler = proc { |template, source| Rbexy.compile(source) }
|
8
|
+
template_handler = proc { |template, source| Rbexy.compile(Rbexy::Template.new(source, template.identifier)) }
|
8
9
|
|
9
10
|
ActionView::Template.register_template_handler(:rbx, template_handler)
|
11
|
+
ActionView::DependencyTracker.register_tracker(:rbx, RbxDependencyTracker)
|
10
12
|
|
11
13
|
ActiveSupport.on_load :action_controller_base do
|
12
14
|
include ControllerHelper
|
13
15
|
end
|
14
16
|
|
15
|
-
if defined?(ViewComponent)
|
16
|
-
ViewComponent::Base.include ViewContextHelper
|
17
|
-
end
|
18
|
-
|
19
17
|
Rbexy.configure do |config|
|
20
|
-
require "rbexy/component_providers/rbexy_provider"
|
21
|
-
config.component_provider = ComponentProviders::RbexyProvider.new
|
22
18
|
config.template_paths << ::Rails.root.join("app", "components")
|
23
19
|
config.enable_context = true
|
24
20
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Rails
|
3
|
+
class RbxDependencyTracker
|
4
|
+
def self.supports_view_paths?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.call(name, template, view_paths = nil)
|
9
|
+
new(name, template, view_paths).dependencies
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(name, template, view_paths = nil)
|
13
|
+
@name, @template, @view_paths = name, template, view_paths
|
14
|
+
end
|
15
|
+
|
16
|
+
def dependencies
|
17
|
+
rails_render_helper_dependencies + rbexy_dependencies
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :name, :template, :view_paths
|
23
|
+
|
24
|
+
def rails_render_helper_dependencies
|
25
|
+
ActionView::DependencyTracker::ERBTracker.call(name, template, view_paths)
|
26
|
+
end
|
27
|
+
|
28
|
+
def rbexy_dependencies
|
29
|
+
Lexer.new(template, Rbexy.configuration.element_resolver).tokenize
|
30
|
+
.select { |t| t[0] == :TAG_DETAILS && t[1][:type] == :component }
|
31
|
+
.map { |t| t[1][:component_class] }
|
32
|
+
.uniq
|
33
|
+
.map(&:template_path)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Refinements
|
3
|
+
module Array
|
4
|
+
autoload :FindMap, "rbexy/refinements/array/find_map"
|
5
|
+
autoload :InsertBetweenTypes, "rbexy/refinements/array/insert_between_types"
|
6
|
+
autoload :MapTypeWhenNeighboringType, "rbexy/refinements/array/map_type_when_neighboring_type"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Refinements
|
3
|
+
module Array
|
4
|
+
module InsertBetweenTypes
|
5
|
+
refine ::Array do
|
6
|
+
def insert_between_types(type1, type2, &block)
|
7
|
+
map.with_index do |curr, i|
|
8
|
+
prev_i = i - 1
|
9
|
+
|
10
|
+
if prev_i >= 0 && InsertBetweenTypes.one_of_each_type?([self[prev_i], curr], [type1, type2])
|
11
|
+
[block.call, curr]
|
12
|
+
else
|
13
|
+
[curr]
|
14
|
+
end
|
15
|
+
end.flatten
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.one_of_each_type?(items_pair, types_pair)
|
20
|
+
items_pair[0].is_a?(types_pair[0]) && items_pair[1].is_a?(types_pair[1]) ||
|
21
|
+
items_pair[0].is_a?(types_pair[1]) && items_pair[1].is_a?(types_pair[0])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Rbexy
|
2
|
+
module Refinements
|
3
|
+
module Array
|
4
|
+
module MapTypeWhenNeighboringType
|
5
|
+
refine ::Array do
|
6
|
+
def map_type_when_neighboring_type(map_type, neighboring_type, &block)
|
7
|
+
map.with_index do |curr, i|
|
8
|
+
prev_i = i - 1
|
9
|
+
next_i = i + 1
|
10
|
+
|
11
|
+
if !curr.is_a?(map_type)
|
12
|
+
curr
|
13
|
+
elsif prev_i >= 0 && self[prev_i].is_a?(neighboring_type)
|
14
|
+
block.call(curr)
|
15
|
+
elsif next_i < length && self[next_i].is_a?(neighboring_type)
|
16
|
+
block.call(curr)
|
17
|
+
else
|
18
|
+
curr
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|