lutaml-model 0.8.64 → 0.8.65
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/lutaml/model/version.rb +1 -1
- data/lib/lutaml/xml/plan_compiler.rb +11 -0
- data/lib/lutaml/xml/plan_hydrator.rb +18 -5
- data/lib/lutaml/xml/plan_serializer.rb +32 -20
- data/spec/lutaml/xml/cross_register_plan_spec.rb +75 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a8bbc0a4da8114a582ee834a333a8614ca1051747296a1e967eb36f47ef68b48
|
|
4
|
+
data.tar.gz: ec01b4058ae9ce9d400f229ae4f116bcdde25cdd015e3b96ff3e2b39af62ff10
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 494b45969f4e8f67e5f79fe09eb6e1630e5b04109fe46b13210cc8dfe455a11e7b7d7955a91ae2132c037c7e53d21fb09c0462a55899cccaafc4d4e5b609bccd
|
|
7
|
+
data.tar.gz: 6694a345fda4b371988fab2af07b9311b7eed87f2e61a696eab7c2a247ba04950bbb93439b1019a27903dbfcf8c5815067f2d3449a41affe2cdb21a6a3deb583
|
data/lib/lutaml/model/version.rb
CHANGED
|
@@ -36,6 +36,17 @@ module Lutaml
|
|
|
36
36
|
|
|
37
37
|
class << self
|
|
38
38
|
def compile(model_class, register)
|
|
39
|
+
# A child model's declared lutaml_default_register takes
|
|
40
|
+
# precedence over the ambient (parent) register — the same
|
|
41
|
+
# contract the interpretive path applies through
|
|
42
|
+
# Register.resolve_for_child. Without this, plan compilation
|
|
43
|
+
# resolves the child's symbol attribute types in the parent
|
|
44
|
+
# context and raises UnknownTypeError for ids registered only
|
|
45
|
+
# in the child's own register (#876).
|
|
46
|
+
register = Lutaml::Model::Register.resolve_for_child(
|
|
47
|
+
model_class, register
|
|
48
|
+
)
|
|
49
|
+
|
|
39
50
|
key = [model_class, register]
|
|
40
51
|
return PLAN_CACHE[key] if PLAN_CACHE.key?(key)
|
|
41
52
|
|
|
@@ -20,11 +20,21 @@ module Lutaml
|
|
|
20
20
|
# node: the parsed source element (leptris); ordered/mixed
|
|
21
21
|
# plans rebuild element_order from it and ordered children
|
|
22
22
|
# hydrate natively against their own nodes
|
|
23
|
-
def call(model_class, plan, value, parent: nil, node: nil
|
|
23
|
+
def call(model_class, plan, value, parent: nil, node: nil,
|
|
24
|
+
register: nil)
|
|
25
|
+
# The child model's declared lutaml_default_register takes
|
|
26
|
+
# precedence over the ambient register — the same
|
|
27
|
+
# resolve_for_child contract the interpretive path applies.
|
|
28
|
+
# Without it, hydration resolves the child's symbol attribute
|
|
29
|
+
# types in the parent context and raises UnknownTypeError
|
|
30
|
+
# for ids registered only in the child's register (#876).
|
|
31
|
+
register = Lutaml::Model::Register.resolve_for_child(
|
|
32
|
+
model_class, register || Lutaml::Model::Config.default_register
|
|
33
|
+
)
|
|
24
34
|
buckets = node && plan[:needs_nodes] ? element_buckets(node) : nil
|
|
25
35
|
attr_kwargs = attributes_kwargs(plan, value, node)
|
|
26
36
|
child_kwargs, children, delegates =
|
|
27
|
-
children_kwargs(model_class, plan, value, buckets)
|
|
37
|
+
children_kwargs(model_class, plan, value, buckets, register)
|
|
28
38
|
plan[:collection_defaults].each do |name|
|
|
29
39
|
next if attr_kwargs.key?(name) || child_kwargs.key?(name)
|
|
30
40
|
|
|
@@ -80,7 +90,9 @@ module Lutaml
|
|
|
80
90
|
# — child instances come back so the caller can decorate
|
|
81
91
|
# parent/root links once the parent exists; delegate values
|
|
82
92
|
# wait for the instance (their target object must exist).
|
|
83
|
-
def children_kwargs(_model_class, plan, value, buckets = nil
|
|
93
|
+
def children_kwargs(_model_class, plan, value, buckets = nil,
|
|
94
|
+
register = nil)
|
|
95
|
+
register ||= Lutaml::Model::Config.default_register
|
|
84
96
|
grouped, tagged = group_children(value, plan[:row_tags])
|
|
85
97
|
buckets = nil unless buckets && plan[:needs_nodes]
|
|
86
98
|
kwargs = {}
|
|
@@ -126,7 +138,8 @@ module Lutaml
|
|
|
126
138
|
child_plan = PlanCompiler.compile(child_type, register)
|
|
127
139
|
items = buckets.fetch(rule.name.to_s, []).map do |n|
|
|
128
140
|
call(child_type, child_plan,
|
|
129
|
-
child_plan[:descriptor].walk(n), node: n
|
|
141
|
+
child_plan[:descriptor].walk(n), node: n,
|
|
142
|
+
register: register)
|
|
130
143
|
end
|
|
131
144
|
unless items.empty?
|
|
132
145
|
children.concat(items)
|
|
@@ -162,7 +175,7 @@ module Lutaml
|
|
|
162
175
|
items = grouped.fetch(rule.name.to_s, []).each_with_index
|
|
163
176
|
.map do |v, i|
|
|
164
177
|
call(child_type, child_plan, v,
|
|
165
|
-
node: cursor && cursor[i])
|
|
178
|
+
node: cursor && cursor[i], register: register)
|
|
166
179
|
end
|
|
167
180
|
next if items.empty?
|
|
168
181
|
|
|
@@ -25,16 +25,24 @@ module Lutaml
|
|
|
25
25
|
|
|
26
26
|
class << self
|
|
27
27
|
# plan: the compiler's entry for instance's class
|
|
28
|
-
def call(instance, plan)
|
|
28
|
+
def call(instance, plan, register = nil)
|
|
29
|
+
# The child model's declared lutaml_default_register takes
|
|
30
|
+
# precedence over the ambient register — the resolve_for_child
|
|
31
|
+
# contract (#876); without it, child plans and attribute
|
|
32
|
+
# serialization resolve in the parent context.
|
|
33
|
+
register = Lutaml::Model::Register.resolve_for_child(
|
|
34
|
+
instance.class,
|
|
35
|
+
register || Lutaml::Model::Config.default_register,
|
|
36
|
+
)
|
|
29
37
|
return nil if plan[:ordered] && instance.element_order.nil?
|
|
30
38
|
|
|
31
39
|
doc = ::Leptris::XML::Document.create
|
|
32
40
|
root = doc.create_element(plan[:tree][:name])
|
|
33
41
|
doc.root = root
|
|
34
42
|
if plan[:ordered]
|
|
35
|
-
build_ordered(root, instance, plan, doc)
|
|
43
|
+
build_ordered(root, instance, plan, doc, register)
|
|
36
44
|
else
|
|
37
|
-
build(root, instance, plan, doc)
|
|
45
|
+
build(root, instance, plan, doc, register)
|
|
38
46
|
end
|
|
39
47
|
doc.to_xml(indent: 2, no_decl: true)
|
|
40
48
|
end
|
|
@@ -54,8 +62,9 @@ module Lutaml
|
|
|
54
62
|
Lutaml::Model::Config.default_register
|
|
55
63
|
end
|
|
56
64
|
|
|
57
|
-
def build(element, instance, plan, doc)
|
|
58
|
-
|
|
65
|
+
def build(element, instance, plan, doc, reg = nil)
|
|
66
|
+
reg ||= register
|
|
67
|
+
write_attributes(element, instance, plan, reg)
|
|
59
68
|
plan[:rows].each do |rule, attr, kind, spelling, delegate|
|
|
60
69
|
value = value_of(instance, attr, delegate)
|
|
61
70
|
next unless rule.render?(value, instance)
|
|
@@ -67,24 +76,25 @@ module Lutaml
|
|
|
67
76
|
# as the interpretive writer does.
|
|
68
77
|
Array(value).each do |item|
|
|
69
78
|
add_leaf(element, row_name(rule, spelling),
|
|
70
|
-
attr.serialize(item, :xml,
|
|
79
|
+
attr.serialize(item, :xml, reg), doc,
|
|
71
80
|
attrs: rule.when_attribute)
|
|
72
81
|
end
|
|
73
82
|
when :collection_native, :collection_cb
|
|
74
83
|
Array(value).each do |item|
|
|
75
84
|
add_leaf(element, rule.name.to_s,
|
|
76
|
-
attr.serialize(item, :xml,
|
|
85
|
+
attr.serialize(item, :xml, reg), doc,
|
|
77
86
|
attrs: rule.when_attribute)
|
|
78
87
|
end
|
|
79
88
|
when :nested, :ordered_deferred
|
|
80
89
|
Array(value).each do |item|
|
|
81
|
-
child_plan = PlanCompiler.compile(item.class,
|
|
90
|
+
child_plan = PlanCompiler.compile(item.class, reg)
|
|
82
91
|
child = element.create_child(child_plan[:tree][:name])
|
|
83
92
|
if child_plan[:ordered]
|
|
84
|
-
return nil unless build_ordered(child, item, child_plan,
|
|
93
|
+
return nil unless build_ordered(child, item, child_plan,
|
|
94
|
+
doc, reg)
|
|
85
95
|
|
|
86
96
|
else
|
|
87
|
-
build(child, item, child_plan, doc)
|
|
97
|
+
build(child, item, child_plan, doc, reg)
|
|
88
98
|
end
|
|
89
99
|
end
|
|
90
100
|
when :raw
|
|
@@ -103,8 +113,9 @@ module Lutaml
|
|
|
103
113
|
# the model are reflected; element_order text is the fallback.
|
|
104
114
|
# PIs drop (interpretive parity). Returns nil when a nested
|
|
105
115
|
# ordered child lacks element_order (caller falls back).
|
|
106
|
-
def build_ordered(element, instance, plan, doc)
|
|
107
|
-
|
|
116
|
+
def build_ordered(element, instance, plan, doc, reg = nil)
|
|
117
|
+
reg ||= register
|
|
118
|
+
write_attributes(element, instance, plan, reg)
|
|
108
119
|
rows_by_name = {}
|
|
109
120
|
plan[:rows].each do |rule, attr, kind, spelling, delegate|
|
|
110
121
|
next unless rule.name
|
|
@@ -162,7 +173,7 @@ module Lutaml
|
|
|
162
173
|
|
|
163
174
|
element_indices[object.name] += 1
|
|
164
175
|
add_leaf(element, object.name,
|
|
165
|
-
attr.serialize(items[index], :xml,
|
|
176
|
+
attr.serialize(items[index], :xml, reg), doc)
|
|
166
177
|
when :nested, :ordered_deferred
|
|
167
178
|
item = if attr.collection?
|
|
168
179
|
index = element_indices[object.name]
|
|
@@ -173,18 +184,19 @@ module Lutaml
|
|
|
173
184
|
end
|
|
174
185
|
next unless item
|
|
175
186
|
|
|
176
|
-
child_plan = PlanCompiler.compile(item.class,
|
|
187
|
+
child_plan = PlanCompiler.compile(item.class, reg)
|
|
177
188
|
child = element.create_child(child_plan[:tree][:name])
|
|
178
189
|
if child_plan[:ordered]
|
|
179
|
-
return nil unless build_ordered(child, item, child_plan,
|
|
190
|
+
return nil unless build_ordered(child, item, child_plan,
|
|
191
|
+
doc, reg)
|
|
180
192
|
else
|
|
181
|
-
build(child, item, child_plan, doc)
|
|
193
|
+
build(child, item, child_plan, doc, reg)
|
|
182
194
|
end
|
|
183
195
|
when :raw
|
|
184
196
|
element.add_child(value.to_s) unless value.nil?
|
|
185
197
|
when :scalar
|
|
186
198
|
add_leaf(element, object.name,
|
|
187
|
-
attr.serialize(value, :xml,
|
|
199
|
+
attr.serialize(value, :xml, reg), doc)
|
|
188
200
|
end
|
|
189
201
|
end
|
|
190
202
|
end
|
|
@@ -193,7 +205,7 @@ module Lutaml
|
|
|
193
205
|
|
|
194
206
|
# Attributes in document order when the instance recorded it
|
|
195
207
|
# (parsed models), else plan row order.
|
|
196
|
-
def write_attributes(element, instance, plan)
|
|
208
|
+
def write_attributes(element, instance, plan, _reg = nil)
|
|
197
209
|
recorded = instance.attribute_order
|
|
198
210
|
if recorded && !recorded.empty?
|
|
199
211
|
by_name = {}
|
|
@@ -213,7 +225,7 @@ module Lutaml
|
|
|
213
225
|
end
|
|
214
226
|
end
|
|
215
227
|
|
|
216
|
-
def write_attribute(element, instance, name, entry)
|
|
228
|
+
def write_attribute(element, instance, name, entry, reg = nil)
|
|
217
229
|
return unless entry
|
|
218
230
|
|
|
219
231
|
_rule, attr, delegate = entry
|
|
@@ -221,7 +233,7 @@ module Lutaml
|
|
|
221
233
|
return if value.nil?
|
|
222
234
|
|
|
223
235
|
element.set_attribute(name,
|
|
224
|
-
attr.serialize(value, :xml, register).to_s)
|
|
236
|
+
attr.serialize(value, :xml, reg || register).to_s)
|
|
225
237
|
end
|
|
226
238
|
|
|
227
239
|
# Partition rows (#88) re-emit their discriminator: the wire
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Cross-register embedding on the plan paths (#876)" do
|
|
6
|
+
before do
|
|
7
|
+
stub_const("CrossRegister876", Module.new)
|
|
8
|
+
stub_const("CrossRegister876::Part", part_class)
|
|
9
|
+
stub_const("CrossRegister876::Widget", widget_class)
|
|
10
|
+
stub_const("Host876", host_class)
|
|
11
|
+
|
|
12
|
+
ctx = Lutaml::Model::GlobalContext.create_context(
|
|
13
|
+
id: :crossregister876, registry: Lutaml::Model::TypeRegistry.new,
|
|
14
|
+
)
|
|
15
|
+
ctx.registry.register(:part, CrossRegister876::Part)
|
|
16
|
+
ctx.registry.register(:widget, CrossRegister876::Widget)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:part_class) do
|
|
20
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
21
|
+
def self.lutaml_default_register
|
|
22
|
+
:crossregister876
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
xml do
|
|
26
|
+
element "part"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
let(:widget_class) do
|
|
32
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
33
|
+
def self.lutaml_default_register
|
|
34
|
+
:crossregister876
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attribute :part_value, :part, collection: true
|
|
38
|
+
|
|
39
|
+
xml do
|
|
40
|
+
element "widget"
|
|
41
|
+
ordered
|
|
42
|
+
map_element "part", to: :part_value
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
klass
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
let(:host_class) do
|
|
49
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
50
|
+
attribute :widget, CrossRegister876::Widget
|
|
51
|
+
|
|
52
|
+
xml do
|
|
53
|
+
element "host"
|
|
54
|
+
map_element "widget", to: :widget
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
let(:xml) { "<host><widget><part/></widget></host>" }
|
|
60
|
+
|
|
61
|
+
it "resolves the child's symbol types through the child register" do
|
|
62
|
+
model = Host876.from_xml(xml)
|
|
63
|
+
expect(model.widget.part_value.first).to be_a(CrossRegister876::Part)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "round-trips through both the plan serializer and back" do
|
|
67
|
+
model = Host876.from_xml(xml)
|
|
68
|
+
xml_out = model.to_xml
|
|
69
|
+
expect(xml_out).to include("<widget>")
|
|
70
|
+
expect(xml_out).to include("<part/>")
|
|
71
|
+
|
|
72
|
+
reparsed = Host876.from_xml(xml_out)
|
|
73
|
+
expect(reparsed.widget.part_value.first).to be_a(CrossRegister876::Part)
|
|
74
|
+
end
|
|
75
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lutaml-model
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.65
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -1916,6 +1916,7 @@ files:
|
|
|
1916
1916
|
- spec/lutaml/xml/conformance/xml_schema_instance_spec.rb
|
|
1917
1917
|
- spec/lutaml/xml/content_model_validation_spec.rb
|
|
1918
1918
|
- spec/lutaml/xml/cross_class_dispatch_spec.rb
|
|
1919
|
+
- spec/lutaml/xml/cross_register_plan_spec.rb
|
|
1919
1920
|
- spec/lutaml/xml/data_model_spec.rb
|
|
1920
1921
|
- spec/lutaml/xml/decisions/decision_engine_spec.rb
|
|
1921
1922
|
- spec/lutaml/xml/decisions/decision_spec.rb
|