interfacets 0.9.9 → 0.9.99
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/.tmp +5 -0
- data/Rakefile +4 -0
- data/lib/interfacets/client/assets.rb +1 -0
- data/lib/interfacets/client/bus.rb +5 -1
- data/lib/interfacets/client/channels/api.rb +10 -3
- data/lib/interfacets/client/registry.rb +9 -70
- data/lib/interfacets/client/utils/mruby_patches.rb +1 -1
- data/lib/interfacets/client/utils/open_struct.rb +90 -77
- data/lib/interfacets/component_registry.rb +115 -0
- data/lib/interfacets/component_schema_parser.rb +84 -0
- data/lib/interfacets/server/api.rb +5 -25
- data/lib/interfacets/server/assets/facet.rb +5 -3
- data/lib/interfacets/server/assets.rb +13 -7
- data/lib/interfacets/server/basic_router.rb +15 -10
- data/lib/interfacets/server/bus.rb +4 -9
- data/lib/interfacets/server/config.rb +1 -1
- data/lib/interfacets/server/registry.rb +23 -184
- data/lib/interfacets/shared/basic_routable.rb +45 -0
- data/lib/interfacets/shared/entities/bus.rb +41 -29
- data/lib/interfacets/shared/entities/specs/handlers.rb +16 -0
- data/lib/interfacets/shared/entities/specs.rb +46 -9
- data/lib/interfacets/shared/entity.rb +23 -99
- data/lib/interfacets/shared/entity_dsl.rb +154 -0
- data/lib/interfacets/shared/facet.rb +200 -0
- data/lib/interfacets/shared/generated_store.rb +6 -2
- data/lib/interfacets/shared/utils.rb +1 -1
- data/lib/interfacets/shared/validations.rb +5 -1
- data/lib/interfacets/{client → shared}/view.rb +33 -6
- data/lib/interfacets/test/component_registry.rb +63 -0
- data/lib/interfacets/test/js/inline_bus.rb +21 -12
- data/lib/interfacets/test/js/nodo_bus.rb +18 -1
- data/lib/interfacets/test/js/receivers/api.rb +14 -3
- data/lib/interfacets/test/js/receivers/react/node/xml_parser.rb +75 -0
- data/lib/interfacets/test/js/receivers/react/node.rb +29 -63
- data/lib/interfacets/test/js/receivers/react.rb +4 -3
- data/lib/interfacets/test/js/receivers/timer.rb +77 -0
- data/lib/interfacets/test/js/receivers/url.rb +5 -0
- data/lib/interfacets/test/standard_elements.yml +173 -0
- data/lib/interfacets/test/{browser.rb → ui_simulator.rb} +18 -6
- data/lib/interfacets/test/validation_engine.rb +151 -0
- data/lib/interfacets/test.rb +0 -4
- data/lib/interfacets/version.rb +1 -1
- data/lib/interfacets.rb +3 -0
- metadata +29 -18
- data/lib/interfacets/client/facet.rb +0 -26
- data/lib/interfacets/client/facet2.rb +0 -15
- data/lib/interfacets/client/facets/attributes/accessor.rb +0 -28
- data/lib/interfacets/client/facets/attributes/association.rb +0 -50
- data/lib/interfacets/client/facets/attributes/bind.rb +0 -25
- data/lib/interfacets/client/facets/attributes/collection.rb +0 -47
- data/lib/interfacets/client/facets/attributes/readonly.rb +0 -19
- data/lib/interfacets/client/facets/deserializer.rb +0 -30
- data/lib/interfacets/client/facets/schema/deserializer.rb +0 -63
- data/lib/interfacets/client/facets/schema.rb +0 -63
- data/lib/interfacets/client/facets/serializer.rb +0 -18
- data/lib/interfacets/server/basic_routable.rb +0 -40
- data/lib/interfacets/server/facet.rb +0 -51
- data/lib/interfacets/shared/entity_collection.rb +0 -88
|
@@ -4,109 +4,15 @@ module Interfacets
|
|
|
4
4
|
module Shared
|
|
5
5
|
class Entity
|
|
6
6
|
include Shared::Validations
|
|
7
|
+
include EntityDsl
|
|
7
8
|
|
|
8
9
|
class << self
|
|
9
|
-
|
|
10
|
+
def inherited(subclass)
|
|
11
|
+
subclass.inherit_attributes(self)
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
@role = name.to_s if name
|
|
13
|
-
@role
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def accessor(
|
|
17
|
-
name,
|
|
18
|
-
getter: -> { store.send(name) },
|
|
19
|
-
setter: ->(val) { store.send("#{name}=", val) },
|
|
20
|
-
accepted_by: Entities::Specs::ANY
|
|
21
|
-
)
|
|
22
|
-
name = name.to_s
|
|
23
|
-
accessors[name] = Entities::Specs::Accessor.new(name:, accepted_by:)
|
|
24
|
-
|
|
25
|
-
define_method(name, &getter)
|
|
26
|
-
define_method("#{name}=", &setter)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def association(*args, type: :reference, **params, &block)
|
|
30
|
-
if type == :reference
|
|
31
|
-
reference(*args, **params, &block)
|
|
32
|
-
elsif type == :collection
|
|
33
|
-
collection(*args, **params, &block)
|
|
34
|
-
else
|
|
35
|
-
raise ArgumentError
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def reference(
|
|
40
|
-
name,
|
|
41
|
-
**spec_params,
|
|
42
|
-
&block
|
|
43
|
-
)
|
|
44
|
-
name = name.to_s
|
|
45
|
-
spec = (associations[name] ||= Entities::Specs::Reference.new(parent: self, name: name))
|
|
46
|
-
spec.apply(**spec_params, &block)
|
|
47
|
-
|
|
48
|
-
define_method(name) do
|
|
49
|
-
association(name).get
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
define_method("#{name}=") do |val|
|
|
53
|
-
association(name).set(val)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def collection(
|
|
58
|
-
name,
|
|
59
|
-
**spec_params,
|
|
60
|
-
&block
|
|
61
|
-
)
|
|
62
|
-
name = name.to_s
|
|
63
|
-
|
|
64
|
-
spec = (associations[name] ||= Entities::Specs::Collection.new(parent: self, name: name))
|
|
65
|
-
spec.apply(**spec_params, &block)
|
|
66
|
-
|
|
67
|
-
define_method(name) do
|
|
68
|
-
association(name).get
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
define_method("#{name}=") do |val|
|
|
72
|
-
association(name).set(val)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
13
|
+
subclass.action(:after_load, accepted_by: :client)
|
|
75
14
|
|
|
76
|
-
|
|
77
|
-
action(name, accepted_by: :server, only_if_valid:)
|
|
78
|
-
action("after_#{name}", accepted_by: :client)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def action(name, accepted_by: Entities::Specs::ANY, only_if_valid: false)
|
|
82
|
-
name = name.to_s
|
|
83
|
-
actions[name] = Entities::Specs::Action.new(name:, accepted_by:, only_if_valid:)
|
|
84
|
-
|
|
85
|
-
define_method(name) do
|
|
86
|
-
store.send(name)
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def accessors
|
|
91
|
-
@accessors ||= {}
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def associations
|
|
95
|
-
@associations ||= {}
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def actions
|
|
99
|
-
@actions ||= {}
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def attributes
|
|
103
|
-
accessors.merge(associations)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def inherited(mod)
|
|
107
|
-
mod.action(:after_load, accepted_by: :client)
|
|
108
|
-
|
|
109
|
-
mod.accessor(
|
|
15
|
+
subclass.accessor(
|
|
110
16
|
:internal_entity_id,
|
|
111
17
|
getter: -> {
|
|
112
18
|
if store.respond_to?(:internal_entity_id)
|
|
@@ -161,6 +67,24 @@ module Interfacets
|
|
|
161
67
|
@entity_nesting ||= (parent&.entity_nesting || []) + [[@nesting, internal_entity_id]]
|
|
162
68
|
end
|
|
163
69
|
|
|
70
|
+
def entity_at(nesting)
|
|
71
|
+
# ignore first entry, cause it's self
|
|
72
|
+
real_nesting = nesting[1..-1]
|
|
73
|
+
|
|
74
|
+
if real_nesting.count == 0
|
|
75
|
+
self
|
|
76
|
+
else
|
|
77
|
+
assoc, internal_entity_id = real_nesting[0]
|
|
78
|
+
|
|
79
|
+
if association(assoc).collection?
|
|
80
|
+
association(assoc).get.find { _1.internal_entity_id == internal_entity_id }
|
|
81
|
+
else
|
|
82
|
+
value = association(assoc).get
|
|
83
|
+
value.internal_entity_id == internal_entity_id ? value : nil
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
164
88
|
def association(name)
|
|
165
89
|
# inlined to avoid polluting API
|
|
166
90
|
@association_handlers ||= (
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Shared
|
|
5
|
+
module EntityDsl
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do |base|
|
|
9
|
+
if base.respond_to?(:inherit_attributes)
|
|
10
|
+
base.inherit_attributes(self)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class_methods do
|
|
15
|
+
attr_accessor :manifest
|
|
16
|
+
|
|
17
|
+
def role(name = nil)
|
|
18
|
+
if name
|
|
19
|
+
@role = name.to_s
|
|
20
|
+
else
|
|
21
|
+
@role
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def inheritable_attributes
|
|
26
|
+
{
|
|
27
|
+
accessors:,
|
|
28
|
+
actions:,
|
|
29
|
+
mergers:,
|
|
30
|
+
role: @role,
|
|
31
|
+
associations:,
|
|
32
|
+
validators:
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def inherit_attributes(parent)
|
|
37
|
+
return unless parent.respond_to?(:inheritable_attributes)
|
|
38
|
+
attrs = parent.inheritable_attributes
|
|
39
|
+
@accessors = attrs[:accessors].dup.merge(accessors)
|
|
40
|
+
@actions = attrs[:actions].dup.merge(actions)
|
|
41
|
+
@mergers = attrs[:mergers].dup.merge(mergers)
|
|
42
|
+
@role ||= attrs[:role]
|
|
43
|
+
|
|
44
|
+
attrs[:associations].each do |name, spec|
|
|
45
|
+
associations[name] ||= spec.dup_for(self)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
@validators = (attrs[:validators].dup + validators).uniq
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def merge(name, *events, &block)
|
|
52
|
+
spec = Entities::Specs::Merger.new(
|
|
53
|
+
name:,
|
|
54
|
+
block:
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
target = (mergers[name.to_s] ||= {})
|
|
58
|
+
|
|
59
|
+
if events.empty?
|
|
60
|
+
target[:default] = spec
|
|
61
|
+
else
|
|
62
|
+
events.each do |event|
|
|
63
|
+
target[event.to_s] = spec
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def accessor(
|
|
69
|
+
name,
|
|
70
|
+
getter: -> { store.send(name) },
|
|
71
|
+
setter: ->(val) { store.send("#{name}=", val) },
|
|
72
|
+
accepted_by: Entities::Specs::ANY
|
|
73
|
+
)
|
|
74
|
+
name = name.to_s
|
|
75
|
+
accessors[name] = Entities::Specs::Accessor.new(name:, accepted_by:)
|
|
76
|
+
|
|
77
|
+
define_method(name, &getter)
|
|
78
|
+
define_method("#{name}=", &setter)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def association(name, mod = nil, **spec_params, &block)
|
|
82
|
+
name = name.to_s
|
|
83
|
+
associations[name] ||= Entities::Specs::Association.new(parent: self, name: name)
|
|
84
|
+
associations[name].apply(mod:, **spec_params, &block)
|
|
85
|
+
|
|
86
|
+
define_method(name) do
|
|
87
|
+
association(name).get
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
define_method("#{name}=") do |val|
|
|
91
|
+
association(name).set(val)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def reference(*a, **p, &b)
|
|
96
|
+
association(*a, **p, type: :reference, &b)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def collection(*a, **p, &b)
|
|
100
|
+
association(*a, **p, type: :collection, &b)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def server_action(name, only_if_valid: true)
|
|
104
|
+
action(name, accepted_by: :server, only_if_valid:)
|
|
105
|
+
action("after_#{name}", accepted_by: :client)
|
|
106
|
+
|
|
107
|
+
define_method(name) do
|
|
108
|
+
store.send(name, entity: self)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def action(name, accepted_by: Entities::Specs::ANY, only_if_valid: false)
|
|
113
|
+
name = name.to_s
|
|
114
|
+
actions[name] = Entities::Specs::Action.new(name:, accepted_by:, only_if_valid:)
|
|
115
|
+
|
|
116
|
+
define_method(name) {}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def accessors
|
|
120
|
+
@accessors ||= {}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def associations
|
|
124
|
+
@associations ||= {}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def actions
|
|
128
|
+
@actions ||= {}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def mergers
|
|
132
|
+
@mergers ||= Hash.new { |h, k|
|
|
133
|
+
h[k] = {
|
|
134
|
+
default: Entities::Specs::Merger.new(
|
|
135
|
+
name: k,
|
|
136
|
+
block: ->(entity, value) {
|
|
137
|
+
entity.send("#{k}=", value)
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def attributes
|
|
145
|
+
accessors.merge(associations)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def validators
|
|
149
|
+
@validators ||= []
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Interfacets
|
|
4
|
+
module Shared
|
|
5
|
+
module Facet
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
class_methods do
|
|
9
|
+
attr_accessor :shared, :entity, :store
|
|
10
|
+
|
|
11
|
+
def mount(facet, as:, type: nil, &block)
|
|
12
|
+
if block_given?
|
|
13
|
+
parent_facet = facet
|
|
14
|
+
facet = Module.new do
|
|
15
|
+
include Interfacets::Shared::Facet
|
|
16
|
+
|
|
17
|
+
server_entity do
|
|
18
|
+
include parent_facet.server_entity_module
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
entity_base do
|
|
22
|
+
include parent_facet.entity_base_module
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
client_entity do
|
|
26
|
+
include parent_facet.client_entity_module
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
view_class.view(&parent_facet.view_class.view) if parent_facet.view_class.view
|
|
30
|
+
|
|
31
|
+
class_exec(&block)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
server_entity do
|
|
36
|
+
association(as, facet.server_entity_module, type:)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
client_entity do
|
|
40
|
+
association(as, facet.client_entity_module, type:)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
entity_base do
|
|
44
|
+
association(as, facet.entity_base_module, type:)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def view(&block)
|
|
49
|
+
view_class.view(&block)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def view_class
|
|
53
|
+
@view_class ||= Class.new(Interfacets::Shared::View)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def client_entity(&block)
|
|
57
|
+
clients << block
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def clients
|
|
61
|
+
@clients ||= []
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def client_entity_module
|
|
65
|
+
@client_entity_module ||= (
|
|
66
|
+
facet = self
|
|
67
|
+
Module.new do
|
|
68
|
+
extend ActiveSupport::Concern
|
|
69
|
+
include facet.entity_base_module
|
|
70
|
+
|
|
71
|
+
included do
|
|
72
|
+
define_singleton_method(:view) do
|
|
73
|
+
facet.view_class
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
role("client")
|
|
77
|
+
facet.clients.each { class_exec(&_1) }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def client_entity_class
|
|
84
|
+
@client_entity_class ||= (
|
|
85
|
+
facet = self
|
|
86
|
+
Class
|
|
87
|
+
.new(entity_base_class)
|
|
88
|
+
.tap do |k|
|
|
89
|
+
k.class_exec do
|
|
90
|
+
include facet.client_entity_module
|
|
91
|
+
|
|
92
|
+
define_singleton_method(:store) do
|
|
93
|
+
@store ||= (
|
|
94
|
+
::Interfacets::Shared::GeneratedStore.construct(self)
|
|
95
|
+
.tap { self.const_set("Store", _1) }
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
self.manifest = facet.entity_base_class
|
|
100
|
+
|
|
101
|
+
def channel(name)
|
|
102
|
+
Interfacets::Client::System.current_bus.channel(name).builder
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def entity_base(&block)
|
|
110
|
+
bases << block
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def bases
|
|
114
|
+
@bases ||= []
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def entity_base_module
|
|
118
|
+
@entity_base_module ||= (
|
|
119
|
+
facet = self
|
|
120
|
+
Module.new do
|
|
121
|
+
extend ActiveSupport::Concern
|
|
122
|
+
include Interfacets::Shared::EntityDsl
|
|
123
|
+
included do
|
|
124
|
+
facet.bases.each { class_exec(&_1) }
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def entity_base_class
|
|
131
|
+
@entity_base_class ||= (
|
|
132
|
+
facet = self
|
|
133
|
+
|
|
134
|
+
Class.new(Shared::Entity)
|
|
135
|
+
.tap { |k| k.include(facet.entity_base_module) }
|
|
136
|
+
.tap { facet.const_set("EntityBase", _1) }
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def server_entity_module
|
|
141
|
+
@server_entity_module ||= (
|
|
142
|
+
facet = self
|
|
143
|
+
Module.new do
|
|
144
|
+
extend ActiveSupport::Concern
|
|
145
|
+
include facet.entity_base_module
|
|
146
|
+
included do
|
|
147
|
+
role("server")
|
|
148
|
+
facet.servers.each { class_exec(&_1) }
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def server_entity_class
|
|
155
|
+
@server_entity_class ||= (
|
|
156
|
+
facet = self
|
|
157
|
+
|
|
158
|
+
Class.new(entity_base_class) {
|
|
159
|
+
include facet.server_entity_module
|
|
160
|
+
|
|
161
|
+
attr_reader :channel
|
|
162
|
+
def initialize(*a, channel:, **p, &b)
|
|
163
|
+
@channel = channel
|
|
164
|
+
super(*a, **p, &b)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def build_entity(...)
|
|
168
|
+
channel.build(...)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
self.manifest = facet.entity_base_class
|
|
172
|
+
}
|
|
173
|
+
).tap { facet.const_set("ServerBase", _1) }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def server_entity(unshift: false, &block)
|
|
177
|
+
if unshift
|
|
178
|
+
servers.unshift(block)
|
|
179
|
+
else
|
|
180
|
+
servers << block
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def servers
|
|
185
|
+
@servers ||= []
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
attr_reader :entity, :view
|
|
190
|
+
def initialize(entity)
|
|
191
|
+
@entity = entity
|
|
192
|
+
@view = self.class.view.new if self.class.view.is_a?(Class)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def render(channels)
|
|
196
|
+
view.render(entity:, channels:)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
@@ -24,8 +24,12 @@ module Interfacets
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
entity_class.actions.each do |name, spec|
|
|
27
|
-
klass.define_method(name) do
|
|
28
|
-
Client::System
|
|
27
|
+
klass.define_method(name) do |entity:|
|
|
28
|
+
Client::System
|
|
29
|
+
.current_bus
|
|
30
|
+
.channel("interfacets:api")
|
|
31
|
+
.builder
|
|
32
|
+
.submit(name, nesting: entity.entity_nesting)
|
|
29
33
|
end
|
|
30
34
|
end
|
|
31
35
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Interfacets
|
|
4
|
-
module
|
|
4
|
+
module Shared
|
|
5
5
|
class View
|
|
6
6
|
class Evaluator
|
|
7
|
-
def self.call(
|
|
8
|
-
new(
|
|
7
|
+
def self.call(**p)
|
|
8
|
+
new(**p).call
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
attr_reader :entity, :channels, :block
|
|
@@ -20,10 +20,23 @@ module Interfacets
|
|
|
20
20
|
instance_exec(entity, &@block)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def render(
|
|
23
|
+
def render(entity)
|
|
24
|
+
entity
|
|
25
|
+
.class
|
|
26
|
+
.view
|
|
27
|
+
.render(
|
|
28
|
+
entity:,
|
|
29
|
+
channels:
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def render_to(channel_id, stream: "default", &block)
|
|
34
|
+
channel_id = channel_id.to_s
|
|
35
|
+
|
|
24
36
|
channels
|
|
25
|
-
.fetch(channel_id
|
|
37
|
+
.fetch(channel_id)
|
|
26
38
|
.builder(stream.to_s)
|
|
39
|
+
.then { @current_builder = _1 }
|
|
27
40
|
.then { instance_exec(_1, &block) }
|
|
28
41
|
end
|
|
29
42
|
|
|
@@ -37,10 +50,24 @@ module Interfacets
|
|
|
37
50
|
@view = block if block_given?
|
|
38
51
|
@view
|
|
39
52
|
end
|
|
53
|
+
|
|
54
|
+
def view=(val)
|
|
55
|
+
@view = val
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def render(**p)
|
|
59
|
+
new.render(**p)
|
|
60
|
+
end
|
|
40
61
|
end
|
|
41
62
|
|
|
42
63
|
def render(entity:, channels:)
|
|
43
|
-
|
|
64
|
+
return unless self.class.view
|
|
65
|
+
|
|
66
|
+
Evaluator.call(
|
|
67
|
+
entity: entity,
|
|
68
|
+
channels: channels,
|
|
69
|
+
block: self.class.view
|
|
70
|
+
)
|
|
44
71
|
end
|
|
45
72
|
end
|
|
46
73
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module Interfacets
|
|
6
|
+
module Test
|
|
7
|
+
class ComponentRegistry
|
|
8
|
+
DEFAULT_CONFIG_PATH = "config/interfacets/components.yml"
|
|
9
|
+
|
|
10
|
+
def self.load(path = DEFAULT_CONFIG_PATH)
|
|
11
|
+
new(path)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :contracts
|
|
15
|
+
|
|
16
|
+
def initialize(path)
|
|
17
|
+
@path = path
|
|
18
|
+
@schema_cache = {}
|
|
19
|
+
@contracts = load_contracts
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def find_component(name)
|
|
23
|
+
@contracts[name.to_s]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def definitions
|
|
27
|
+
@contracts["definitions"] || {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def load_contracts
|
|
33
|
+
return {} unless File.exist?(@path)
|
|
34
|
+
|
|
35
|
+
contracts = YAML.load_file(@path, aliases: true) || {}
|
|
36
|
+
contracts.each do |name, config|
|
|
37
|
+
next unless config.is_a?(Hash)
|
|
38
|
+
|
|
39
|
+
schema = config["schema"]
|
|
40
|
+
if schema.is_a?(String)
|
|
41
|
+
config["schema"] = load_schema_file(name, schema)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
contracts
|
|
45
|
+
rescue Psych::SyntaxError => e
|
|
46
|
+
raise Error, "Failed to parse component registry at #{@path}: #{e.message}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def load_schema_file(component_name, schema_path)
|
|
50
|
+
full_path = File.expand_path(schema_path, File.dirname(@path))
|
|
51
|
+
return @schema_cache[full_path] if @schema_cache.key?(full_path)
|
|
52
|
+
|
|
53
|
+
raise Errno::ENOENT, "Schema file not found for component '#{component_name}' at #{full_path}" unless File.exist?(full_path)
|
|
54
|
+
|
|
55
|
+
begin
|
|
56
|
+
@schema_cache[full_path] = YAML.load_file(full_path, aliases: true) || {}
|
|
57
|
+
rescue Psych::SyntaxError => e
|
|
58
|
+
raise Error, "Failed to parse schema file for component '#{component_name}' at #{full_path}: #{e.message}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|