compex 0.1.1 → 0.1.2
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/lib/compex/asset_provider.rb +10 -13
- data/lib/compex/bag.rb +4 -3
- data/lib/compex/base.rb +23 -134
- data/lib/compex/cache/memory_backend.rb +5 -5
- data/lib/compex/cache.rb +8 -57
- data/lib/compex/component_descriptor.rb +119 -0
- data/lib/compex/component_registry.rb +47 -1
- data/lib/compex/config.rb +14 -25
- data/lib/compex/js_rewriter.rb +1 -1
- data/lib/compex/refinements/class_refinements.rb +21 -0
- data/lib/compex/{string_refinements.rb → refinements/string_refinements.rb} +9 -0
- data/lib/compex/style.rb +14 -24
- data/lib/compex/template/assembler.rb +6 -4
- data/lib/compex/template/renderer.rb +16 -5
- data/lib/compex/template.rb +2 -0
- data/lib/compex/version.rb +1 -1
- data/lib/compex.rb +4 -1
- metadata +6 -8
- data/lib/compex/cache/base.rb +0 -10
- data/lib/compex/cache/memcached_backend.rb +0 -19
- data/lib/compex/cache/noop_backend.rb +0 -10
- data/lib/compex/cache/redis_backend.rb +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b7cf8ba1fdb3025ced5fbd9ac131254f434f8c5b0fb9051db7ad99ecebbcff5
|
|
4
|
+
data.tar.gz: 5c999e47eecd15154c8bf51bf537fb579f970d73c19b3649e5f2e0752b195f53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4282c8050c62201e2171969458e13facac1d9e8a02fe376531a671ddecefdd14a8c1d2a4137e9a9ae129fc5ce4b4dbfc5453b57a8f0af05ab4ac90eb67e2ebf5
|
|
7
|
+
data.tar.gz: 59055e79cc4caae458af50b83c296e959c3700b8c8538750494adc6dd9e8cd02b2cbab72dcbac5b0735456bca2e922aa4a8af2e52db3481fb7f5ea7a21232fee
|
|
@@ -2,21 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module CompEx
|
|
4
4
|
class AssetProvider
|
|
5
|
-
def self.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
script = Cache.get_hashed_script(hashed[2...])
|
|
11
|
-
return nil unless script
|
|
5
|
+
def self.register(hash, value)
|
|
6
|
+
@assets ||= {}
|
|
7
|
+
CompEx::Cache.cache_asset(hash, value)
|
|
8
|
+
@assets[hash] = value
|
|
9
|
+
end
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return nil unless style
|
|
11
|
+
def self.reset_cache!
|
|
12
|
+
@assets = {}
|
|
13
|
+
end
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
def self.provide(hashed)
|
|
16
|
+
@assets[hashed] ||= CompEx::Cache.get_asset(hashed)
|
|
20
17
|
end
|
|
21
18
|
end
|
|
22
19
|
end
|
data/lib/compex/bag.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module CompEx
|
|
4
4
|
class Bag
|
|
5
5
|
def initialize(parent, **args)
|
|
6
|
-
parent.
|
|
6
|
+
parent.args.each do |arg|
|
|
7
7
|
next unless args.key? arg
|
|
8
8
|
|
|
9
9
|
define_singleton_method(arg) { args[arg] }
|
|
@@ -12,8 +12,9 @@ module CompEx
|
|
|
12
12
|
@_extra_js_hashes = []
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
|
|
15
|
+
def required_hashes
|
|
16
|
+
[@_extra_css_hashes, @_extra_js_hashes].compact.flatten
|
|
17
|
+
end
|
|
17
18
|
|
|
18
19
|
def render(klass, **args)
|
|
19
20
|
inst = klass.new(**args)
|
data/lib/compex/base.rb
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module CompEx
|
|
4
4
|
class Base
|
|
5
|
+
using CompEx::StringRefinements
|
|
6
|
+
using CompEx::ClassRefinements
|
|
7
|
+
|
|
5
8
|
class << self
|
|
9
|
+
def descriptor
|
|
10
|
+
@descriptor ||= ComponentDescriptor.new(self)
|
|
11
|
+
end
|
|
12
|
+
|
|
6
13
|
def args(first, *rest)
|
|
7
14
|
[first, *rest].each { arg(it) }
|
|
8
15
|
self
|
|
@@ -15,8 +22,7 @@ module CompEx
|
|
|
15
22
|
define_method("#{name}=") do |value|
|
|
16
23
|
instance_variable_set(var_name, UnsafeString.new(value))
|
|
17
24
|
end
|
|
18
|
-
|
|
19
|
-
@args << name
|
|
25
|
+
descriptor.args.add(name)
|
|
20
26
|
|
|
21
27
|
return self if rest.empty?
|
|
22
28
|
|
|
@@ -26,131 +32,61 @@ module CompEx
|
|
|
26
32
|
end
|
|
27
33
|
|
|
28
34
|
def expose(first, *rest)
|
|
29
|
-
|
|
30
|
-
@exposed << first
|
|
31
|
-
@exposed.append(*rest)
|
|
32
|
-
@exposed.uniq!
|
|
35
|
+
descriptor.exposed.add(first, *rest)
|
|
33
36
|
nil
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
def html(source)
|
|
37
|
-
|
|
40
|
+
descriptor.raw_html = source
|
|
38
41
|
self
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
def style(source)
|
|
42
|
-
|
|
45
|
+
descriptor.raw_style = source
|
|
43
46
|
self
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
def js(source)
|
|
47
|
-
|
|
50
|
+
descriptor.raw_js = source
|
|
48
51
|
self
|
|
49
52
|
end
|
|
50
53
|
|
|
51
|
-
def raw_component_id
|
|
52
|
-
@raw_component_id ||= name || hash.to_s(36)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def component_id
|
|
56
|
-
@component_id ||= hash_string(raw_component_id)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
54
|
def js_class_id
|
|
60
55
|
return @js_class_id if @js_class_id
|
|
61
56
|
|
|
62
57
|
class_id = name.split("::").map do |id|
|
|
63
58
|
next id unless id.start_with? "#"
|
|
64
59
|
|
|
65
|
-
|
|
60
|
+
id.hashed
|
|
66
61
|
end.join
|
|
67
62
|
@js_class_id ||= ["CompEx_", class_id, "_", component_id.upcase].join
|
|
68
63
|
end
|
|
69
64
|
|
|
70
|
-
def hash_string(str)
|
|
71
|
-
value = 2906
|
|
72
|
-
str.chars.map(&:ord).each do |v|
|
|
73
|
-
value = (value * 33) ^ v
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
(value & 0xFFFFFFFF).to_s(36)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
65
|
def inherited(subclass)
|
|
80
66
|
super
|
|
81
67
|
CompEx::ComponentRegistry.register(subclass)
|
|
82
68
|
end
|
|
83
|
-
|
|
84
|
-
def stable_class_id(klass)
|
|
85
|
-
klass.name || Digest::SHA256.hexdigest(klass.inspect)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def defined_args
|
|
89
|
-
@args || []
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
using CompEx::StringRefinements
|
|
94
|
-
|
|
95
|
-
def html
|
|
96
|
-
@html ||= Cache.html(self.class) do
|
|
97
|
-
Template.parse(self.class.instance_variable_get(:@html) || find_template, self.class)
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def style
|
|
102
|
-
self.class.instance_variable_get(:@style) || find_style
|
|
103
69
|
end
|
|
104
70
|
|
|
105
|
-
|
|
71
|
+
def descriptor = self.class.descriptor
|
|
106
72
|
|
|
107
73
|
def initialize(**args)
|
|
108
|
-
@bag = Bag.new(
|
|
74
|
+
@bag = Bag.new(descriptor, **args)
|
|
109
75
|
@extra_hashes = []
|
|
110
|
-
|
|
111
|
-
initialize_css
|
|
112
|
-
initialize_js
|
|
113
76
|
end
|
|
114
77
|
|
|
115
|
-
def exposed = self.class.
|
|
78
|
+
def exposed = self.class.descriptor.exposed
|
|
116
79
|
|
|
117
|
-
def
|
|
118
|
-
css_id, css_source, css_hash = Cache.style(self.class) do
|
|
119
|
-
id, css = Style.compile(self, html.root_classes)
|
|
120
|
-
# Keeping define_css_prefix here, so we only define it once.
|
|
121
|
-
html.define_css_prefix(id)
|
|
122
|
-
css_hash = Digest::SHA1.hexdigest(css)
|
|
123
|
-
[id, css, "c_#{css_hash}"]
|
|
124
|
-
end
|
|
125
|
-
@css_id = css_id
|
|
126
|
-
@css = css_source
|
|
127
|
-
@css_hash = css_hash
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def initialize_js
|
|
131
|
-
js = self.class.instance_variable_get(:@js) || find_js
|
|
132
|
-
return unless js
|
|
133
|
-
|
|
134
|
-
hash, src = Cache.script(self.class) do
|
|
135
|
-
js_hash = Digest::SHA1.hexdigest(js)
|
|
136
|
-
# Keeping define_js_module here, so we only define it once.
|
|
137
|
-
html.define_js_module(self.class.js_class_id)
|
|
138
|
-
["j_#{js_hash}", JSRewriter.rewrite(self.class, js)]
|
|
139
|
-
end
|
|
140
|
-
@js = src
|
|
141
|
-
@js_hash = hash
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def defined_args = self.class.defined_args
|
|
80
|
+
def defined_args = self.class.descriptor.args
|
|
145
81
|
|
|
146
|
-
def render_html
|
|
147
|
-
html.render do |v|
|
|
82
|
+
def render_html(children: nil)
|
|
83
|
+
self.class.descriptor.html.render do |v|
|
|
148
84
|
case v
|
|
85
|
+
when :slot
|
|
86
|
+
children
|
|
149
87
|
when Hash
|
|
150
88
|
inst = v[:component].new(**v[:args])
|
|
151
|
-
ret = inst.render_html
|
|
152
|
-
v[:children]
|
|
153
|
-
end
|
|
89
|
+
ret = inst.render_html(children: v[:children])
|
|
154
90
|
@extra_hashes << inst.required_hashes
|
|
155
91
|
ret
|
|
156
92
|
|
|
@@ -163,57 +99,10 @@ module CompEx
|
|
|
163
99
|
end
|
|
164
100
|
|
|
165
101
|
def required_hashes
|
|
166
|
-
[
|
|
102
|
+
[descriptor.required_hashes, @bag.required_hashes, @extra_hashes]
|
|
167
103
|
.flatten
|
|
168
104
|
.compact
|
|
169
105
|
.uniq
|
|
170
106
|
end
|
|
171
|
-
|
|
172
|
-
def component_name
|
|
173
|
-
return nil if self.class.name.nil?
|
|
174
|
-
|
|
175
|
-
@component_name ||= self.class.name.split("::").map do |item|
|
|
176
|
-
next item if item[0] == "#"
|
|
177
|
-
|
|
178
|
-
item.underscore
|
|
179
|
-
end.join("/")
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
private
|
|
183
|
-
|
|
184
|
-
def find_template
|
|
185
|
-
path = CompEx.config.lookup_template(self, :html)
|
|
186
|
-
begin
|
|
187
|
-
File.read(path)
|
|
188
|
-
rescue Errno::ENOENT
|
|
189
|
-
raise "No template defined by #{self.class.name} (through ::html), nor could be found at #{path}"
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def find_style
|
|
194
|
-
cname = component_name
|
|
195
|
-
return nil unless cname
|
|
196
|
-
|
|
197
|
-
path = CompEx.config.lookup_template(self, :style)
|
|
198
|
-
begin
|
|
199
|
-
style = File.read(path)
|
|
200
|
-
rescue Errno::ENOENT
|
|
201
|
-
return nil
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
Style.compile(style)
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
def find_js
|
|
208
|
-
cname = component_name
|
|
209
|
-
return nil unless cname
|
|
210
|
-
|
|
211
|
-
path = CompEx.config.lookup_template(self, :js)
|
|
212
|
-
begin
|
|
213
|
-
File.read(path)
|
|
214
|
-
rescue Errno::ENOENT
|
|
215
|
-
nil
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
107
|
end
|
|
219
108
|
end
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module CompEx
|
|
4
4
|
class Cache
|
|
5
|
-
class MemoryBackend
|
|
6
|
-
def
|
|
7
|
-
@
|
|
5
|
+
class MemoryBackend
|
|
6
|
+
def initialize
|
|
7
|
+
@cache = {}
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def set(key, value)
|
|
11
|
-
@
|
|
11
|
+
@cache[key] = value
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def get(key) = @keys[
|
|
14
|
+
def get(key) = @keys[key]
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
end
|
data/lib/compex/cache.rb
CHANGED
|
@@ -1,73 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "cache/base"
|
|
4
|
-
require_relative "cache/memcached_backend"
|
|
5
3
|
require_relative "cache/memory_backend"
|
|
6
|
-
require_relative "cache/noop_backend"
|
|
7
|
-
require_relative "cache/redis_backend"
|
|
8
4
|
|
|
9
5
|
module CompEx
|
|
10
6
|
class Cache
|
|
7
|
+
using ClassRefinements
|
|
8
|
+
|
|
11
9
|
class << self
|
|
12
10
|
def backend = CompEx.config.cache_backend
|
|
13
|
-
def stable_class_id(comp) = CompEx::Base.stable_class_id(comp)
|
|
14
|
-
|
|
15
|
-
def style(component_class)
|
|
16
|
-
stable_id = stable_class_id(component_class)
|
|
17
|
-
stable_key = "style:#{stable_id}"
|
|
18
|
-
v = backend.get(stable_key)
|
|
19
|
-
return JSON.parse(v, symbolize_names: true) if v
|
|
20
11
|
|
|
21
|
-
|
|
22
|
-
backend.set(
|
|
23
|
-
backend.set(stable_key, r.to_json)
|
|
24
|
-
|
|
25
|
-
r
|
|
12
|
+
def cache_asset(key, value)
|
|
13
|
+
backend.set(prefix(key), value)
|
|
26
14
|
end
|
|
27
15
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
return nil unless ptr
|
|
31
|
-
|
|
32
|
-
v = backend.get("style:#{ptr}")
|
|
33
|
-
return nil unless v
|
|
34
|
-
|
|
35
|
-
JSON.parse(v, symbolize_names: true)
|
|
16
|
+
def get_asset(key)
|
|
17
|
+
backend.get(prefix(key))
|
|
36
18
|
end
|
|
37
19
|
|
|
38
|
-
def
|
|
39
|
-
|
|
40
|
-
stable_key = "html:#{stable_id}"
|
|
41
|
-
v = backend.get(stable_key)
|
|
42
|
-
return v if v
|
|
43
|
-
|
|
44
|
-
html = yield
|
|
45
|
-
backend.set(stable_key, html)
|
|
46
|
-
|
|
47
|
-
html
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def script(component_class)
|
|
51
|
-
stable_id = stable_class_id(component_class)
|
|
52
|
-
stable_key = "script:#{stable_id}"
|
|
53
|
-
v = backend.get(stable_key)
|
|
54
|
-
return JSON.parse(v, symbolize_names: true) if v
|
|
55
|
-
|
|
56
|
-
r = yield
|
|
57
|
-
backend.set(stable_key, r.to_json)
|
|
58
|
-
backend.set("script:#{r.first[2...]}", stable_id)
|
|
59
|
-
|
|
60
|
-
r
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def get_hashed_script(hashed)
|
|
64
|
-
ptr = backend.get("script:#{hashed}")
|
|
65
|
-
return nil unless ptr
|
|
66
|
-
|
|
67
|
-
v = backend.get("script:#{ptr}")
|
|
68
|
-
return nil unless v
|
|
69
|
-
|
|
70
|
-
JSON.parse(v, symbolize_names: true)
|
|
20
|
+
def prefix(key)
|
|
21
|
+
"#{CompEx.config.cache_prefix}#{key}"
|
|
71
22
|
end
|
|
72
23
|
end
|
|
73
24
|
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CompEx
|
|
4
|
+
class ComponentDescriptor
|
|
5
|
+
using CompEx::ClassRefinements
|
|
6
|
+
using CompEx::StringRefinements
|
|
7
|
+
|
|
8
|
+
attr_accessor :raw_style
|
|
9
|
+
attr_reader :args, :exposed, :css, :css_hash, :css_prefix
|
|
10
|
+
attr_writer :raw_html, :raw_js
|
|
11
|
+
|
|
12
|
+
def initialize(parent)
|
|
13
|
+
@parent = parent
|
|
14
|
+
@args = Set.new
|
|
15
|
+
@exposed = Set.new
|
|
16
|
+
@css_prefix = "cx_#{@parent.component_id}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def html
|
|
20
|
+
prepare!
|
|
21
|
+
@html
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def has_js? = !@js.nil?
|
|
25
|
+
def has_style? = !@css.nil?
|
|
26
|
+
|
|
27
|
+
def prepare!
|
|
28
|
+
return if @prepared
|
|
29
|
+
|
|
30
|
+
# prime cached templates
|
|
31
|
+
js_template
|
|
32
|
+
css_template
|
|
33
|
+
|
|
34
|
+
prepare_html
|
|
35
|
+
prepare_js
|
|
36
|
+
prepare_css
|
|
37
|
+
update_html_attrs
|
|
38
|
+
|
|
39
|
+
@prepared = true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def required_hashes
|
|
43
|
+
[@css_hash, @js_hash].compact
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def js_class_id
|
|
47
|
+
return @js_class_id if @js_class_id
|
|
48
|
+
|
|
49
|
+
id = @parent.name.split("::")
|
|
50
|
+
.reject { it.start_with?("#") }
|
|
51
|
+
.map(&:hashed).join
|
|
52
|
+
@js_class_id = "CompEx_#{id}_#{@parent.component_id.upcase}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def dynamically_defined?
|
|
56
|
+
@dynamically_defined ||= @parent.name.include?("#")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def find_template(kind)
|
|
62
|
+
if kind == :html && dynamically_defined?
|
|
63
|
+
# This is an error already. #find should only be called in case
|
|
64
|
+
# raw_html isn't called. Bail early.
|
|
65
|
+
raise MissingTemplateError, @parent
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
path = CompEx.config.lookup_template(@parent, kind)
|
|
69
|
+
if path.nil?
|
|
70
|
+
raise MissingTemplateError, @parent if kind == :html
|
|
71
|
+
|
|
72
|
+
return nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
begin
|
|
76
|
+
File.read(path)
|
|
77
|
+
rescue Errno::ENOENT
|
|
78
|
+
return nil unless kind == :html
|
|
79
|
+
|
|
80
|
+
raise MissingTemplateError, @parent
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def prepare_js
|
|
85
|
+
return unless @js_template
|
|
86
|
+
|
|
87
|
+
js_hash = Digest::SHA1.hexdigest(@js_template)
|
|
88
|
+
@js = JSRewriter.rewrite(@parent, @js_template)
|
|
89
|
+
@js_hash = "j_#{js_hash}"
|
|
90
|
+
CompEx::AssetProvider.register(@js_hash, @js)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def prepare_html
|
|
94
|
+
@html_template ||= @raw_html || find_template(:html)
|
|
95
|
+
@html = Template.parse(@html_template, @parent)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def prepare_css
|
|
99
|
+
return unless css_template
|
|
100
|
+
|
|
101
|
+
@css = Style.compile(@parent, css_template, @html.root_classes)
|
|
102
|
+
@css_hash = "c_#{Digest::SHA1.hexdigest(@css)}"
|
|
103
|
+
CompEx::AssetProvider.register(@css_hash, @css)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def update_html_attrs
|
|
107
|
+
@html.define_css_prefix(css_prefix) if @css
|
|
108
|
+
@html.define_js_module(js_class_id) if @js
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def js_template
|
|
112
|
+
@js_template ||= @raw_js || find_template(:js)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def css_template
|
|
116
|
+
@css_template ||= @raw_style || find_template(:style)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module CompEx
|
|
4
4
|
class ComponentRegistry
|
|
5
|
+
using ClassRefinements
|
|
6
|
+
|
|
5
7
|
Node = Struct.new(:name, :children, :component) do
|
|
6
8
|
def initialize(*)
|
|
7
9
|
super
|
|
@@ -12,11 +14,16 @@ module CompEx
|
|
|
12
14
|
class << self
|
|
13
15
|
attr_reader :components, :tree
|
|
14
16
|
|
|
17
|
+
def flush!
|
|
18
|
+
@components = {}
|
|
19
|
+
@tree = Struct.new(:children).new(children: [])
|
|
20
|
+
end
|
|
21
|
+
|
|
15
22
|
def register(component)
|
|
16
23
|
@components ||= {}
|
|
17
24
|
@tree ||= Struct.new(:children).new(children: [])
|
|
18
25
|
|
|
19
|
-
stable_id =
|
|
26
|
+
stable_id = component.stable_class_id
|
|
20
27
|
@components[stable_id] = component
|
|
21
28
|
|
|
22
29
|
names = stable_id.split("::")
|
|
@@ -47,6 +54,7 @@ module CompEx
|
|
|
47
54
|
end
|
|
48
55
|
|
|
49
56
|
def resolve(name, context: nil)
|
|
57
|
+
puts "resolve #{name}, #{context.inspect}"
|
|
50
58
|
return nil if name.nil? || name.empty?
|
|
51
59
|
|
|
52
60
|
# Normalize
|
|
@@ -76,6 +84,44 @@ module CompEx
|
|
|
76
84
|
node = find_by_path(path, @tree)
|
|
77
85
|
node&.component
|
|
78
86
|
end
|
|
87
|
+
|
|
88
|
+
def walk(base, &)
|
|
89
|
+
base.children.each do |v|
|
|
90
|
+
if v.respond_to?(:component) && !v.component.nil?
|
|
91
|
+
yield v.component.descriptor
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
walk(v, &) if v.children && v.children.length > 0
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def gather_scripts
|
|
99
|
+
scripts = []
|
|
100
|
+
walk(@tree) do |comp|
|
|
101
|
+
next unless comp.has_js?
|
|
102
|
+
scripts << {
|
|
103
|
+
component: comp,
|
|
104
|
+
source: comp.js,
|
|
105
|
+
hash: comp.js_hash
|
|
106
|
+
}
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
scripts
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def gather_styles
|
|
113
|
+
styles = []
|
|
114
|
+
walk(@tree) do |comp|
|
|
115
|
+
next unless comp.has_style?
|
|
116
|
+
styles << {
|
|
117
|
+
component: comp,
|
|
118
|
+
source: comp.css,
|
|
119
|
+
hash: comp.css_hash
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
styles
|
|
124
|
+
end
|
|
79
125
|
end
|
|
80
126
|
end
|
|
81
127
|
end
|
data/lib/compex/config.rb
CHANGED
|
@@ -16,56 +16,45 @@ module CompEx
|
|
|
16
16
|
def config = configure
|
|
17
17
|
|
|
18
18
|
class Config
|
|
19
|
-
|
|
20
|
-
:on_non_element, :cache_args, :cache_kwargs, :cache_prefix,
|
|
21
|
-
:template_search_path, :js_search_path, :style_search_path
|
|
22
|
-
attr_reader :cache_mode, :cache_backend
|
|
19
|
+
using CompEx::ClassRefinements
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
memory: Cache::MemoryBackend,
|
|
28
|
-
disabled: Cache::NoopBackend
|
|
29
|
-
}.freeze
|
|
21
|
+
attr_accessor :on_multiple_root, :multiple_root_wrap_element,
|
|
22
|
+
:on_non_element, :template_search_path, :js_search_path,
|
|
23
|
+
:style_search_path, :cache_backend, :cache_prefix
|
|
30
24
|
|
|
31
25
|
def initialize
|
|
32
26
|
@on_multiple_root = :wrap
|
|
33
27
|
@multiple_root_wrap_element = "div"
|
|
34
28
|
@on_non_element = :wrap
|
|
35
|
-
self.
|
|
29
|
+
self.cache_backend = Cache::MemoryBackend.new
|
|
30
|
+
self.cache_prefix = "compex.cache"
|
|
36
31
|
|
|
37
|
-
return unless defined?(Rails)
|
|
32
|
+
return unless defined?(::Rails)
|
|
38
33
|
|
|
39
|
-
root_path = File.join(Rails.root, "app", "views", "components")
|
|
34
|
+
root_path = File.join(::Rails.root, "app", "views", "components")
|
|
40
35
|
@template_search_path = root_path
|
|
41
36
|
@style_search_path = root_path
|
|
42
37
|
@js_search_path = root_path
|
|
43
38
|
end
|
|
44
39
|
|
|
45
|
-
def cache_mode=(value)
|
|
46
|
-
value = value.to_sym if value.is_a? String
|
|
47
|
-
raise ArgumentError, "Unknown cache mode #{value}, valid options are #{CACHE_MODES.keys.map(&:to_s).join(", ")}" unless CACHE_MODES.key? value
|
|
48
|
-
|
|
49
|
-
@cache_backend = CACHE_MODES[value].new
|
|
50
|
-
@cache_backend.prepare!(*cache_args, **cache_kwargs)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
40
|
def lookup_template(component, type)
|
|
54
41
|
@lookup_cache ||= {}
|
|
55
42
|
lookup_key = [component, type]
|
|
56
43
|
return @lookup_cache[lookup_key] if @lookup_cache.key? lookup_key
|
|
57
44
|
|
|
58
|
-
|
|
59
|
-
raise
|
|
45
|
+
dynamically_defined = component.descriptor.dynamically_defined?
|
|
46
|
+
raise MissingTemplateError, "No template defined by anonymous class #{self.class} (through ::#{type})" if dynamically_defined && type == :html
|
|
60
47
|
|
|
61
48
|
# Anonymous classes can't be looked up since we can't define its path
|
|
62
|
-
|
|
49
|
+
if dynamically_defined
|
|
63
50
|
@lookup_cache[lookup_key] = nil
|
|
64
51
|
return nil
|
|
65
52
|
end
|
|
66
53
|
|
|
54
|
+
cname = component.name
|
|
55
|
+
|
|
67
56
|
ret = case type
|
|
68
|
-
when :
|
|
57
|
+
when :html
|
|
69
58
|
File.join(@template_search_path || "", "#{cname}.html.crb")
|
|
70
59
|
when :style
|
|
71
60
|
File.join(@style_search_path || "", "#{cname}.css")
|
data/lib/compex/js_rewriter.rb
CHANGED
|
@@ -5,7 +5,7 @@ module CompEx
|
|
|
5
5
|
def self.rewrite(component, raw_js)
|
|
6
6
|
return nil unless raw_js
|
|
7
7
|
|
|
8
|
-
class_name = component.js_class_id
|
|
8
|
+
class_name = component.descriptor.js_class_id
|
|
9
9
|
source = raw_js.strip.gsub(/\Aclass\s+[^\s]+\s*{/, "").gsub(/}\z/, "")
|
|
10
10
|
<<~JS
|
|
11
11
|
((Runtime, Component) => {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CompEx
|
|
4
|
+
module ClassRefinements
|
|
5
|
+
refine Class do
|
|
6
|
+
using CompEx::StringRefinements
|
|
7
|
+
|
|
8
|
+
def stable_class_id
|
|
9
|
+
@stable_class_id ||= name || Digest::SHA256.hexdigest(inspect)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def raw_component_id
|
|
13
|
+
@raw_component_id ||= name || hash.to_s(36)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def component_id
|
|
17
|
+
@component_id ||= raw_component_id.hashed
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/compex/style.rb
CHANGED
|
@@ -2,35 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
module CompEx
|
|
4
4
|
class Style
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
component_prefix(component_class),
|
|
8
|
-
new(component_class, root_classes).compile
|
|
9
|
-
]
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def self.component_prefix(component) = "cx_#{hash_string(comp_id(component))}"
|
|
13
|
-
|
|
14
|
-
def self.comp_id(comp) = comp.class.name || comp.class.hash.to_s(36)
|
|
5
|
+
using CompEx::StringRefinements
|
|
6
|
+
using CompEx::ClassRefinements
|
|
15
7
|
|
|
16
|
-
def self.
|
|
17
|
-
|
|
18
|
-
str.chars.map(&:ord).each do |v|
|
|
19
|
-
value = (value * 33) ^ v
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
(value & 0xFFFFFFFF).to_s(36)
|
|
8
|
+
def self.compile(component_class, source, root_classes = nil)
|
|
9
|
+
new(component_class, source, root_classes).compile
|
|
23
10
|
end
|
|
24
11
|
|
|
25
|
-
def initialize(klass, root_classes = nil)
|
|
26
|
-
@
|
|
27
|
-
@style = klass.style
|
|
12
|
+
def initialize(klass, source, root_classes = nil)
|
|
13
|
+
@style = source
|
|
28
14
|
@ast = MiniCSS.parse(@style)
|
|
29
15
|
@root_classes = root_classes || []
|
|
16
|
+
@component_prefix = klass.descriptor.css_prefix
|
|
30
17
|
end
|
|
31
18
|
|
|
32
|
-
def id_for_component = self.class.component_prefix(@comp)
|
|
33
|
-
|
|
34
19
|
def compile
|
|
35
20
|
@ast.each do |v|
|
|
36
21
|
case v
|
|
@@ -61,28 +46,33 @@ module CompEx
|
|
|
61
46
|
{
|
|
62
47
|
type: :complex,
|
|
63
48
|
combinator: @root_classes.include?(sel[:content].gsub(/^\./, "")) ? "" : " ",
|
|
64
|
-
left: { type: :class, content: ".#{
|
|
49
|
+
left: { type: :class, content: ".#{@component_prefix}" },
|
|
65
50
|
right: sel
|
|
66
51
|
}
|
|
52
|
+
|
|
67
53
|
when :type, :attribute, :universal, :id
|
|
68
54
|
{
|
|
69
55
|
type: :complex,
|
|
70
56
|
combinator: " ",
|
|
71
|
-
left: { type: :class, content: ".#{
|
|
57
|
+
left: { type: :class, content: ".#{@component_prefix}" },
|
|
72
58
|
right: sel
|
|
73
59
|
}
|
|
60
|
+
|
|
74
61
|
when :list
|
|
75
62
|
sel.tap do |s|
|
|
76
63
|
s[:list].map! { prefix_selector(it) }
|
|
77
64
|
end
|
|
65
|
+
|
|
78
66
|
when :complex
|
|
79
67
|
sel.tap do |s|
|
|
80
68
|
s[:left] = prefix_selector(s[:left])
|
|
81
69
|
end
|
|
70
|
+
|
|
82
71
|
when :compound
|
|
83
72
|
sel.tap do |s|
|
|
84
73
|
s[:list][0] = prefix_selector(s[:list].first)
|
|
85
74
|
end
|
|
75
|
+
|
|
86
76
|
else
|
|
87
77
|
raise "Unexpected selector type #{sel[:type]}"
|
|
88
78
|
end
|
|
@@ -102,9 +102,10 @@ module CompEx
|
|
|
102
102
|
|
|
103
103
|
when MiniHTML::AST::Executable
|
|
104
104
|
push_literal
|
|
105
|
-
id = @embedding_id
|
|
105
|
+
id = "__cx_embedding_quoted_#{@context_id}_#{@embedding_id}__"
|
|
106
106
|
@embedding_id += 1
|
|
107
|
-
@
|
|
107
|
+
@embeddings[id] = at.value.source
|
|
108
|
+
@result << { kind: :executable, id: id, source: at.value.source }
|
|
108
109
|
|
|
109
110
|
when MiniHTML::AST::Interpolation
|
|
110
111
|
assemble_interpolation(at.value)
|
|
@@ -135,9 +136,10 @@ module CompEx
|
|
|
135
136
|
@literal << v.literal
|
|
136
137
|
when MiniHTML::AST::Executable
|
|
137
138
|
push_literal
|
|
138
|
-
id = @embedding_id
|
|
139
|
+
id = "__cx_embedding_#{@context_id}_#{@embedding_id}__"
|
|
139
140
|
@embedding_id += 1
|
|
140
|
-
@
|
|
141
|
+
@embeddings[id] = v.source
|
|
142
|
+
@result << { kind: :executable, id: id, source: v.source }
|
|
141
143
|
end
|
|
142
144
|
end
|
|
143
145
|
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module CompEx
|
|
4
4
|
class Template
|
|
5
5
|
class Renderer
|
|
6
|
+
using CompEx::ClassRefinements
|
|
7
|
+
|
|
6
8
|
# The following attributes will have their values merged when we
|
|
7
9
|
# find the same tag already defined by an embedded component
|
|
8
10
|
MERGEABLE_ATTRIBUTES = Set.new(%w[class])
|
|
@@ -61,7 +63,7 @@ module CompEx
|
|
|
61
63
|
c_node = CompEx::ComponentRegistry.resolve(inner_name, context: @context)
|
|
62
64
|
next unless c_node
|
|
63
65
|
|
|
64
|
-
args = c_node.
|
|
66
|
+
args = c_node.descriptor.args.map(&:to_s)
|
|
65
67
|
component_props = node.attributes.slice(*args)
|
|
66
68
|
component_props.each_key { node.remove_attribute(it) }
|
|
67
69
|
child = node.children
|
|
@@ -121,13 +123,22 @@ module CompEx
|
|
|
121
123
|
embed.replace(result)
|
|
122
124
|
end
|
|
123
125
|
|
|
126
|
+
slot = dom.at_css("slot")
|
|
127
|
+
|
|
128
|
+
# WARNING: The safe navigation operator below WILL PREVENT yield when
|
|
129
|
+
# slot is nil.
|
|
130
|
+
slot&.replace((yield :slot) || "")
|
|
131
|
+
|
|
124
132
|
dom.to_html
|
|
125
|
-
.gsub(/__cx_embedding_#{@context_id}_\d+__/) {
|
|
126
|
-
.gsub(/__cx_embedding_quoted_#{@context_id}_\d+__/) {
|
|
133
|
+
.gsub(/__cx_embedding_#{@context_id}_\d+__/) { process_embedding(it, false, &) }
|
|
134
|
+
.gsub(/__cx_embedding_quoted_#{@context_id}_\d+__/) { process_embedding(it, true, &) }
|
|
127
135
|
end
|
|
128
136
|
|
|
129
|
-
def
|
|
130
|
-
|
|
137
|
+
def process_embedding(key, quoted)
|
|
138
|
+
unless @embeddings.key? key
|
|
139
|
+
warn "[CompEx] BUG: Embedding not found: #{key} @ #{@context}"
|
|
140
|
+
return key
|
|
141
|
+
end
|
|
131
142
|
|
|
132
143
|
ret = yield @embeddings[key]
|
|
133
144
|
ret = ret.inspect if quoted
|
data/lib/compex/template.rb
CHANGED
data/lib/compex/version.rb
CHANGED
data/lib/compex.rb
CHANGED
|
@@ -9,8 +9,10 @@ require "minicss"
|
|
|
9
9
|
require "minihtml"
|
|
10
10
|
|
|
11
11
|
require_relative "compex/version"
|
|
12
|
+
require_relative "compex/refinements/string_refinements"
|
|
13
|
+
require_relative "compex/refinements/class_refinements"
|
|
14
|
+
require_relative "compex/component_descriptor"
|
|
12
15
|
require_relative "compex/template"
|
|
13
|
-
require_relative "compex/string_refinements"
|
|
14
16
|
require_relative "compex/cache"
|
|
15
17
|
require_relative "compex/style"
|
|
16
18
|
require_relative "compex/bag"
|
|
@@ -33,6 +35,7 @@ module CompEx
|
|
|
33
35
|
end
|
|
34
36
|
end
|
|
35
37
|
|
|
38
|
+
class MissingTemplateError < Error; end
|
|
36
39
|
class MultipleRootError < Error; end
|
|
37
40
|
class NonElementRootError < Error; end
|
|
38
41
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: compex
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vito Sartori
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2025-10-23 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: minicss
|
|
@@ -74,15 +74,13 @@ files:
|
|
|
74
74
|
- lib/compex/bag.rb
|
|
75
75
|
- lib/compex/base.rb
|
|
76
76
|
- lib/compex/cache.rb
|
|
77
|
-
- lib/compex/cache/base.rb
|
|
78
|
-
- lib/compex/cache/memcached_backend.rb
|
|
79
77
|
- lib/compex/cache/memory_backend.rb
|
|
80
|
-
- lib/compex/
|
|
81
|
-
- lib/compex/cache/redis_backend.rb
|
|
78
|
+
- lib/compex/component_descriptor.rb
|
|
82
79
|
- lib/compex/component_registry.rb
|
|
83
80
|
- lib/compex/config.rb
|
|
84
81
|
- lib/compex/js_rewriter.rb
|
|
85
|
-
- lib/compex/
|
|
82
|
+
- lib/compex/refinements/class_refinements.rb
|
|
83
|
+
- lib/compex/refinements/string_refinements.rb
|
|
86
84
|
- lib/compex/style.rb
|
|
87
85
|
- lib/compex/template.rb
|
|
88
86
|
- lib/compex/template/assembler.rb
|
|
@@ -110,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
110
108
|
- !ruby/object:Gem::Version
|
|
111
109
|
version: '0'
|
|
112
110
|
requirements: []
|
|
113
|
-
rubygems_version: 3.6.
|
|
111
|
+
rubygems_version: 3.6.2
|
|
114
112
|
specification_version: 4
|
|
115
113
|
summary: View Component Extensions for Ruby
|
|
116
114
|
test_files: []
|
data/lib/compex/cache/base.rb
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CompEx
|
|
4
|
-
class Cache
|
|
5
|
-
class MemcachedBackend
|
|
6
|
-
def prepare!(*, **)
|
|
7
|
-
begin
|
|
8
|
-
require "memcached"
|
|
9
|
-
rescue LoadError
|
|
10
|
-
raise "CompEx: Memcached cache mode requires the gem 'memcached' to be installed."
|
|
11
|
-
end
|
|
12
|
-
@instance = Memcached::Client.new(*, **)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def set(key, value) = @instance.set(prefix(key), value)
|
|
16
|
-
def get(key) = @instance.get(prefix(key))
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CompEx
|
|
4
|
-
class Cache
|
|
5
|
-
class RedisBackend < Base
|
|
6
|
-
def prepare!(*, **)
|
|
7
|
-
begin
|
|
8
|
-
require "redis"
|
|
9
|
-
rescue LoadError
|
|
10
|
-
raise "CompEx: Redis cache mode requires the gem 'redis' to be installed."
|
|
11
|
-
end
|
|
12
|
-
@instance = Redis.new(*, **)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def set(key, value) = @instance.set(prefix(key), value)
|
|
16
|
-
def get(key) = @instance.get(prefix(key))
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|