lutaml-model 0.7.5 → 0.7.6
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/.github/workflows/dependent-tests.yml +4 -0
- data/.github/workflows/rake.yml +12 -0
- data/README.adoc +119 -2
- data/lib/lutaml/model/collection.rb +6 -5
- data/lib/lutaml/model/mapping/mapping_rule.rb +1 -1
- data/lib/lutaml/model/serialize.rb +17 -16
- data/lib/lutaml/model/transform/key_value_transform.rb +20 -24
- data/lib/lutaml/model/transform/xml_transform.rb +7 -7
- data/lib/lutaml/model/transform.rb +10 -9
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml/document.rb +4 -4
- data/spec/lutaml/model/custom_model_spec.rb +3 -15
- data/spec/lutaml/model/defaults_spec.rb +1 -5
- data/spec/lutaml/model/register/xml_spec.rb +1 -3
- data/spec/lutaml/model/xml/root_mappings/nested_child_mappings_spec.rb +164 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 073512f7cfe8a5d5bb7af56cf3c1804e0eb9942f9fe2e3f7e0dcc3b0b9fd879a
|
4
|
+
data.tar.gz: '00916ad5bc08547bd350f59be3c554700cd73c1b5af09e2ce7175f39d3443e45'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0196826861701d9277a81f5e0c52fd0bfdac8859128eddb82aee55c074a3688bc453f89ff40f54f8a40a639d5ac625d18e6f097c4abb88f7ee11294b7594e1ff'
|
7
|
+
data.tar.gz: 50fb93a7b534ae371ebecc62576633027a9cc945ed333cdbc7aa3c32a993a558b196dde0513aa594796cfb442c2837158c9181a101351933778009e4e5a00a05
|
data/.github/workflows/rake.yml
CHANGED
@@ -6,10 +6,22 @@ on:
|
|
6
6
|
push:
|
7
7
|
branches: [ master, main ]
|
8
8
|
tags: [ v* ]
|
9
|
+
paths-ignore:
|
10
|
+
- '**.adoc'
|
9
11
|
pull_request:
|
12
|
+
paths-ignore:
|
13
|
+
- '**.adoc'
|
10
14
|
|
11
15
|
jobs:
|
12
16
|
rake:
|
13
17
|
uses: metanorma/ci/.github/workflows/generic-rake.yml@main
|
18
|
+
with:
|
19
|
+
before-setup-ruby: |
|
20
|
+
mkdir -p .bundle
|
21
|
+
touch .bundle/config
|
22
|
+
cat .bundle/config
|
23
|
+
echo "---" >> .bundle/config
|
24
|
+
echo 'BUNDLE_BUILD__RUBY___LL: "--with-cflags=-std=gnu17"' >> .bundle/config
|
25
|
+
cat .bundle/config
|
14
26
|
secrets:
|
15
27
|
pat_token: ${{ secrets.LUTAML_CI_PAT_TOKEN }}
|
data/README.adoc
CHANGED
@@ -1813,7 +1813,7 @@ invalid_xml = <<~XML
|
|
1813
1813
|
<zip>12345</zip>
|
1814
1814
|
</Person>
|
1815
1815
|
XML
|
1816
|
-
Person.from_xml(valid_xml) # #<Person:0x00000002d56b3988 @city="Metropolis", @name="John Doe", @street="123 Main St", @zip="12345">
|
1816
|
+
Person.from_xml(valid_xml) # #<Person:0x00000002d56b3988 @city="Metropolis", @name="John Doe", @street="123 Main St", @zip="12345">
|
1817
1817
|
Person.from_xml(invalid_xml) # raises `Element `zip` does not match the expected sequence order element `city` (Lutaml::Model::IncorrectSequenceError)`
|
1818
1818
|
----
|
1819
1819
|
====
|
@@ -1835,7 +1835,7 @@ class ContactEmail < Lutaml::Model::Serializable
|
|
1835
1835
|
attribute :email, :string
|
1836
1836
|
|
1837
1837
|
xml do
|
1838
|
-
no_root
|
1838
|
+
no_root
|
1839
1839
|
|
1840
1840
|
map_element :email, to: :email
|
1841
1841
|
end
|
@@ -3151,6 +3151,123 @@ authors.first.name
|
|
3151
3151
|
====
|
3152
3152
|
|
3153
3153
|
|
3154
|
+
==== Nested keyed object collection
|
3155
|
+
|
3156
|
+
A nested keyed object collection is a keyed collection that contain other keyed
|
3157
|
+
collections. This case is simply a more complex arrangement of the principles
|
3158
|
+
applied to keyed object collections.
|
3159
|
+
|
3160
|
+
This pattern can extend to multiple levels of nesting, where each level contains
|
3161
|
+
a keyed object collection that can have its own key and value mappings.
|
3162
|
+
|
3163
|
+
Depends on whether a custom collection class is needed, the following
|
3164
|
+
mechanisms are available:
|
3165
|
+
|
3166
|
+
* When using a Lutaml::Model::Serializable class for a keyed collection,
|
3167
|
+
use the `child_mappings` option to map attributes.
|
3168
|
+
|
3169
|
+
* When using a Lutaml::Model::Collection class for a keyed collection,
|
3170
|
+
there are two options:
|
3171
|
+
|
3172
|
+
* use the `map_key`, `map_value`, and `map_instances` methods to map attributes;
|
3173
|
+
or
|
3174
|
+
|
3175
|
+
* use the `root_mappings` option to map attributes.
|
3176
|
+
|
3177
|
+
|
3178
|
+
.Nested 2-layer keyed object collection
|
3179
|
+
[example]
|
3180
|
+
====
|
3181
|
+
This example provides a two-layer nested structure where:
|
3182
|
+
|
3183
|
+
* The first layer keys pieces by type (`bowls`, `vases`).
|
3184
|
+
* The second layer keys glazes by finish name within each piece type.
|
3185
|
+
* Each glaze finish contains detailed attributes like temperature.
|
3186
|
+
|
3187
|
+
[source,ruby]
|
3188
|
+
----
|
3189
|
+
# Third layer represents glaze finishes.
|
3190
|
+
class GlazeFinish < Lutaml::Model::Serializable
|
3191
|
+
attribute :name, :string
|
3192
|
+
attribute :temperature, :integer
|
3193
|
+
|
3194
|
+
key_value do
|
3195
|
+
map "name", to: :name
|
3196
|
+
map "temperature", to: :temperature
|
3197
|
+
end
|
3198
|
+
end
|
3199
|
+
|
3200
|
+
# Second layer represents ceramic pieces each with multiple finishes.
|
3201
|
+
class CeramicPiece < Lutaml::Model::Serializable
|
3202
|
+
attribute :piece_type, :string
|
3203
|
+
attribute :glazes, GlazeFinish, collection: true
|
3204
|
+
|
3205
|
+
key_value do
|
3206
|
+
map "piece_type", to: :piece_type
|
3207
|
+
map "glazes", to: :glazes, child_mappings: {
|
3208
|
+
name: :key,
|
3209
|
+
temperature: :temperature
|
3210
|
+
}
|
3211
|
+
end
|
3212
|
+
end
|
3213
|
+
|
3214
|
+
# Uppermost layer represents the collection of ceramic pieces.
|
3215
|
+
class StudioInventory < Lutaml::Model::Collection
|
3216
|
+
instances :pieces, CeramicPiece
|
3217
|
+
|
3218
|
+
key_value do
|
3219
|
+
map to: :pieces, root_mappings: {
|
3220
|
+
piece_type: :key,
|
3221
|
+
glazes: :value,
|
3222
|
+
}
|
3223
|
+
end
|
3224
|
+
end
|
3225
|
+
----
|
3226
|
+
|
3227
|
+
[source,yaml]
|
3228
|
+
----
|
3229
|
+
---
|
3230
|
+
bowls:
|
3231
|
+
matte_finish:
|
3232
|
+
name: Earth Matte
|
3233
|
+
temperature: 1240
|
3234
|
+
glossy_finish:
|
3235
|
+
name: Ocean Blue
|
3236
|
+
temperature: 1260
|
3237
|
+
crackle_finish:
|
3238
|
+
name: Antique Crackle
|
3239
|
+
temperature: 1220
|
3240
|
+
vases:
|
3241
|
+
metallic_finish:
|
3242
|
+
name: Bronze Metallic
|
3243
|
+
temperature: 1280
|
3244
|
+
crystalline_finish:
|
3245
|
+
name: Ice Crystal
|
3246
|
+
temperature: 1300
|
3247
|
+
----
|
3248
|
+
|
3249
|
+
[source,ruby]
|
3250
|
+
----
|
3251
|
+
inventory = StudioInventory.from_yaml(yaml_data)
|
3252
|
+
|
3253
|
+
# Access nested data through the hierarchy
|
3254
|
+
puts inventory.pieces.bowls.matte_finish.name
|
3255
|
+
# => "Earth Matte"
|
3256
|
+
|
3257
|
+
puts inventory.pieces.bowls.matte_finish.temperature
|
3258
|
+
# => 1240
|
3259
|
+
|
3260
|
+
# Iterate through all pieces and their glazes
|
3261
|
+
inventory.pieces.each do |piece_type, piece|
|
3262
|
+
puts "#{piece_type.capitalize}:"
|
3263
|
+
piece.glazes.each do |glaze_name, glaze|
|
3264
|
+
puts " #{glaze_name}: #{glaze.name} (#{glaze.temperature}°C)"
|
3265
|
+
end
|
3266
|
+
end
|
3267
|
+
----
|
3268
|
+
====
|
3269
|
+
|
3270
|
+
|
3154
3271
|
|
3155
3272
|
|
3156
3273
|
=== Behavior
|
@@ -87,15 +87,15 @@ module Lutaml
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
attr_reader :
|
90
|
+
attr_reader :__register
|
91
91
|
|
92
|
-
def initialize(items = [],
|
92
|
+
def initialize(items = [], __register: Lutaml::Model::Config.default_register)
|
93
93
|
super()
|
94
94
|
|
95
|
-
@
|
95
|
+
@__register = __register
|
96
96
|
items = [items].compact unless items.is_a?(Array)
|
97
97
|
|
98
|
-
register_object = Lutaml::Model::GlobalRegister.lookup(
|
98
|
+
register_object = Lutaml::Model::GlobalRegister.lookup(@__register)
|
99
99
|
type = register_object.get_class_without_register(self.class.instance_type)
|
100
100
|
self.collection = items.map do |item|
|
101
101
|
if item.is_a?(type)
|
@@ -103,7 +103,8 @@ module Lutaml
|
|
103
103
|
elsif type <= Lutaml::Model::Type::Value
|
104
104
|
type.cast(item)
|
105
105
|
else
|
106
|
-
|
106
|
+
item[:__register] = __register
|
107
|
+
type.new(item)
|
107
108
|
end
|
108
109
|
end
|
109
110
|
|
@@ -289,7 +289,7 @@ module Lutaml
|
|
289
289
|
delegate_value = model.public_send(delegate)
|
290
290
|
return if Utils.initialized?(delegate_value) && !delegate_value.nil?
|
291
291
|
|
292
|
-
model.public_send(:"#{delegate}=", attributes[delegate].type(model.
|
292
|
+
model.public_send(:"#{delegate}=", attributes[delegate].type(model.__register).new)
|
293
293
|
end
|
294
294
|
|
295
295
|
def handle_transform_method(model, value, attributes)
|
@@ -123,7 +123,7 @@ module Lutaml
|
|
123
123
|
end
|
124
124
|
define_method(:"#{name}=") do |value|
|
125
125
|
value_set_for(name)
|
126
|
-
instance_variable_set(:"@#{name}", attr.cast_value(value,
|
126
|
+
instance_variable_set(:"@#{name}", attr.cast_value(value, __register))
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
@@ -412,7 +412,7 @@ module Lutaml
|
|
412
412
|
instance = if options.key?(:instance)
|
413
413
|
options[:instance]
|
414
414
|
elsif model.include?(Lutaml::Model::Serialize)
|
415
|
-
model.new({
|
415
|
+
model.new({ __register: register })
|
416
416
|
else
|
417
417
|
object = model.new
|
418
418
|
register_accessor_methods_for(object, register)
|
@@ -438,7 +438,7 @@ module Lutaml
|
|
438
438
|
klass_name = polymorphic_mapping.polymorphic_map[klass_key]
|
439
439
|
klass = Object.const_get(klass_name)
|
440
440
|
|
441
|
-
klass.apply_mappings(doc, format, options.merge(register: instance.
|
441
|
+
klass.apply_mappings(doc, format, options.merge(register: instance.__register))
|
442
442
|
end
|
443
443
|
|
444
444
|
def apply_value_map(value, value_map, attr)
|
@@ -485,13 +485,13 @@ module Lutaml
|
|
485
485
|
end
|
486
486
|
|
487
487
|
def register_accessor_methods_for(object, register)
|
488
|
-
Utils.add_singleton_method_if_not_defined(object, :
|
489
|
-
@
|
488
|
+
Utils.add_singleton_method_if_not_defined(object, :__register) do
|
489
|
+
@__register
|
490
490
|
end
|
491
|
-
Utils.add_singleton_method_if_not_defined(object, :
|
492
|
-
@
|
491
|
+
Utils.add_singleton_method_if_not_defined(object, :__register=) do |value|
|
492
|
+
@__register = value
|
493
493
|
end
|
494
|
-
object.
|
494
|
+
object.__register = register
|
495
495
|
end
|
496
496
|
|
497
497
|
def extract_register_id(register)
|
@@ -583,20 +583,21 @@ module Lutaml
|
|
583
583
|
end
|
584
584
|
end
|
585
585
|
|
586
|
-
attr_accessor :element_order, :schema_location, :encoding, :
|
586
|
+
attr_accessor :element_order, :schema_location, :encoding, :__register
|
587
587
|
attr_writer :ordered, :mixed
|
588
588
|
|
589
589
|
def initialize(attrs = {}, options = {})
|
590
590
|
@using_default = {}
|
591
591
|
return unless self.class.attributes
|
592
592
|
|
593
|
-
@
|
593
|
+
@__register = extract_register_id(attrs, options)
|
594
594
|
set_ordering(attrs)
|
595
595
|
set_schema_location(attrs)
|
596
596
|
initialize_attributes(attrs, options)
|
597
597
|
end
|
598
598
|
|
599
|
-
def extract_register_id(
|
599
|
+
def extract_register_id(attrs, options)
|
600
|
+
register = attrs&.dig(:__register) || options&.dig(:register)
|
600
601
|
self.class.extract_register_id(register)
|
601
602
|
end
|
602
603
|
|
@@ -609,8 +610,8 @@ module Lutaml
|
|
609
610
|
end
|
610
611
|
|
611
612
|
def attr_value(attrs, name, attribute)
|
612
|
-
value = Utils.fetch_str_or_sym(attrs, name, attribute.default(
|
613
|
-
attribute.cast_value(value,
|
613
|
+
value = Utils.fetch_str_or_sym(attrs, name, attribute.default(__register))
|
614
|
+
attribute.cast_value(value, __register)
|
614
615
|
end
|
615
616
|
|
616
617
|
def using_default_for(attribute_name)
|
@@ -670,7 +671,7 @@ module Lutaml
|
|
670
671
|
end
|
671
672
|
|
672
673
|
def pretty_print_instance_variables
|
673
|
-
(instance_variables - %i[@using_default]).sort
|
674
|
+
(instance_variables - %i[@using_default @__register]).sort
|
674
675
|
end
|
675
676
|
|
676
677
|
def to_yaml_hash
|
@@ -722,9 +723,9 @@ module Lutaml
|
|
722
723
|
def determine_value(attrs, name, attr)
|
723
724
|
if attrs.key?(name) || attrs.key?(name.to_s)
|
724
725
|
attr_value(attrs, name, attr)
|
725
|
-
elsif attr.default_set?(
|
726
|
+
elsif attr.default_set?(__register)
|
726
727
|
using_default_for(name)
|
727
|
-
attr.default(
|
728
|
+
attr.default(__register)
|
728
729
|
else
|
729
730
|
Lutaml::Model::UninitializedClass.instance
|
730
731
|
end
|
@@ -2,11 +2,12 @@ module Lutaml
|
|
2
2
|
module Model
|
3
3
|
class KeyValueTransform < Lutaml::Model::Transform
|
4
4
|
def data_to_model(data, format, options = {})
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
if model_class.include?(Lutaml::Model::Serialize)
|
6
|
+
instance = model_class.new(__register: __register)
|
7
|
+
else
|
8
|
+
instance = model_class.new
|
9
|
+
register_accessor_methods_for(instance, __register)
|
10
|
+
end
|
10
11
|
mappings = extract_mappings(options, format)
|
11
12
|
|
12
13
|
Utils.add_if_present(options, :key_mappings, mappings.key_mappings)
|
@@ -93,7 +94,7 @@ module Lutaml
|
|
93
94
|
end
|
94
95
|
|
95
96
|
def serialize_value(value, rule, attr, format, options)
|
96
|
-
return attr.serialize(value, format,
|
97
|
+
return attr.serialize(value, format, __register, options) unless rule.child_mappings
|
97
98
|
|
98
99
|
generate_hash_from_child_mappings(
|
99
100
|
attr, value, format, rule.child_mappings
|
@@ -111,12 +112,12 @@ module Lutaml
|
|
111
112
|
|
112
113
|
generate_remaining_mappings_for_value(child_mappings, value, format)
|
113
114
|
|
114
|
-
attr_type = attr.type(
|
115
|
+
attr_type = attr.type(__register)
|
115
116
|
value.each do |child_obj|
|
116
117
|
rules = attr_type.mappings_for(format)
|
117
118
|
|
118
119
|
hash.merge!(
|
119
|
-
extract_hash_for_child_mapping(child_mappings, child_obj, rules),
|
120
|
+
extract_hash_for_child_mapping(child_mappings, child_obj, rules, format),
|
120
121
|
)
|
121
122
|
end
|
122
123
|
|
@@ -144,14 +145,13 @@ module Lutaml
|
|
144
145
|
mappings.find_by_to(name)&.name.to_s
|
145
146
|
end
|
146
147
|
|
147
|
-
def extract_hash_for_child_mapping(child_mappings, child_obj, rules)
|
148
|
+
def extract_hash_for_child_mapping(child_mappings, child_obj, rules, format)
|
148
149
|
key = nil
|
149
150
|
value = {}
|
150
151
|
|
151
152
|
child_mappings.each do |attr_name, path|
|
152
153
|
rule = rules.find_by_to(attr_name)
|
153
|
-
|
154
|
-
attr_value = normalize_attribute_value(child_obj.send(attr_name))
|
154
|
+
attr_value = normalize_attribute_value(child_obj, rule.from, format)
|
155
155
|
|
156
156
|
next unless rule&.render?(attr_value, nil)
|
157
157
|
next key = attr_value if path == :key
|
@@ -163,14 +163,10 @@ module Lutaml
|
|
163
163
|
{ key => value }
|
164
164
|
end
|
165
165
|
|
166
|
-
def normalize_attribute_value(value)
|
167
|
-
|
168
|
-
value.
|
169
|
-
|
170
|
-
value.map(&:to_hash)
|
171
|
-
else
|
172
|
-
value
|
173
|
-
end
|
166
|
+
def normalize_attribute_value(value, attr_name, format)
|
167
|
+
Lutaml::Model::Config.adapter_for(format).parse(
|
168
|
+
value.public_send(:"to_#{format}"),
|
169
|
+
)[attr_name.to_s]
|
174
170
|
end
|
175
171
|
|
176
172
|
def extract_hash_value_for_child_mapping(path, value, map_value)
|
@@ -189,7 +185,7 @@ module Lutaml
|
|
189
185
|
return if value.nil? && !rule.render_nil
|
190
186
|
|
191
187
|
attribute = instance.send(rule.delegate).class.attributes[rule.to]
|
192
|
-
hash[rule_from_name(rule)] = attribute.serialize(value, format,
|
188
|
+
hash[rule_from_name(rule)] = attribute.serialize(value, format, __register)
|
193
189
|
end
|
194
190
|
|
195
191
|
def extract_value_for_delegate(instance, rule)
|
@@ -206,7 +202,7 @@ module Lutaml
|
|
206
202
|
|
207
203
|
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule, attr)
|
208
204
|
|
209
|
-
value = rule_value_extractor_class.call(rule, doc, format, attr,
|
205
|
+
value = rule_value_extractor_class.call(rule, doc, format, attr, __register, options)
|
210
206
|
value = apply_value_map(value, rule.value_map(:from, options), attr)
|
211
207
|
|
212
208
|
return process_custom_method(rule, instance, value) if rule.has_custom_method_for_deserialization?
|
@@ -226,7 +222,7 @@ module Lutaml
|
|
226
222
|
|
227
223
|
def cast_value(value, attr, format, rule)
|
228
224
|
cast_options = rule.polymorphic ? { polymorphic: rule.polymorphic } : {}
|
229
|
-
attr.cast(value, format,
|
225
|
+
attr.cast(value, format, __register, cast_options)
|
230
226
|
end
|
231
227
|
|
232
228
|
def translate_mappings(hash, child_mappings, attr, format)
|
@@ -248,7 +244,7 @@ module Lutaml
|
|
248
244
|
end
|
249
245
|
|
250
246
|
def build_child_hash(key, value, child_mappings, attr, format)
|
251
|
-
attr_type = attr.type(
|
247
|
+
attr_type = attr.type(__register)
|
252
248
|
child_mappings.to_h do |attr_name, path|
|
253
249
|
attr_value = extract_attr_value(path, key, value)
|
254
250
|
attr_rule = attr_type.mappings_for(format).find_by_to(attr_name)
|
@@ -271,7 +267,7 @@ module Lutaml
|
|
271
267
|
end
|
272
268
|
|
273
269
|
def map_child_data(child_hash, attr, format)
|
274
|
-
attr_type = attr.type(
|
270
|
+
attr_type = attr.type(__register)
|
275
271
|
self.class.data_to_model(
|
276
272
|
attr_type,
|
277
273
|
child_hash,
|
@@ -3,10 +3,10 @@ module Lutaml
|
|
3
3
|
class XmlTransform < Lutaml::Model::Transform
|
4
4
|
def data_to_model(data, _format, options = {})
|
5
5
|
if model_class.include?(Lutaml::Model::Serialize)
|
6
|
-
instance = model_class.new({
|
6
|
+
instance = model_class.new({ __register: __register })
|
7
7
|
else
|
8
8
|
instance = model_class.new
|
9
|
-
register_accessor_methods_for(instance,
|
9
|
+
register_accessor_methods_for(instance, __register)
|
10
10
|
end
|
11
11
|
apply_xml_mapping(data, instance, options)
|
12
12
|
end
|
@@ -53,7 +53,7 @@ module Lutaml
|
|
53
53
|
|
54
54
|
if (val.nil? || Utils.uninitialized?(val)) && (instance.using_default?(rule.to) || rule.render_default)
|
55
55
|
defaults_used << rule.to
|
56
|
-
attr&.default(
|
56
|
+
attr&.default(__register) || rule.to_value_for(instance)
|
57
57
|
else
|
58
58
|
val
|
59
59
|
end
|
@@ -114,7 +114,7 @@ module Lutaml
|
|
114
114
|
return doc.root.find_attribute_value(rule_names) if rule.attribute?
|
115
115
|
|
116
116
|
attr = attribute_for_rule(rule)
|
117
|
-
attr_type = attr&.type(
|
117
|
+
attr_type = attr&.type(__register)
|
118
118
|
|
119
119
|
children = doc.children.select do |child|
|
120
120
|
rule_names.include?(child.namespaced_name) && !child.text?
|
@@ -136,9 +136,9 @@ module Lutaml
|
|
136
136
|
if !rule.has_custom_method_for_deserialization? && attr_type <= Serialize
|
137
137
|
cast_options = options.except(:mappings)
|
138
138
|
cast_options[:polymorphic] = rule.polymorphic if rule.polymorphic
|
139
|
-
cast_options[:register] =
|
139
|
+
cast_options[:register] = __register
|
140
140
|
|
141
|
-
values << attr.cast(child, :xml,
|
141
|
+
values << attr.cast(child, :xml, __register, cast_options)
|
142
142
|
elsif attr.raw?
|
143
143
|
values << inner_xml_of(child)
|
144
144
|
else
|
@@ -179,7 +179,7 @@ module Lutaml
|
|
179
179
|
|
180
180
|
return value unless cast_value?(attr, rule)
|
181
181
|
|
182
|
-
attr.cast(value, :xml,
|
182
|
+
attr.cast(value, :xml, __register, options)
|
183
183
|
end
|
184
184
|
|
185
185
|
def cast_value?(attr, rule)
|
@@ -6,15 +6,16 @@ module Lutaml
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.model_to_data(context, model, format, options = {})
|
9
|
-
|
9
|
+
register = model.__register if model.respond_to?(:__register)
|
10
|
+
new(context, register).model_to_data(model, format, options)
|
10
11
|
end
|
11
12
|
|
12
|
-
attr_reader :context, :attributes, :
|
13
|
+
attr_reader :context, :attributes, :__register
|
13
14
|
|
14
15
|
def initialize(context, register = nil)
|
15
16
|
@context = context
|
16
17
|
@attributes = context.attributes
|
17
|
-
@
|
18
|
+
@__register = register || Lutaml::Model::Config.default_register
|
18
19
|
end
|
19
20
|
|
20
21
|
def model_class
|
@@ -71,18 +72,18 @@ module Lutaml
|
|
71
72
|
def attribute_for_rule(rule)
|
72
73
|
return attributes[rule.to] unless rule.delegate
|
73
74
|
|
74
|
-
attributes[rule.delegate].type(
|
75
|
+
attributes[rule.delegate].type(__register).attributes[rule.to]
|
75
76
|
end
|
76
77
|
|
77
78
|
def register_accessor_methods_for(object, register)
|
78
79
|
klass = object.class
|
79
|
-
Utils.add_method_if_not_defined(klass, :
|
80
|
-
@
|
80
|
+
Utils.add_method_if_not_defined(klass, :__register) do
|
81
|
+
@__register
|
81
82
|
end
|
82
|
-
Utils.add_method_if_not_defined(klass, :
|
83
|
-
@
|
83
|
+
Utils.add_method_if_not_defined(klass, :__register=) do |value|
|
84
|
+
@__register = value
|
84
85
|
end
|
85
|
-
object.
|
86
|
+
object.__register = register
|
86
87
|
end
|
87
88
|
end
|
88
89
|
end
|
data/lib/lutaml/model/version.rb
CHANGED
@@ -443,10 +443,10 @@ module Lutaml
|
|
443
443
|
|
444
444
|
return_register = if register.is_a?(Lutaml::Model::Register)
|
445
445
|
register.id
|
446
|
-
elsif @root.respond_to?(:
|
447
|
-
@root.
|
448
|
-
elsif @root.instance_variable_defined?(:@
|
449
|
-
@root.instance_variable_get(:@
|
446
|
+
elsif @root.respond_to?(:__register)
|
447
|
+
@root.__register
|
448
|
+
elsif @root.instance_variable_defined?(:@__register)
|
449
|
+
@root.instance_variable_get(:@__register)
|
450
450
|
end
|
451
451
|
return_register || Lutaml::Model::Config.default_register
|
452
452
|
end
|
@@ -1,19 +1,11 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
class CustomModelChild
|
4
|
-
attr_accessor :street, :city
|
5
|
-
|
6
|
-
def initialize(register = nil)
|
7
|
-
@register = register
|
8
|
-
end
|
4
|
+
attr_accessor :street, :city
|
9
5
|
end
|
10
6
|
|
11
7
|
class CustomModelParent
|
12
|
-
attr_accessor :first_name, :middle_name, :last_name, :child_mapper, :math
|
13
|
-
|
14
|
-
def initialize(register = nil)
|
15
|
-
@register = register
|
16
|
-
end
|
8
|
+
attr_accessor :first_name, :middle_name, :last_name, :child_mapper, :math
|
17
9
|
|
18
10
|
def name
|
19
11
|
"#{first_name} #{last_name}"
|
@@ -21,11 +13,7 @@ class CustomModelParent
|
|
21
13
|
end
|
22
14
|
|
23
15
|
class GenericFormulaClass
|
24
|
-
attr_accessor :value
|
25
|
-
|
26
|
-
def initialize(register = nil)
|
27
|
-
@register = register
|
28
|
-
end
|
16
|
+
attr_accessor :value
|
29
17
|
end
|
30
18
|
|
31
19
|
class Mi < Lutaml::Model::Serializable
|
@@ -108,11 +108,7 @@ module DefaultsSpec
|
|
108
108
|
|
109
109
|
# Class for testing render_default: true with custom model
|
110
110
|
class Lang
|
111
|
-
attr_accessor :lang, :content
|
112
|
-
|
113
|
-
def initialize(register = nil)
|
114
|
-
@register = register
|
115
|
-
end
|
111
|
+
attr_accessor :lang, :content
|
116
112
|
end
|
117
113
|
|
118
114
|
class CustomModelWithDefaultValue < Lutaml::Model::Serializable
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module NestedChildMappingsSpec
|
4
|
+
class ManifestFont < Lutaml::Model::Serializable
|
5
|
+
attribute :name, :string
|
6
|
+
attribute :styles, :string, collection: true
|
7
|
+
|
8
|
+
key_value do
|
9
|
+
map "name", to: :name
|
10
|
+
map "styles", to: :styles
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ManifestResponseFontStyle < Lutaml::Model::Serializable
|
15
|
+
attribute :full_name, :string
|
16
|
+
attribute :type, :string
|
17
|
+
attribute :paths, :string, collection: true
|
18
|
+
|
19
|
+
key_value do
|
20
|
+
map "full_name", to: :full_name
|
21
|
+
map "type", to: :type
|
22
|
+
map "paths", to: :paths
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ManifestResponseFont < ManifestFont
|
27
|
+
attribute :name, :string
|
28
|
+
attribute :styles, ManifestResponseFontStyle, collection: true
|
29
|
+
|
30
|
+
key_value do
|
31
|
+
map "name", to: :name
|
32
|
+
map "styles", to: :styles, child_mappings: {
|
33
|
+
type: :key,
|
34
|
+
full_name: :full_name,
|
35
|
+
paths: :paths,
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class ManifestResponse < Lutaml::Model::Collection
|
41
|
+
instances :fonts, ManifestResponseFont
|
42
|
+
|
43
|
+
key_value do
|
44
|
+
map to: :fonts, root_mappings: {
|
45
|
+
name: :key,
|
46
|
+
styles: :value,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
RSpec.describe "NestedChildMappingsSpec" do
|
53
|
+
let(:yaml) do
|
54
|
+
<<~YAML
|
55
|
+
---
|
56
|
+
Yu Gothic:
|
57
|
+
Bold:
|
58
|
+
full_name: Yu Gothic Bold
|
59
|
+
paths:
|
60
|
+
- "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothB.ttc"
|
61
|
+
- "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothB.ttc"
|
62
|
+
- "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothB.ttc"
|
63
|
+
- "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothB.ttc"
|
64
|
+
- "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothB.ttc"
|
65
|
+
Regular:
|
66
|
+
full_name: Yu Gothic Regular
|
67
|
+
paths:
|
68
|
+
- "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothR.ttc"
|
69
|
+
- "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothR.ttc"
|
70
|
+
- "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothR.ttc"
|
71
|
+
- "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothR.ttc"
|
72
|
+
- "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothR.ttc"
|
73
|
+
Noto Sans Condensed:
|
74
|
+
Regular:
|
75
|
+
full_name: Noto Sans Condensed
|
76
|
+
paths:
|
77
|
+
- "/Users/foo/.fontist/fonts/NotoSans-Condensed.ttf"
|
78
|
+
Bold:
|
79
|
+
full_name: Noto Sans Condensed Bold
|
80
|
+
paths:
|
81
|
+
- "/Users/foo/.fontist/fonts/NotoSans-CondensedBold.ttf"
|
82
|
+
Bold Italic:
|
83
|
+
full_name: Noto Sans Condensed Bold Italic
|
84
|
+
paths:
|
85
|
+
- "/Users/foo/.fontist/fonts/NotoSans-CondensedBoldItalic.ttf"
|
86
|
+
Italic:
|
87
|
+
full_name: Noto Sans Condensed Italic
|
88
|
+
paths:
|
89
|
+
- "/Users/foo/.fontist/fonts/NotoSans-CondensedItalic.ttf"
|
90
|
+
YAML
|
91
|
+
end
|
92
|
+
|
93
|
+
let(:parsed_yaml) do
|
94
|
+
NestedChildMappingsSpec::ManifestResponse.from_yaml(yaml)
|
95
|
+
end
|
96
|
+
|
97
|
+
let(:expected_fonts) do
|
98
|
+
NestedChildMappingsSpec::ManifestResponse.new(
|
99
|
+
[
|
100
|
+
NestedChildMappingsSpec::ManifestResponseFont.new(
|
101
|
+
name: "Yu Gothic",
|
102
|
+
styles: [
|
103
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
104
|
+
full_name: "Yu Gothic Bold",
|
105
|
+
type: "Bold",
|
106
|
+
paths: [
|
107
|
+
"/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothB.ttc",
|
108
|
+
"/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothB.ttc",
|
109
|
+
"/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothB.ttc",
|
110
|
+
"/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothB.ttc",
|
111
|
+
"/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothB.ttc",
|
112
|
+
],
|
113
|
+
),
|
114
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
115
|
+
full_name: "Yu Gothic Regular",
|
116
|
+
type: "Regular",
|
117
|
+
paths: [
|
118
|
+
"/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothR.ttc",
|
119
|
+
"/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothR.ttc",
|
120
|
+
"/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothR.ttc",
|
121
|
+
"/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothR.ttc",
|
122
|
+
"/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothR.ttc",
|
123
|
+
],
|
124
|
+
),
|
125
|
+
],
|
126
|
+
),
|
127
|
+
NestedChildMappingsSpec::ManifestResponseFont.new(
|
128
|
+
name: "Noto Sans Condensed",
|
129
|
+
styles: [
|
130
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
131
|
+
full_name: "Noto Sans Condensed",
|
132
|
+
type: "Regular",
|
133
|
+
paths: ["/Users/foo/.fontist/fonts/NotoSans-Condensed.ttf"],
|
134
|
+
),
|
135
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
136
|
+
full_name: "Noto Sans Condensed Bold",
|
137
|
+
type: "Bold",
|
138
|
+
paths: ["/Users/foo/.fontist/fonts/NotoSans-CondensedBold.ttf"],
|
139
|
+
),
|
140
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
141
|
+
full_name: "Noto Sans Condensed Bold Italic",
|
142
|
+
type: "Bold Italic",
|
143
|
+
paths: ["/Users/foo/.fontist/fonts/NotoSans-CondensedBoldItalic.ttf"],
|
144
|
+
),
|
145
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
146
|
+
full_name: "Noto Sans Condensed Italic",
|
147
|
+
type: "Italic",
|
148
|
+
paths: ["/Users/foo/.fontist/fonts/NotoSans-CondensedItalic.ttf"],
|
149
|
+
),
|
150
|
+
],
|
151
|
+
),
|
152
|
+
],
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "parses nested child mappings correctly" do
|
157
|
+
expect(parsed_yaml).to eq(expected_fonts)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "rounds trip correctly" do
|
161
|
+
expected_yaml = parsed_yaml.to_yaml
|
162
|
+
expect(expected_yaml).to eq(yaml)
|
163
|
+
end
|
164
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lutaml-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-07-
|
11
|
+
date: 2025-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: moxml
|
@@ -367,6 +367,7 @@ files:
|
|
367
367
|
- spec/lutaml/model/xml/derived_attributes_spec.rb
|
368
368
|
- spec/lutaml/model/xml/namespace/nested_with_explicit_namespace_spec.rb
|
369
369
|
- spec/lutaml/model/xml/namespace_spec.rb
|
370
|
+
- spec/lutaml/model/xml/root_mappings/nested_child_mappings_spec.rb
|
370
371
|
- spec/lutaml/model/xml/xml_element_spec.rb
|
371
372
|
- spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb
|
372
373
|
- spec/lutaml/model/xml_adapter/oga_adapter_spec.rb
|