lutaml-model 0.8.9 → 0.8.11
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-repos.json +1 -0
- data/.rubocop_todo.yml +52 -22
- data/README.adoc +43 -0
- data/docs/_guides/jsonld-serialization.adoc +3 -1
- data/docs/_guides/rdf-serialization.adoc +94 -8
- data/docs/_guides/turtle-serialization.adoc +17 -4
- data/lib/lutaml/jsonld/transform.rb +70 -24
- data/lib/lutaml/key_value/transform.rb +5 -5
- data/lib/lutaml/key_value/transformation/collection_serializer.rb +25 -11
- data/lib/lutaml/key_value/transformation/value_serializer.rb +7 -7
- data/lib/lutaml/key_value/transformation.rb +27 -17
- data/lib/lutaml/model/adapter_resolver.rb +4 -6
- data/lib/lutaml/model/attribute.rb +26 -23
- data/lib/lutaml/model/cached_type_resolver.rb +10 -9
- data/lib/lutaml/model/cli.rb +1 -1
- data/lib/lutaml/model/collection.rb +4 -4
- data/lib/lutaml/model/comparable_model.rb +11 -11
- data/lib/lutaml/model/config.rb +1 -1
- data/lib/lutaml/model/consolidation/dispatcher.rb +1 -1
- data/lib/lutaml/model/consolidation/pattern_chunker.rb +3 -3
- data/lib/lutaml/model/format_registry.rb +6 -4
- data/lib/lutaml/model/global_context.rb +2 -2
- data/lib/lutaml/model/global_register.rb +1 -1
- data/lib/lutaml/model/instrumentation.rb +1 -1
- data/lib/lutaml/model/mapping/mapping_rule.rb +3 -3
- data/lib/lutaml/model/mapping/model_mapping.rb +1 -1
- data/lib/lutaml/model/mapping/model_mapping_rule.rb +1 -1
- data/lib/lutaml/model/register.rb +3 -3
- data/lib/lutaml/model/render_policy.rb +11 -17
- data/lib/lutaml/model/runtime_compatibility.rb +0 -1
- data/lib/lutaml/model/schema/xml_compiler/group.rb +1 -1
- data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +1 -1
- data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -2
- data/lib/lutaml/model/schema/xml_compiler.rb +14 -14
- data/lib/lutaml/model/serialize/attribute_definition.rb +1 -1
- data/lib/lutaml/model/serialize/deserialization_context.rb +50 -0
- data/lib/lutaml/model/serialize/format_conversion.rb +2 -2
- data/lib/lutaml/model/serialize/initialization.rb +44 -7
- data/lib/lutaml/model/serialize/model_import.rb +1 -1
- data/lib/lutaml/model/serialize.rb +8 -1
- data/lib/lutaml/model/services/rule_value_extractor.rb +2 -1
- data/lib/lutaml/model/store.rb +77 -24
- data/lib/lutaml/model/transformation_registry.rb +1 -1
- data/lib/lutaml/model/type_context.rb +7 -1
- data/lib/lutaml/model/type_resolver.rb +1 -6
- data/lib/lutaml/model/utils.rb +19 -6
- data/lib/lutaml/model/validation_framework.rb +1 -1
- data/lib/lutaml/model/value_transformer.rb +2 -2
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/rdf/mapping.rb +19 -13
- data/lib/lutaml/rdf/mapping_rule.rb +19 -2
- data/lib/lutaml/rdf/member_rule.rb +19 -2
- data/lib/lutaml/rdf/transform.rb +20 -11
- data/lib/lutaml/turtle/transform.rb +125 -53
- data/lib/lutaml/xml/adapter/adapter_helpers.rb +1 -1
- data/lib/lutaml/xml/adapter/base_adapter.rb +10 -14
- data/lib/lutaml/xml/adapter/namespace_uri_collector.rb +3 -3
- data/lib/lutaml/xml/adapter/plan_based_builder.rb +14 -14
- data/lib/lutaml/xml/adapter/xml_serializer.rb +3 -3
- data/lib/lutaml/xml/configurable.rb +2 -1
- data/lib/lutaml/xml/data_model.rb +2 -2
- data/lib/lutaml/xml/decisions/decision_context.rb +3 -3
- data/lib/lutaml/xml/decisions/rules/element_form_default_unqualified_rule.rb +1 -1
- data/lib/lutaml/xml/decisions/rules/element_form_option_rule.rb +1 -1
- data/lib/lutaml/xml/decisions/rules/used_prefix_rule.rb +1 -1
- data/lib/lutaml/xml/declaration_plan.rb +2 -2
- data/lib/lutaml/xml/declaration_planner.rb +12 -13
- data/lib/lutaml/xml/document.rb +13 -13
- data/lib/lutaml/xml/format_chooser.rb +3 -3
- data/lib/lutaml/xml/hoisting_algorithm.rb +1 -1
- data/lib/lutaml/xml/mapping.rb +2 -2
- data/lib/lutaml/xml/mapping_rule.rb +16 -3
- data/lib/lutaml/xml/model_transform.rb +17 -19
- data/lib/lutaml/xml/namespace_collector.rb +10 -10
- data/lib/lutaml/xml/namespace_declaration.rb +2 -2
- data/lib/lutaml/xml/namespace_declaration_data.rb +5 -8
- data/lib/lutaml/xml/namespace_scope_config.rb +3 -2
- data/lib/lutaml/xml/namespace_type_resolver.rb +4 -4
- data/lib/lutaml/xml/nokogiri/element.rb +2 -2
- data/lib/lutaml/xml/polymorphic_value_handler.rb +1 -1
- data/lib/lutaml/xml/schema/xsd/base.rb +7 -7
- data/lib/lutaml/xml/schema/xsd/choice.rb +2 -2
- data/lib/lutaml/xml/schema/xsd/complex_type.rb +5 -5
- data/lib/lutaml/xml/schema/xsd/errors/message_builder.rb +3 -3
- data/lib/lutaml/xml/schema/xsd/group.rb +2 -2
- data/lib/lutaml/xml/schema/xsd/sequence.rb +2 -2
- data/lib/lutaml/xml/schema/xsd_schema.rb +5 -5
- data/lib/lutaml/xml/serialization/format_conversion.rb +4 -3
- data/lib/lutaml/xml/transformation/element_builder.rb +4 -2
- data/lib/lutaml/xml/transformation/rule_applier.rb +2 -2
- data/lib/lutaml/xml/transformation/value_serializer.rb +4 -6
- data/lib/lutaml/xml/transformation.rb +4 -4
- data/lib/lutaml/xml/type/configurable.rb +0 -4
- data/lib/lutaml/xml/xml_element.rb +21 -13
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/jsonld/transform_spec.rb +239 -0
- data/spec/lutaml/model/cached_type_resolver_spec.rb +3 -3
- data/spec/lutaml/model/optimization_spec.rb +228 -0
- data/spec/lutaml/model/store_spec.rb +195 -0
- data/spec/lutaml/rdf/mapping_rule_spec.rb +97 -0
- data/spec/lutaml/rdf/mapping_spec.rb +74 -4
- data/spec/lutaml/rdf/member_rule_spec.rb +41 -0
- data/spec/lutaml/rdf/rdf_transform_spec.rb +95 -29
- data/spec/lutaml/turtle/mapping_spec.rb +2 -2
- data/spec/lutaml/turtle/transform_spec.rb +315 -0
- data/spec/lutaml/xml/data_model_spec.rb +10 -28
- metadata +7 -4
|
@@ -175,10 +175,14 @@ options = {})
|
|
|
175
175
|
items.each do |item|
|
|
176
176
|
next if item.nil?
|
|
177
177
|
|
|
178
|
-
key_value = item.
|
|
178
|
+
key_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(key_attribute)
|
|
179
|
+
item.public_send(key_attribute)
|
|
180
|
+
end
|
|
179
181
|
next if key_value.nil?
|
|
180
182
|
|
|
181
|
-
attr_value = item.
|
|
183
|
+
attr_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(value_attribute)
|
|
184
|
+
item.public_send(value_attribute)
|
|
185
|
+
end
|
|
182
186
|
next if attr_value.nil? || Lutaml::Model::Utils.uninitialized?(attr_value)
|
|
183
187
|
|
|
184
188
|
# Get the attribute definition for the value attribute
|
|
@@ -193,7 +197,7 @@ options = {})
|
|
|
193
197
|
item_mapping = item_class.mappings_for(format, register_id)
|
|
194
198
|
item_mapping&.mappings&.each do |mapping_rule|
|
|
195
199
|
if mapping_rule.to == value_attribute
|
|
196
|
-
child_mappings = mapping_rule.child_mappings
|
|
200
|
+
child_mappings = mapping_rule.child_mappings
|
|
197
201
|
break
|
|
198
202
|
end
|
|
199
203
|
end
|
|
@@ -226,7 +230,9 @@ options)
|
|
|
226
230
|
|
|
227
231
|
keyed_hash = {}
|
|
228
232
|
items.each do |item|
|
|
229
|
-
key_value = item.
|
|
233
|
+
key_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(key_attribute)
|
|
234
|
+
item.public_send(key_attribute)
|
|
235
|
+
end
|
|
230
236
|
next if key_value.nil?
|
|
231
237
|
|
|
232
238
|
item_hash = {}
|
|
@@ -281,7 +287,9 @@ options)
|
|
|
281
287
|
items.each do |item|
|
|
282
288
|
next if item.nil?
|
|
283
289
|
|
|
284
|
-
key_value = item.
|
|
290
|
+
key_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(key_attribute)
|
|
291
|
+
item.public_send(key_attribute)
|
|
292
|
+
end
|
|
285
293
|
next if key_value.nil?
|
|
286
294
|
|
|
287
295
|
item_hash = {}
|
|
@@ -340,7 +348,9 @@ options)
|
|
|
340
348
|
value_attribute = find_value_attribute(child_mappings)
|
|
341
349
|
|
|
342
350
|
items.each do |item|
|
|
343
|
-
key_value = item.
|
|
351
|
+
key_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(key_attribute)
|
|
352
|
+
item.public_send(key_attribute)
|
|
353
|
+
end
|
|
344
354
|
next if key_value.nil?
|
|
345
355
|
|
|
346
356
|
if value_attribute
|
|
@@ -371,7 +381,9 @@ options)
|
|
|
371
381
|
# Serialize a keyed collection item with value mapping.
|
|
372
382
|
def serialize_keyed_value_item(keyed_hash, item, key_value,
|
|
373
383
|
value_attribute, options)
|
|
374
|
-
attr_value = item.
|
|
384
|
+
attr_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(value_attribute)
|
|
385
|
+
item.public_send(value_attribute)
|
|
386
|
+
end
|
|
375
387
|
return if attr_value.nil? || Lutaml::Model::Utils.uninitialized?(attr_value)
|
|
376
388
|
|
|
377
389
|
item_class = item.class
|
|
@@ -419,7 +431,9 @@ options)
|
|
|
419
431
|
child_mappings.each do |attr_name, path_spec|
|
|
420
432
|
next if %i[key value].include?(path_spec)
|
|
421
433
|
|
|
422
|
-
attr_value = item.
|
|
434
|
+
attr_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(attr_name)
|
|
435
|
+
item.public_send(attr_name)
|
|
436
|
+
end
|
|
423
437
|
next if attr_value.nil? || Lutaml::Model::Utils.uninitialized?(attr_value)
|
|
424
438
|
|
|
425
439
|
attr_def = nil
|
|
@@ -508,7 +522,7 @@ options)
|
|
|
508
522
|
serialize_collection_value(value, attr_type, options)
|
|
509
523
|
elsif attr_type.is_a?(Class) && attr_type < Lutaml::Model::Serialize
|
|
510
524
|
serialize_nested_model(value, attr_type, options)
|
|
511
|
-
elsif attr_type.
|
|
525
|
+
elsif attr_type.is_a?(Class) && attr_type < Lutaml::Model::Type::Value
|
|
512
526
|
serialize_primitive(value, attr_type)
|
|
513
527
|
else
|
|
514
528
|
value
|
|
@@ -523,7 +537,7 @@ options)
|
|
|
523
537
|
# Handle Reference type collections - serialize each as a key
|
|
524
538
|
if attr_type == Lutaml::Model::Type::Reference
|
|
525
539
|
return items.map do |item|
|
|
526
|
-
item.
|
|
540
|
+
item.is_a?(Lutaml::Model::Type::Value) ? item.public_send(:"to_#{format}") : item
|
|
527
541
|
end
|
|
528
542
|
end
|
|
529
543
|
|
|
@@ -547,7 +561,7 @@ options)
|
|
|
547
561
|
# Serialize a primitive value.
|
|
548
562
|
def serialize_primitive(value, attr_type)
|
|
549
563
|
wrapped_value = attr_type.new(value)
|
|
550
|
-
wrapped_value.
|
|
564
|
+
wrapped_value.public_send(:"to_#{format}")
|
|
551
565
|
end
|
|
552
566
|
|
|
553
567
|
# Create a transformation for a type class.
|
|
@@ -128,13 +128,13 @@ model_class: nil)
|
|
|
128
128
|
if rule.attribute_type.is_a?(Class) &&
|
|
129
129
|
rule.attribute_type < Lutaml::Model::Serialize
|
|
130
130
|
validate_serializable_type!(value, rule)
|
|
131
|
-
return value.
|
|
131
|
+
return value.public_send(:"to_#{format}")
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
# Wrap value in type and call to_#{format}
|
|
135
|
-
if rule.attribute_type.
|
|
135
|
+
if rule.attribute_type.is_a?(Class) && rule.attribute_type < Lutaml::Model::Type::Value
|
|
136
136
|
wrapped_value = rule.attribute_type.new(value)
|
|
137
|
-
wrapped_value.
|
|
137
|
+
wrapped_value.public_send(:"to_#{format}")
|
|
138
138
|
else
|
|
139
139
|
value
|
|
140
140
|
end
|
|
@@ -152,7 +152,7 @@ model_class: nil)
|
|
|
152
152
|
subs = context.substitution_for(rule.attribute_type)
|
|
153
153
|
uses_type_substitution = subs.any? { |s| s.to_type == value.class }
|
|
154
154
|
|
|
155
|
-
if rule.attribute_type.
|
|
155
|
+
if rule.attribute_type.is_a?(Class) && rule.attribute_type.include?(Lutaml::Model::Serialize) && rule.attribute_type.model
|
|
156
156
|
# Mapper class: value should be an instance of the mapped model
|
|
157
157
|
unless value.is_a?(rule.attribute_type.model) || uses_type_substitution
|
|
158
158
|
msg = "attribute '#{rule.attribute_name}' value is a '#{value.class}' " \
|
|
@@ -227,7 +227,7 @@ model_class: nil)
|
|
|
227
227
|
# @param rule [CompiledRule] The compiled rule
|
|
228
228
|
# @return [Boolean] true if Reference type
|
|
229
229
|
def reference_type?(rule)
|
|
230
|
-
return false unless rule.
|
|
230
|
+
return false unless rule.is_a?(Lutaml::Model::CompiledRule)
|
|
231
231
|
return false unless model_class
|
|
232
232
|
|
|
233
233
|
# Get the attribute from the model class to check unresolved_type
|
|
@@ -256,8 +256,8 @@ model_class: nil)
|
|
|
256
256
|
end
|
|
257
257
|
|
|
258
258
|
# Fallback: just call to_format on the value
|
|
259
|
-
if value.
|
|
260
|
-
value.
|
|
259
|
+
if value.is_a?(Lutaml::Model::Type::Value) || value.is_a?(Lutaml::Model::Serialize)
|
|
260
|
+
value.public_send(:"to_#{format}")
|
|
261
261
|
else
|
|
262
262
|
value
|
|
263
263
|
end
|
|
@@ -209,8 +209,8 @@ register = self.register)
|
|
|
209
209
|
# Handle custom serialization methods (e.g., with: { to: ... })
|
|
210
210
|
if rule.has_custom_methods? && rule.custom_methods[:to]
|
|
211
211
|
# Call custom method which directly modifies the parent element
|
|
212
|
-
return model_instance.
|
|
213
|
-
|
|
212
|
+
return model_instance.public_send(rule.custom_methods[:to],
|
|
213
|
+
model_instance, parent)
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
# Handle delegation - extract value from delegated object
|
|
@@ -357,11 +357,15 @@ converted_from_empty_to_nil: false, converted_from_nil_to_empty: false)
|
|
|
357
357
|
next if item.nil?
|
|
358
358
|
|
|
359
359
|
# Get the key value from the item
|
|
360
|
-
key_value = item.
|
|
360
|
+
key_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(key_attribute)
|
|
361
|
+
item.public_send(key_attribute)
|
|
362
|
+
end
|
|
361
363
|
next if key_value.nil?
|
|
362
364
|
|
|
363
365
|
# Get the value from the item
|
|
364
|
-
attr_value = item.
|
|
366
|
+
attr_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(value_attribute)
|
|
367
|
+
item.public_send(value_attribute)
|
|
368
|
+
end
|
|
365
369
|
next if attr_value.nil? || Lutaml::Model::Utils.uninitialized?(attr_value)
|
|
366
370
|
|
|
367
371
|
# For the value, we need to transform it appropriately
|
|
@@ -419,7 +423,7 @@ converted_from_empty_to_nil: false, converted_from_nil_to_empty: false)
|
|
|
419
423
|
item_class_mapping, format, register)
|
|
420
424
|
|
|
421
425
|
# Find the rule for the value_attribute in the item's class mapping
|
|
422
|
-
item_rule = item_class_transformation.
|
|
426
|
+
item_rule = item_class_transformation.compiled_rules.find do |r|
|
|
423
427
|
r.attribute_name == value_attribute
|
|
424
428
|
end
|
|
425
429
|
|
|
@@ -460,8 +464,9 @@ converted_from_empty_to_nil: false, converted_from_nil_to_empty: false)
|
|
|
460
464
|
items.each do |item|
|
|
461
465
|
next if item.nil?
|
|
462
466
|
|
|
463
|
-
|
|
464
|
-
|
|
467
|
+
key_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(key_attribute)
|
|
468
|
+
item.public_send(key_attribute)
|
|
469
|
+
end
|
|
465
470
|
next if key_value.nil?
|
|
466
471
|
|
|
467
472
|
# Serialize all attributes except the key
|
|
@@ -573,7 +578,9 @@ child_mappings, options)
|
|
|
573
578
|
|
|
574
579
|
items.each do |item|
|
|
575
580
|
# Get the key value from the item
|
|
576
|
-
key_value = item.
|
|
581
|
+
key_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(key_attribute)
|
|
582
|
+
item.public_send(key_attribute)
|
|
583
|
+
end
|
|
577
584
|
|
|
578
585
|
if ENV["DEBUG_KEYED_COLLECTION"]
|
|
579
586
|
puts " item: #{item.inspect}, key_value=#{key_value.inspect}"
|
|
@@ -583,7 +590,9 @@ child_mappings, options)
|
|
|
583
590
|
|
|
584
591
|
# If there's a value mapping, serialize just that attribute value
|
|
585
592
|
if value_attribute
|
|
586
|
-
attr_value = item.
|
|
593
|
+
attr_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(value_attribute)
|
|
594
|
+
item.public_send(value_attribute)
|
|
595
|
+
end
|
|
587
596
|
next if attr_value.nil? || Lutaml::Model::Utils.uninitialized?(attr_value)
|
|
588
597
|
|
|
589
598
|
# Get the attribute definition for proper serialization
|
|
@@ -637,7 +646,9 @@ child_mappings, options)
|
|
|
637
646
|
next if path_spec == :value
|
|
638
647
|
|
|
639
648
|
# Get the attribute value
|
|
640
|
-
attr_value = item.
|
|
649
|
+
attr_value = if item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(attr_name)
|
|
650
|
+
item.public_send(attr_name)
|
|
651
|
+
end
|
|
641
652
|
|
|
642
653
|
if ENV["DEBUG_KEYED_COLLECTION"]
|
|
643
654
|
puts " #{attr_name}: path_spec=#{path_spec.inspect}, attr_value=#{attr_value.inspect}"
|
|
@@ -833,10 +844,9 @@ child_mappings, options)
|
|
|
833
844
|
child_hash = child_root.to_hash
|
|
834
845
|
child_hash["__root__"]
|
|
835
846
|
# Use the transformation to serialize the nested model
|
|
836
|
-
elsif attr_type.
|
|
837
|
-
# Serialize primitive value
|
|
847
|
+
elsif attr_type.is_a?(Class) && attr_type < Lutaml::Model::Type::Value
|
|
838
848
|
wrapped_value = attr_type.new(value)
|
|
839
|
-
wrapped_value.
|
|
849
|
+
wrapped_value.public_send(:"to_#{format}")
|
|
840
850
|
else
|
|
841
851
|
value
|
|
842
852
|
end
|
|
@@ -940,7 +950,7 @@ child_mappings, options)
|
|
|
940
950
|
subs = context.substitution_for(rule.attribute_type)
|
|
941
951
|
uses_type_substitution = subs.any? { |s| s.to_type == value.class }
|
|
942
952
|
|
|
943
|
-
if rule.attribute_type.
|
|
953
|
+
if rule.attribute_type.is_a?(Class) && rule.attribute_type.include?(Lutaml::Model::Serialize) && rule.attribute_type.model
|
|
944
954
|
# Mapper class: value should be an instance of the mapped model
|
|
945
955
|
# or a valid substituted type
|
|
946
956
|
unless value.is_a?(rule.attribute_type.model) || uses_type_substitution
|
|
@@ -1054,14 +1064,14 @@ child_mappings, options)
|
|
|
1054
1064
|
end
|
|
1055
1065
|
|
|
1056
1066
|
# Value is a valid Serialize instance - serialize it using its own to_#{format} method
|
|
1057
|
-
return value.
|
|
1067
|
+
return value.public_send(:"to_#{format}")
|
|
1058
1068
|
end
|
|
1059
1069
|
|
|
1060
1070
|
# Wrap value in type and call to_#{format} instance method (like legacy Attribute#serialize_value)
|
|
1061
1071
|
# This allows custom type subclasses to override to_json, to_yaml, etc.
|
|
1062
|
-
if rule.attribute_type.
|
|
1072
|
+
if rule.attribute_type.is_a?(Class) && rule.attribute_type < Lutaml::Model::Type::Value
|
|
1063
1073
|
wrapped_value = rule.attribute_type.new(value)
|
|
1064
|
-
wrapped_value.
|
|
1074
|
+
wrapped_value.public_send(:"to_#{format}")
|
|
1065
1075
|
else
|
|
1066
1076
|
value
|
|
1067
1077
|
end
|
|
@@ -225,7 +225,7 @@ module Lutaml
|
|
|
225
225
|
# @param type [String] normalized type name
|
|
226
226
|
def load_adapter_file(adapter, type)
|
|
227
227
|
loader = FormatRegistry.adapter_loader_for(adapter.to_sym)
|
|
228
|
-
if loader
|
|
228
|
+
if loader
|
|
229
229
|
loader.load_adapter_file(adapter, type)
|
|
230
230
|
return
|
|
231
231
|
end
|
|
@@ -248,10 +248,8 @@ module Lutaml
|
|
|
248
248
|
# @param format [Symbol] format name
|
|
249
249
|
def load_moxml_adapter(type_name, format)
|
|
250
250
|
loader = FormatRegistry.adapter_loader_for(format)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
format)
|
|
254
|
-
end
|
|
251
|
+
loader&.load_moxml_adapter(type_name,
|
|
252
|
+
format)
|
|
255
253
|
end
|
|
256
254
|
|
|
257
255
|
# Resolve the adapter class from the type name.
|
|
@@ -261,7 +259,7 @@ module Lutaml
|
|
|
261
259
|
# @return [Class] the adapter class
|
|
262
260
|
def class_for(adapter, type)
|
|
263
261
|
loader = FormatRegistry.adapter_loader_for(adapter.to_sym)
|
|
264
|
-
if loader
|
|
262
|
+
if loader
|
|
265
263
|
return loader.class_for(adapter, type)
|
|
266
264
|
end
|
|
267
265
|
|
|
@@ -36,20 +36,8 @@ module Lutaml
|
|
|
36
36
|
:string,
|
|
37
37
|
].freeze
|
|
38
38
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
# intentionally excluded — children must derive their own context.
|
|
42
|
-
CHILD_PROPAGATION_KEYS = %i[
|
|
43
|
-
lutaml_parent
|
|
44
|
-
lutaml_root
|
|
45
|
-
default_namespace
|
|
46
|
-
import_declaration_plan
|
|
47
|
-
polymorphic
|
|
48
|
-
collection
|
|
49
|
-
render_empty
|
|
50
|
-
render_nil
|
|
51
|
-
cdata
|
|
52
|
-
].freeze
|
|
39
|
+
# Delegate propagation to DeserializationContext for single source of truth.
|
|
40
|
+
CHILD_PROPAGATION_KEYS = Serialize::DeserializationContext::PROPAGATION_KEYS
|
|
53
41
|
|
|
54
42
|
# Methods where accidental override is likely to cause issues
|
|
55
43
|
# All names are allowed - this list only controls which ones get a warning
|
|
@@ -590,9 +578,10 @@ instance_object = nil)
|
|
|
590
578
|
type(register)
|
|
591
579
|
end
|
|
592
580
|
if collection_instance?(value) || value.is_a?(Array)
|
|
581
|
+
merged_opts = options.merge(resolved_type: resolved_type,
|
|
582
|
+
converted: true)
|
|
593
583
|
return build_collection(value.map do |v|
|
|
594
|
-
cast(v, format, register,
|
|
595
|
-
options.merge(resolved_type: resolved_type, converted: true))
|
|
584
|
+
cast(v, format, register, merged_opts)
|
|
596
585
|
end)
|
|
597
586
|
end
|
|
598
587
|
|
|
@@ -616,11 +605,11 @@ instance_object = nil)
|
|
|
616
605
|
|
|
617
606
|
klass = resolve_polymorphic_class(resolved_type, value, options)
|
|
618
607
|
if can_serialize?(klass, value, format, options)
|
|
619
|
-
propagated =
|
|
608
|
+
propagated = Serialize::DeserializationContext.propagate(options)
|
|
620
609
|
klass.apply_mappings(value, format,
|
|
621
610
|
propagated.merge(register: register))
|
|
622
611
|
elsif needs_conversion?(klass, value)
|
|
623
|
-
klass.
|
|
612
|
+
klass.public_send(:"from_#{format}", value)
|
|
624
613
|
else
|
|
625
614
|
# No need to use register#get_class,
|
|
626
615
|
# can_serialize? method already checks if type is Serializable or not.
|
|
@@ -711,9 +700,7 @@ instance_object = nil)
|
|
|
711
700
|
# Skip validation during deep_dup - options are already validated in original
|
|
712
701
|
# This prevents infinite recursion when process_options! tries to access collection
|
|
713
702
|
self.class.new(name, unresolved_type, duped_options).tap do |dup_attr|
|
|
714
|
-
|
|
715
|
-
dup_attr.send(:raw=, @raw)
|
|
716
|
-
dup_attr.send(:validations=, @validations)
|
|
703
|
+
dup_attr.copy_internal_state_from(self)
|
|
717
704
|
end
|
|
718
705
|
end
|
|
719
706
|
|
|
@@ -765,6 +752,22 @@ instance_object = nil)
|
|
|
765
752
|
type_namespace_class(register)&.prefix_default
|
|
766
753
|
end
|
|
767
754
|
|
|
755
|
+
# Copy internal state from another Attribute instance.
|
|
756
|
+
# Used by `dup` to transfer processed state without calling private writers.
|
|
757
|
+
def copy_internal_state_from(source)
|
|
758
|
+
@raw = source.raw?
|
|
759
|
+
@validations = source.validations
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
# Clear type resolution caches for this attribute.
|
|
763
|
+
# Called by GlobalContext.clear_caches to ensure stale entries
|
|
764
|
+
# from GC'd TypeContext objects don't persist.
|
|
765
|
+
def clear_type_cache
|
|
766
|
+
@type_cache&.clear
|
|
767
|
+
@cached_type_default = nil
|
|
768
|
+
@default_type_context = nil
|
|
769
|
+
end
|
|
770
|
+
|
|
768
771
|
private
|
|
769
772
|
|
|
770
773
|
attr_writer :raw, :validations
|
|
@@ -852,7 +855,7 @@ instance_object = nil)
|
|
|
852
855
|
register
|
|
853
856
|
end
|
|
854
857
|
|
|
855
|
-
as_options =
|
|
858
|
+
as_options = Serialize::DeserializationContext.propagate(options).merge(register: value_register)
|
|
856
859
|
|
|
857
860
|
# Respect mapping layer policy: render_empty from MappingRule
|
|
858
861
|
# Allow empty Serializable models when render_empty: true
|
|
@@ -874,7 +877,7 @@ instance_object = nil)
|
|
|
874
877
|
|
|
875
878
|
def serialize_value(value, format, resolved_type)
|
|
876
879
|
value = wrap_in_type_if_needed(value, resolved_type)
|
|
877
|
-
value.
|
|
880
|
+
value.public_send(:"to_#{format}")
|
|
878
881
|
end
|
|
879
882
|
|
|
880
883
|
def wrap_in_type_if_needed(value, resolved_type)
|
|
@@ -63,10 +63,11 @@ module Lutaml
|
|
|
63
63
|
# @return [Class] The resolved type class
|
|
64
64
|
# @raise [UnknownTypeError] If type cannot be resolved
|
|
65
65
|
def resolve(name, context)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
cache_key = if name.is_a?(Class)
|
|
67
|
+
[context.id, name]
|
|
68
|
+
else
|
|
69
|
+
build_cache_key(name, context)
|
|
70
|
+
end
|
|
70
71
|
|
|
71
72
|
@cache_backend.fetch_or_store(cache_key) do
|
|
72
73
|
@delegate.resolve(name, context)
|
|
@@ -79,14 +80,14 @@ module Lutaml
|
|
|
79
80
|
# @param context [TypeContext] The resolution context
|
|
80
81
|
# @return [Boolean] true if type can be resolved
|
|
81
82
|
def resolvable?(name, context)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
cache_key = if name.is_a?(Class)
|
|
84
|
+
[context.id, name]
|
|
85
|
+
else
|
|
86
|
+
build_cache_key(name, context)
|
|
87
|
+
end
|
|
85
88
|
|
|
86
|
-
# Check cache first (fast path)
|
|
87
89
|
return true if @cache_backend.key?(cache_key)
|
|
88
90
|
|
|
89
|
-
# Not in cache - delegate
|
|
90
91
|
@delegate.resolvable?(name, context)
|
|
91
92
|
end
|
|
92
93
|
|
data/lib/lutaml/model/cli.rb
CHANGED
|
@@ -70,7 +70,7 @@ module Lutaml
|
|
|
70
70
|
format = path.split(".").last.downcase
|
|
71
71
|
format = "yaml" if format == "yml"
|
|
72
72
|
|
|
73
|
-
model.
|
|
73
|
+
model.public_send("from_#{format}", content)
|
|
74
74
|
rescue StandardError => e
|
|
75
75
|
raise StandardError, "Error parsing file #{path}: #{e.message}"
|
|
76
76
|
end
|
|
@@ -292,7 +292,7 @@ module Lutaml
|
|
|
292
292
|
!ctx.stopped?
|
|
293
293
|
}) do |collection, errors, ctx|
|
|
294
294
|
missing_items = collection.select do |instance|
|
|
295
|
-
value = instance.
|
|
295
|
+
value = instance.is_a?(Serialize) ? instance.public_send(field) : nil
|
|
296
296
|
Utils.blank?(value)
|
|
297
297
|
end
|
|
298
298
|
|
|
@@ -420,7 +420,7 @@ module Lutaml
|
|
|
420
420
|
end
|
|
421
421
|
|
|
422
422
|
def extract_field_value(instance, field)
|
|
423
|
-
instance.
|
|
423
|
+
instance.is_a?(Serialize) ? instance.public_send(field) : nil
|
|
424
424
|
end
|
|
425
425
|
|
|
426
426
|
def add_uniqueness_error(errors, field, duplicates, message)
|
|
@@ -553,7 +553,7 @@ lutaml_register: Lutaml::Model::Config.default_register)
|
|
|
553
553
|
key = if field_or_proc.is_a?(Proc)
|
|
554
554
|
field_or_proc.call(item)
|
|
555
555
|
else
|
|
556
|
-
item.
|
|
556
|
+
item.public_send(field_or_proc)
|
|
557
557
|
end
|
|
558
558
|
@index_caches[name][key] = item
|
|
559
559
|
end
|
|
@@ -591,7 +591,7 @@ lutaml_register: Lutaml::Model::Config.default_register)
|
|
|
591
591
|
if field.is_a?(Proc)
|
|
592
592
|
collection.sort_by!(&field)
|
|
593
593
|
else
|
|
594
|
-
collection.sort_by! { |item| item.
|
|
594
|
+
collection.sort_by! { |item| item.public_send(field) }
|
|
595
595
|
end
|
|
596
596
|
end
|
|
597
597
|
|
|
@@ -19,10 +19,10 @@ module Lutaml
|
|
|
19
19
|
|
|
20
20
|
compared_objects[comparison_key(other)] = true
|
|
21
21
|
self.class.attributes.all? do |attr, _|
|
|
22
|
-
attr_value =
|
|
23
|
-
other_value = other.
|
|
22
|
+
attr_value = public_send(attr)
|
|
23
|
+
other_value = other.public_send(attr)
|
|
24
24
|
|
|
25
|
-
if attr_value.
|
|
25
|
+
if attr_value.is_a?(ComparableModel) && same_class?(attr_value)
|
|
26
26
|
attr_value.eql?(other_value, compared_objects)
|
|
27
27
|
else
|
|
28
28
|
attr_value == other_value
|
|
@@ -59,9 +59,9 @@ module Lutaml
|
|
|
59
59
|
|
|
60
60
|
def attributes_hash(processed_objects)
|
|
61
61
|
self.class.attributes.map do |attr, _|
|
|
62
|
-
attr_value =
|
|
62
|
+
attr_value = public_send(attr)
|
|
63
63
|
|
|
64
|
-
if attr_value.
|
|
64
|
+
if attr_value.is_a?(ComparableModel)
|
|
65
65
|
attr_value.calculate_hash(processed_objects)
|
|
66
66
|
else
|
|
67
67
|
attr_value.hash
|
|
@@ -205,7 +205,7 @@ module Lutaml
|
|
|
205
205
|
return yield nil, nil, obj1, obj2, true if obj1.class != obj2.class
|
|
206
206
|
|
|
207
207
|
obj1.class.attributes.each_with_index do |(name, type), index|
|
|
208
|
-
yield name, type, obj1.
|
|
208
|
+
yield name, type, obj1.public_send(name), obj2.public_send(name), index == obj1.class.attributes.length - 1
|
|
209
209
|
end
|
|
210
210
|
end
|
|
211
211
|
|
|
@@ -398,7 +398,7 @@ module Lutaml
|
|
|
398
398
|
return format_value(obj) unless obj.is_a?(ComparableModel)
|
|
399
399
|
|
|
400
400
|
obj.class.attributes.map do |attr, _|
|
|
401
|
-
"#{attr}: #{format_value(obj.
|
|
401
|
+
"#{attr}: #{format_value(obj.public_send(attr))}"
|
|
402
402
|
end.join("\n")
|
|
403
403
|
end
|
|
404
404
|
|
|
@@ -426,7 +426,7 @@ module Lutaml
|
|
|
426
426
|
def type_name(type)
|
|
427
427
|
if type.is_a?(Class)
|
|
428
428
|
type.name
|
|
429
|
-
elsif type.
|
|
429
|
+
elsif type.is_a?(Attribute)
|
|
430
430
|
type.type.name
|
|
431
431
|
else
|
|
432
432
|
type.class.name
|
|
@@ -439,8 +439,8 @@ module Lutaml
|
|
|
439
439
|
# @return [String] Formatted attributes of the objects
|
|
440
440
|
def format_object_attributes(obj1, obj2, parent_node)
|
|
441
441
|
obj1.class.attributes.each_key do |attr|
|
|
442
|
-
value1 = obj1.
|
|
443
|
-
value2 = obj2.
|
|
442
|
+
value1 = obj1.public_send(attr)
|
|
443
|
+
value2 = obj2.public_send(attr)
|
|
444
444
|
|
|
445
445
|
attr_type = obj1.class.attributes[attr].collection? ? "collection" : type_name(obj1.class.attributes[attr])
|
|
446
446
|
|
|
@@ -525,7 +525,7 @@ type_info = nil)
|
|
|
525
525
|
# @return [String] Formatted ComparableModel object
|
|
526
526
|
def format_comparable_mapper(obj, parent_node, color = nil)
|
|
527
527
|
obj.class.attributes.each do |attr_name, attr_type|
|
|
528
|
-
attr_value = obj.
|
|
528
|
+
attr_value = obj.public_send(attr_name)
|
|
529
529
|
attr_node = Tree.new("#{attr_name} (#{type_name(attr_type)}):",
|
|
530
530
|
color: color)
|
|
531
531
|
parent_node.add_child(attr_node)
|
data/lib/lutaml/model/config.rb
CHANGED
|
@@ -125,7 +125,7 @@ module Lutaml
|
|
|
125
125
|
#
|
|
126
126
|
# @param format [Symbol] the format name
|
|
127
127
|
def define_adapter_type_methods(format)
|
|
128
|
-
return if
|
|
128
|
+
return if method_defined?(:"#{format}_adapter_type=")
|
|
129
129
|
|
|
130
130
|
# Adapter class getter (returns Class)
|
|
131
131
|
define_method(:"#{format}_adapter") do
|
|
@@ -53,7 +53,7 @@ module Lutaml
|
|
|
53
53
|
# @param token [Object] a mixed content token
|
|
54
54
|
# @return [Symbol] :element or :text
|
|
55
55
|
def token_type(token)
|
|
56
|
-
if token.
|
|
56
|
+
if token.is_a?(Lutaml::Xml::XmlElement)
|
|
57
57
|
token.node_type == :element ? :element : :text
|
|
58
58
|
elsif token.is_a?(Hash)
|
|
59
59
|
token[:type] || (token.key?(:text) ? :text : :element)
|
|
@@ -65,7 +65,7 @@ module Lutaml
|
|
|
65
65
|
# @param token [Object] a mixed content token
|
|
66
66
|
# @return [String, nil] the element name
|
|
67
67
|
def token_name(token)
|
|
68
|
-
if token.
|
|
68
|
+
if token.is_a?(Lutaml::Xml::XmlElement)
|
|
69
69
|
token.name
|
|
70
70
|
elsif token.is_a?(Hash)
|
|
71
71
|
token[:name]
|
|
@@ -75,7 +75,7 @@ module Lutaml
|
|
|
75
75
|
# @param token [Object] a mixed content token
|
|
76
76
|
# @return [String, nil] the text content
|
|
77
77
|
def token_text(token)
|
|
78
|
-
if token.
|
|
78
|
+
if token.is_a?(Lutaml::Xml::XmlElement)
|
|
79
79
|
token.text
|
|
80
80
|
elsif token.is_a?(Hash)
|
|
81
81
|
token[:text]
|
|
@@ -163,10 +163,12 @@ module Lutaml
|
|
|
163
163
|
end
|
|
164
164
|
|
|
165
165
|
# Aliased _type methods
|
|
166
|
-
cfg.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
cfg.class_eval do
|
|
167
|
+
alias_method :"#{format}_adapter_type=", :"#{format}_adapter="
|
|
168
|
+
end
|
|
169
|
+
cfg.class_eval do
|
|
170
|
+
alias_method :"#{format}_adapter_type", :"#{format}_adapter"
|
|
171
|
+
end
|
|
170
172
|
end
|
|
171
173
|
|
|
172
174
|
# Derive a symbol adapter name from an adapter class.
|
|
@@ -181,7 +181,7 @@ module Lutaml
|
|
|
181
181
|
@resolver.clear_all_caches
|
|
182
182
|
@imports.reset!
|
|
183
183
|
@format_registries.each_value do |reg|
|
|
184
|
-
reg.clear! if reg.
|
|
184
|
+
reg.clear! if reg.is_a?(FormatRegistry)
|
|
185
185
|
end
|
|
186
186
|
@namespace_register_map.clear
|
|
187
187
|
@default_context_id = :default
|
|
@@ -227,7 +227,7 @@ module Lutaml
|
|
|
227
227
|
# @return [void]
|
|
228
228
|
def clear_format_registry!(format)
|
|
229
229
|
reg = @format_registries[format]
|
|
230
|
-
reg&.clear! if reg.
|
|
230
|
+
reg&.clear! if reg.is_a?(FormatRegistry)
|
|
231
231
|
end
|
|
232
232
|
|
|
233
233
|
# Backward-compatible accessor for XML namespace registry.
|
|
@@ -86,7 +86,7 @@ module Lutaml
|
|
|
86
86
|
if ctx
|
|
87
87
|
ctx.registry.names.each do |name|
|
|
88
88
|
model_class = ctx.registry.lookup(name)
|
|
89
|
-
if model_class.
|
|
89
|
+
if model_class.is_a?(Class) && model_class.include?(Lutaml::Model::Serialize)
|
|
90
90
|
model_class.clear_cache(register_id)
|
|
91
91
|
end
|
|
92
92
|
end
|