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
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json_schemer"
|
|
4
|
+
require "set"
|
|
5
|
+
require "yaml"
|
|
6
|
+
|
|
7
|
+
module Interfacets
|
|
8
|
+
module Test
|
|
9
|
+
class ValidationEngine
|
|
10
|
+
attr_reader :registry, :validation_mode
|
|
11
|
+
|
|
12
|
+
@warned_components = Set.new
|
|
13
|
+
|
|
14
|
+
def self.warned_components
|
|
15
|
+
@warned_components
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.reset_warnings!
|
|
19
|
+
@warned_components.clear
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(registry = ComponentRegistry.load, validation_mode: :strict)
|
|
23
|
+
@registry = registry
|
|
24
|
+
@validation_mode = validation_mode
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def active?
|
|
28
|
+
registry.contracts.any?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def validate_props(component_name, props)
|
|
32
|
+
return unless active?
|
|
33
|
+
|
|
34
|
+
contract = registry.find_component(component_name)
|
|
35
|
+
is_standard = standard_element?(component_name)
|
|
36
|
+
|
|
37
|
+
props_config = contract&.dig("props")
|
|
38
|
+
|
|
39
|
+
if is_standard
|
|
40
|
+
return unless props_config
|
|
41
|
+
else
|
|
42
|
+
return unless contract_exists?(component_name)
|
|
43
|
+
|
|
44
|
+
raise ValidationError, "Missing props schema for custom component: #{component_name}" unless props_config
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
schema = { "type" => "object", "properties" => props_config }
|
|
48
|
+
validate!(schema, props, "properties for #{component_name}")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def validate_event(component_name, event_name, payload)
|
|
52
|
+
return unless active?
|
|
53
|
+
|
|
54
|
+
contract = registry.find_component(component_name)
|
|
55
|
+
is_standard = standard_element?(component_name)
|
|
56
|
+
|
|
57
|
+
event_config = contract&.dig("props", event_name.to_s)
|
|
58
|
+
|
|
59
|
+
if is_standard
|
|
60
|
+
return unless event_config
|
|
61
|
+
else
|
|
62
|
+
return unless contract_exists?(component_name)
|
|
63
|
+
|
|
64
|
+
raise ValidationError, "Missing events schema for custom component: #{component_name}" unless contract.key?("props")
|
|
65
|
+
raise ValidationError, "Missing event schema for '#{event_name}' in custom component: #{component_name}" unless event_config
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
schema = event_config.is_a?(Hash) ? (event_config["payload"] || {}) : {}
|
|
69
|
+
|
|
70
|
+
validate!(schema, payload, "event '#{event_name}' for #{component_name}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def standard_element?(component_name)
|
|
76
|
+
@standard_elements ||= begin
|
|
77
|
+
yaml_path = File.expand_path("standard_elements.yml", __dir__)
|
|
78
|
+
Set.new(YAML.load_file(yaml_path).map(&:to_s))
|
|
79
|
+
end
|
|
80
|
+
@standard_elements.include?(component_name.to_s)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def contract_exists?(component_name)
|
|
84
|
+
contract = registry.find_component(component_name)
|
|
85
|
+
return true if contract
|
|
86
|
+
|
|
87
|
+
case validation_mode
|
|
88
|
+
when :strict
|
|
89
|
+
raise MissingComponentContractError, "Missing component contract for: #{component_name}"
|
|
90
|
+
when :warn
|
|
91
|
+
unless self.class.warned_components.include?(component_name)
|
|
92
|
+
warn "Warning: Missing component contract for: #{component_name}"
|
|
93
|
+
self.class.warned_components << component_name
|
|
94
|
+
end
|
|
95
|
+
false
|
|
96
|
+
when :permissive
|
|
97
|
+
false
|
|
98
|
+
else
|
|
99
|
+
raise ArgumentError, "Unknown validation_mode: #{validation_mode}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def find_contract!(name)
|
|
104
|
+
contract = registry.find_component(name)
|
|
105
|
+
return contract if contract
|
|
106
|
+
|
|
107
|
+
raise MissingComponentContractError, "Missing component contract for: #{name}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def validate!(schema, data, context)
|
|
111
|
+
full_schema = schema.dup
|
|
112
|
+
if full_schema["type"] == "object" && !full_schema.key?("additionalProperties")
|
|
113
|
+
full_schema["additionalProperties"] = false
|
|
114
|
+
end
|
|
115
|
+
full_schema["definitions"] = registry.definitions if registry.definitions.any?
|
|
116
|
+
|
|
117
|
+
schemer = JSONSchemer.schema(full_schema, keywords: {
|
|
118
|
+
"is_event" => lambda do |instance, keyword_value, pointer|
|
|
119
|
+
return true unless keyword_value
|
|
120
|
+
unless instance.is_a?(Hash) && instance.key?("type") && instance.key?("payload")
|
|
121
|
+
return [false, "missing 'type' or 'payload'"]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
prop_name = pointer.split("/").last
|
|
125
|
+
prop_schema = full_schema.dig("properties", prop_name)
|
|
126
|
+
if prop_schema && (nested_schema = prop_schema["schema"])
|
|
127
|
+
nested_schemer = JSONSchemer.schema(nested_schema)
|
|
128
|
+
nested_errors = nested_schemer.validate(instance["payload"]).to_a
|
|
129
|
+
if nested_errors.any?
|
|
130
|
+
return [false, "event payload validation failed: #{nested_errors.map { |e| "#{e['data_pointer']}: #{e['type']} #{e['details']}" }.join(', ')}"]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
true
|
|
135
|
+
end
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
errors = schemer.validate(data).to_a
|
|
139
|
+
|
|
140
|
+
return if errors.empty?
|
|
141
|
+
|
|
142
|
+
message = "Validation failed for #{context}:\n"
|
|
143
|
+
errors.each do |error|
|
|
144
|
+
message += " - #{error['data_pointer']}: #{error['type']} #{error['details']}\n"
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
raise ValidationError, message
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
data/lib/interfacets/test.rb
CHANGED
data/lib/interfacets/version.rb
CHANGED
data/lib/interfacets.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "zeitwerk"
|
|
4
|
+
require "active_support/concern"
|
|
4
5
|
|
|
5
6
|
module Interfacets
|
|
6
7
|
class << self
|
|
@@ -25,6 +26,8 @@ module Interfacets
|
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
class Error < StandardError; end
|
|
29
|
+
class ValidationError < Error; end
|
|
30
|
+
class MissingComponentContractError < Error; end
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
unless Interfacets.enable_reloading?
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: interfacets
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.99
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pete Kinnecom
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -38,6 +38,20 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '2.5'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: json_schemer
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '2.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.0'
|
|
41
55
|
description:
|
|
42
56
|
email:
|
|
43
57
|
- git@k7u7.com
|
|
@@ -46,6 +60,7 @@ extensions: []
|
|
|
46
60
|
extra_rdoc_files: []
|
|
47
61
|
files:
|
|
48
62
|
- ".rubocop.yml"
|
|
63
|
+
- ".tmp"
|
|
49
64
|
- LICENSE
|
|
50
65
|
- LICENSE.txt
|
|
51
66
|
- Rakefile
|
|
@@ -67,24 +82,14 @@ files:
|
|
|
67
82
|
- lib/interfacets/client/channels/url.rb
|
|
68
83
|
- lib/interfacets/client/config.rb
|
|
69
84
|
- lib/interfacets/client/delegator.rb
|
|
70
|
-
- lib/interfacets/client/facet.rb
|
|
71
|
-
- lib/interfacets/client/facet2.rb
|
|
72
|
-
- lib/interfacets/client/facets/attributes/accessor.rb
|
|
73
|
-
- lib/interfacets/client/facets/attributes/association.rb
|
|
74
|
-
- lib/interfacets/client/facets/attributes/bind.rb
|
|
75
|
-
- lib/interfacets/client/facets/attributes/collection.rb
|
|
76
|
-
- lib/interfacets/client/facets/attributes/readonly.rb
|
|
77
|
-
- lib/interfacets/client/facets/deserializer.rb
|
|
78
|
-
- lib/interfacets/client/facets/schema.rb
|
|
79
|
-
- lib/interfacets/client/facets/schema/deserializer.rb
|
|
80
|
-
- lib/interfacets/client/facets/serializer.rb
|
|
81
85
|
- lib/interfacets/client/registry.rb
|
|
82
86
|
- lib/interfacets/client/system.rb
|
|
83
87
|
- lib/interfacets/client/utils/active_support_concern.rb
|
|
84
88
|
- lib/interfacets/client/utils/mruby_patches.rb
|
|
85
89
|
- lib/interfacets/client/utils/open_struct.rb
|
|
86
90
|
- lib/interfacets/client/utils/securerandom.rb
|
|
87
|
-
- lib/interfacets/
|
|
91
|
+
- lib/interfacets/component_registry.rb
|
|
92
|
+
- lib/interfacets/component_schema_parser.rb
|
|
88
93
|
- lib/interfacets/mruby/build.dockerfile
|
|
89
94
|
- lib/interfacets/mruby/build_config.rb
|
|
90
95
|
- lib/interfacets/mruby/entrypoint.rb
|
|
@@ -92,32 +97,38 @@ files:
|
|
|
92
97
|
- lib/interfacets/server/api.rb
|
|
93
98
|
- lib/interfacets/server/assets.rb
|
|
94
99
|
- lib/interfacets/server/assets/facet.rb
|
|
95
|
-
- lib/interfacets/server/basic_routable.rb
|
|
96
100
|
- lib/interfacets/server/basic_router.rb
|
|
97
101
|
- lib/interfacets/server/bus.rb
|
|
98
102
|
- lib/interfacets/server/config.rb
|
|
99
|
-
- lib/interfacets/server/facet.rb
|
|
100
103
|
- lib/interfacets/server/facets/deserializer.rb
|
|
101
104
|
- lib/interfacets/server/facets/schema/serializer.rb
|
|
102
105
|
- lib/interfacets/server/facets/serializer.rb
|
|
103
106
|
- lib/interfacets/server/registry.rb
|
|
107
|
+
- lib/interfacets/shared/basic_routable.rb
|
|
104
108
|
- lib/interfacets/shared/entities/bus.rb
|
|
105
109
|
- lib/interfacets/shared/entities/collection_proxy.rb
|
|
106
110
|
- lib/interfacets/shared/entities/specs.rb
|
|
107
111
|
- lib/interfacets/shared/entities/specs/handlers.rb
|
|
108
112
|
- lib/interfacets/shared/entity.rb
|
|
109
|
-
- lib/interfacets/shared/
|
|
113
|
+
- lib/interfacets/shared/entity_dsl.rb
|
|
114
|
+
- lib/interfacets/shared/facet.rb
|
|
110
115
|
- lib/interfacets/shared/generated_store.rb
|
|
111
116
|
- lib/interfacets/shared/utils.rb
|
|
112
117
|
- lib/interfacets/shared/validations.rb
|
|
118
|
+
- lib/interfacets/shared/view.rb
|
|
113
119
|
- lib/interfacets/test.rb
|
|
114
|
-
- lib/interfacets/test/
|
|
120
|
+
- lib/interfacets/test/component_registry.rb
|
|
115
121
|
- lib/interfacets/test/js/inline_bus.rb
|
|
116
122
|
- lib/interfacets/test/js/nodo_bus.rb
|
|
117
123
|
- lib/interfacets/test/js/receivers/api.rb
|
|
118
124
|
- lib/interfacets/test/js/receivers/react.rb
|
|
119
125
|
- lib/interfacets/test/js/receivers/react/node.rb
|
|
126
|
+
- lib/interfacets/test/js/receivers/react/node/xml_parser.rb
|
|
127
|
+
- lib/interfacets/test/js/receivers/timer.rb
|
|
120
128
|
- lib/interfacets/test/js/receivers/url.rb
|
|
129
|
+
- lib/interfacets/test/standard_elements.yml
|
|
130
|
+
- lib/interfacets/test/ui_simulator.rb
|
|
131
|
+
- lib/interfacets/test/validation_engine.rb
|
|
121
132
|
- lib/interfacets/version.rb
|
|
122
133
|
homepage: https://github.com/petekinnecom/interfacets
|
|
123
134
|
licenses:
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Interfacets
|
|
4
|
-
module Client
|
|
5
|
-
class Facet
|
|
6
|
-
class << self
|
|
7
|
-
attr_accessor(
|
|
8
|
-
:shared,
|
|
9
|
-
:entity,
|
|
10
|
-
:view,
|
|
11
|
-
:store,
|
|
12
|
-
)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
attr_reader :entity, :view
|
|
16
|
-
def initialize(entity)
|
|
17
|
-
@entity = entity
|
|
18
|
-
@view = self.class.view.new
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def render(channels)
|
|
22
|
-
view.render(entity:, channels:)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Interfacets
|
|
4
|
-
module Client
|
|
5
|
-
module Facets
|
|
6
|
-
module Attributes
|
|
7
|
-
class Accessor
|
|
8
|
-
attr_reader :send_if
|
|
9
|
-
def initialize(send_if:)
|
|
10
|
-
@send_if = send_if
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def coerce(val, **)
|
|
14
|
-
val
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def serialize(val)
|
|
18
|
-
val
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def should_send?(facet)
|
|
22
|
-
facet.instance_exec(&send_if)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Interfacets
|
|
4
|
-
module Client
|
|
5
|
-
module Facets
|
|
6
|
-
module Attributes
|
|
7
|
-
class Association
|
|
8
|
-
attr_reader :nested, :anonymous, :config, :bind_map
|
|
9
|
-
|
|
10
|
-
def initialize(nested:, anonymous:, config:, bind_map:)
|
|
11
|
-
@nested = nested
|
|
12
|
-
@anonymous = anonymous
|
|
13
|
-
@config = config
|
|
14
|
-
@bind_map = bind_map
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def coerce(val, parent:)
|
|
18
|
-
return if val.nil?
|
|
19
|
-
|
|
20
|
-
facet.build(
|
|
21
|
-
attrs: val,
|
|
22
|
-
binds: bind_map.transform_values { |v| Bind.new(name: v, facet: parent) },
|
|
23
|
-
)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def build(attrs:, parent:)
|
|
27
|
-
facet.build(
|
|
28
|
-
attrs:,
|
|
29
|
-
binds: bind_map.transform_values { |v| Bind.new(name: v, facet: parent) },
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def serialize(val)
|
|
34
|
-
val&.serialize
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def facet
|
|
38
|
-
@facet ||= (
|
|
39
|
-
if anonymous
|
|
40
|
-
@nested
|
|
41
|
-
else
|
|
42
|
-
config.schema(@nested.fetch("name"))
|
|
43
|
-
end
|
|
44
|
-
)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Interfacets
|
|
4
|
-
module Client
|
|
5
|
-
module Facets
|
|
6
|
-
module Attributes
|
|
7
|
-
class Bind
|
|
8
|
-
attr_reader :name, :facet
|
|
9
|
-
def initialize(name:, facet:)
|
|
10
|
-
@name = name
|
|
11
|
-
@facet = facet
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def get
|
|
15
|
-
facet.public_send(name)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def set(v)
|
|
19
|
-
facet.public_send("#{name}=", v)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Interfacets
|
|
4
|
-
module Client
|
|
5
|
-
module Facets
|
|
6
|
-
module Attributes
|
|
7
|
-
class Collection
|
|
8
|
-
attr_reader :nested, :anonymous, :config, :bind_map
|
|
9
|
-
|
|
10
|
-
def initialize(nested:, anonymous:, config:, bind_map:)
|
|
11
|
-
@nested = nested
|
|
12
|
-
@anonymous = anonymous
|
|
13
|
-
@config = config
|
|
14
|
-
@bind_map = bind_map
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def coerce(val, parent:)
|
|
18
|
-
return [] if val.nil?
|
|
19
|
-
|
|
20
|
-
val.map { build(attrs: _1, parent:) }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def build(attrs:, parent:)
|
|
24
|
-
facet.build(
|
|
25
|
-
attrs:,
|
|
26
|
-
binds: bind_map.transform_values { |v| Bind.new(name: v, facet: parent) },
|
|
27
|
-
)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def serialize(val)
|
|
31
|
-
val.map(&:serialize)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def facet
|
|
35
|
-
@facet ||= (
|
|
36
|
-
if anonymous
|
|
37
|
-
@nested
|
|
38
|
-
else
|
|
39
|
-
config.schema(@nested.fetch("name"))
|
|
40
|
-
end
|
|
41
|
-
)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Interfacets
|
|
4
|
-
module Client
|
|
5
|
-
module Facets
|
|
6
|
-
class Deserializer
|
|
7
|
-
def self.call(data:, config: Client.system.config)
|
|
8
|
-
Logger.main.debug("deserializing facet: #{data.fetch("facet_class")}")
|
|
9
|
-
|
|
10
|
-
klass = Object.const_get(data.fetch("facet_class"))
|
|
11
|
-
|
|
12
|
-
Logger.main.debug("constructing: #{klass.client.name}")
|
|
13
|
-
|
|
14
|
-
facet = klass.new
|
|
15
|
-
|
|
16
|
-
entity = (
|
|
17
|
-
klass
|
|
18
|
-
.client
|
|
19
|
-
.build(data.fetch("attributes"), parent: nil, facet:)
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
Logger.main.debug("entity constructed")
|
|
23
|
-
|
|
24
|
-
facet.entity = entity
|
|
25
|
-
facet
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Interfacets
|
|
4
|
-
module Client
|
|
5
|
-
module Facets
|
|
6
|
-
module Schema
|
|
7
|
-
class Deserializer
|
|
8
|
-
def self.call(...)
|
|
9
|
-
new(...).call
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
attr_reader :schema, :results
|
|
13
|
-
def initialize(schema:)
|
|
14
|
-
@schema = schema
|
|
15
|
-
@results = { views: {}, entities: {}, apis: {} }
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def call
|
|
19
|
-
schema.fetch("views").each do |view_type, view_json|
|
|
20
|
-
block = view_json.fetch("body")
|
|
21
|
-
|
|
22
|
-
results[:views][view_type] = View.new(
|
|
23
|
-
block: eval(block),
|
|
24
|
-
name: view_type,
|
|
25
|
-
)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
schema.fetch("apis").each do |api_type, entity_json|
|
|
29
|
-
block = entity_json.fetch("body")
|
|
30
|
-
|
|
31
|
-
results[:apis][api_type] =
|
|
32
|
-
Module.new do
|
|
33
|
-
extend ActiveSupport::Concern
|
|
34
|
-
define_singleton_method(:name) { api_type }
|
|
35
|
-
|
|
36
|
-
included do
|
|
37
|
-
class_exec(&eval(block))
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
schema.fetch("entities").each do |entity_type, client_json|
|
|
43
|
-
block = client_json.fetch("body")
|
|
44
|
-
api_type = client_json.fetch("api_type")
|
|
45
|
-
|
|
46
|
-
api_mod = results[:apis].fetch(api_type)
|
|
47
|
-
|
|
48
|
-
results[:entities][entity_type] =
|
|
49
|
-
Class.new(Entity) do
|
|
50
|
-
define_singleton_method(:name) { entity_type }
|
|
51
|
-
include api_mod
|
|
52
|
-
|
|
53
|
-
class_exec(&eval(block))
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
results
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Interfacets
|
|
4
|
-
module Client
|
|
5
|
-
module Facets
|
|
6
|
-
module Schema
|
|
7
|
-
extend ActiveSupport::Concern
|
|
8
|
-
class_methods do
|
|
9
|
-
def view_spec(&block)
|
|
10
|
-
@view_spec = block
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def view
|
|
14
|
-
# TODO: change this to just inherit from view
|
|
15
|
-
@view ||= View.new(
|
|
16
|
-
block: @view_spec,
|
|
17
|
-
name: "#{self.name}::View",
|
|
18
|
-
)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def client_spec(&block)
|
|
22
|
-
@client_spec = block
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def client
|
|
26
|
-
# scoping
|
|
27
|
-
client_name = "#{self.name}::Client"
|
|
28
|
-
client_spec = @client_spec
|
|
29
|
-
entity = self.entity
|
|
30
|
-
|
|
31
|
-
@client ||=
|
|
32
|
-
Class.new(Entity) do
|
|
33
|
-
define_singleton_method(:name) { client_name }
|
|
34
|
-
extend_entity { include entity }
|
|
35
|
-
|
|
36
|
-
class_exec(&client_spec)
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def entity_spec(&block)
|
|
41
|
-
@entity_spec = block
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def entity
|
|
45
|
-
# scoping
|
|
46
|
-
entity_name = "#{self.name}::Entity"
|
|
47
|
-
entity_spec = @entity_spec
|
|
48
|
-
|
|
49
|
-
@entity ||=
|
|
50
|
-
Module.new do
|
|
51
|
-
extend ActiveSupport::Concern
|
|
52
|
-
define_singleton_method(:name) { entity_name }
|
|
53
|
-
|
|
54
|
-
included do
|
|
55
|
-
class_exec(&entity_spec)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Interfacets
|
|
4
|
-
module Client
|
|
5
|
-
module Facets
|
|
6
|
-
class Serializer
|
|
7
|
-
class << self
|
|
8
|
-
def call(facet)
|
|
9
|
-
{
|
|
10
|
-
facet_class: facet.class.name,
|
|
11
|
-
attributes: facet.entity.serialize,
|
|
12
|
-
}
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|