lutaml-model 0.8.9 → 0.8.10
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/.rubocop_todo.yml +12 -32
- 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 +27 -21
- 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/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/model/cached_type_resolver_spec.rb +3 -3
- data/spec/lutaml/model/optimization_spec.rb +228 -0
- data/spec/lutaml/model/store_spec.rb +41 -0
- data/spec/lutaml/xml/data_model_spec.rb +10 -28
- metadata +6 -4
|
@@ -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
|
|
@@ -167,7 +167,7 @@ module Lutaml
|
|
|
167
167
|
return nil if Lutaml::Model::RuntimeCompatibility.opal?
|
|
168
168
|
return nil unless defined?(GC)
|
|
169
169
|
|
|
170
|
-
GC.start if
|
|
170
|
+
GC.start if RUBY_VERSION >= "2.7"
|
|
171
171
|
IO.popen(["ps", "-o", "rss=", "-p", Process.pid.to_s],
|
|
172
172
|
&:read).to_i * 1024
|
|
173
173
|
rescue StandardError
|
|
@@ -250,7 +250,7 @@ module Lutaml
|
|
|
250
250
|
|
|
251
251
|
def serialize_attribute(model, element, doc)
|
|
252
252
|
if custom_methods[:to]
|
|
253
|
-
model.
|
|
253
|
+
model.public_send(custom_methods[:to], model, element, doc)
|
|
254
254
|
end
|
|
255
255
|
end
|
|
256
256
|
|
|
@@ -266,7 +266,7 @@ module Lutaml
|
|
|
266
266
|
|
|
267
267
|
def serialize(model, parent = nil, doc = nil)
|
|
268
268
|
if custom_methods[:to]
|
|
269
|
-
model.
|
|
269
|
+
model.public_send(custom_methods[:to], model, parent, doc)
|
|
270
270
|
else
|
|
271
271
|
value = to_value_for(model)
|
|
272
272
|
|
|
@@ -415,7 +415,7 @@ module Lutaml
|
|
|
415
415
|
def handle_custom_method(model, value, mapper_class)
|
|
416
416
|
return if !custom_methods[:from] || value.nil?
|
|
417
417
|
|
|
418
|
-
mapper_class.new.
|
|
418
|
+
mapper_class.new.public_send(custom_methods[:from], model, value)
|
|
419
419
|
true
|
|
420
420
|
end
|
|
421
421
|
|
|
@@ -63,7 +63,7 @@ module Lutaml
|
|
|
63
63
|
from_attr, to_attr = transform_attributes(rule, reverse: reverse)
|
|
64
64
|
next if from_attr.nil? || to_attr.nil?
|
|
65
65
|
|
|
66
|
-
value = input.
|
|
66
|
+
value = input.public_send(from_attr.name)
|
|
67
67
|
|
|
68
68
|
value = transformed_value(value, rule, from_attr, to_attr,
|
|
69
69
|
reverse: reverse)
|
|
@@ -95,7 +95,7 @@ module Lutaml
|
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def self._resolve_for_child_uncached(child_class, parent_register)
|
|
98
|
-
default_reg = if child_class.
|
|
98
|
+
default_reg = if child_class.is_a?(Class) && child_class.include?(Lutaml::Model::Serialize)
|
|
99
99
|
child_class.lutaml_default_register
|
|
100
100
|
end
|
|
101
101
|
|
|
@@ -172,7 +172,7 @@ module Lutaml
|
|
|
172
172
|
|
|
173
173
|
# Set @register on the class so instances know their context
|
|
174
174
|
# This ensures proper OOP context propagation during serialization
|
|
175
|
-
klass.set_register_context(@id) if klass.
|
|
175
|
+
klass.set_register_context(@id) if klass.is_a?(Class) && klass.include?(Lutaml::Model::Serialize)
|
|
176
176
|
|
|
177
177
|
# Register in GlobalContext
|
|
178
178
|
ctx = global_context
|
|
@@ -226,7 +226,7 @@ module Lutaml
|
|
|
226
226
|
# Set register context using proper OOP method
|
|
227
227
|
expected_class.set_register_context(id) if
|
|
228
228
|
!(expected_class < Lutaml::Model::Type::Value) &&
|
|
229
|
-
expected_class.
|
|
229
|
+
expected_class.is_a?(Class) && expected_class.include?(Lutaml::Model::Serialize)
|
|
230
230
|
|
|
231
231
|
expected_class
|
|
232
232
|
end
|
|
@@ -13,9 +13,11 @@ module Lutaml
|
|
|
13
13
|
# time, so downstream code never needs to re-interpret them.
|
|
14
14
|
module RenderPolicy
|
|
15
15
|
def self.derived_attribute_for?(context_obj, attr_name)
|
|
16
|
-
return false unless context_obj
|
|
16
|
+
return false unless context_obj.is_a?(Lutaml::Model::Serialize) &&
|
|
17
|
+
context_obj.class.is_a?(Class) &&
|
|
18
|
+
context_obj.class.include?(Lutaml::Model::Serialize)
|
|
17
19
|
|
|
18
|
-
register = context_obj.
|
|
20
|
+
register = context_obj.lutaml_register
|
|
19
21
|
context_obj.class.attributes(register)&.[](attr_name)&.derived?
|
|
20
22
|
end
|
|
21
23
|
|
|
@@ -92,7 +94,7 @@ module Lutaml
|
|
|
92
94
|
def should_skip_default?(value, rule, context_obj, attr_name)
|
|
93
95
|
# Skip if context object is using default and render_default is false
|
|
94
96
|
# But for collections, check if they were mutated (non-empty)
|
|
95
|
-
if context_obj.
|
|
97
|
+
if context_obj.is_a?(Lutaml::Model::Serialize) &&
|
|
96
98
|
context_obj.using_default?(attr_name) &&
|
|
97
99
|
!extract_option(rule, :render_default)
|
|
98
100
|
return false if derived_attribute?(context_obj, attr_name)
|
|
@@ -115,31 +117,23 @@ module Lutaml
|
|
|
115
117
|
# @param option_name [Symbol] The option name
|
|
116
118
|
# @return [Object, nil] The option value
|
|
117
119
|
def extract_option(rule, option_name)
|
|
118
|
-
if rule.
|
|
120
|
+
if rule.is_a?(CompiledRule)
|
|
119
121
|
rule.option(option_name)
|
|
120
|
-
elsif rule.
|
|
121
|
-
rule.
|
|
122
|
+
elsif rule.is_a?(MappingRule)
|
|
123
|
+
rule.public_send(option_name)
|
|
122
124
|
end
|
|
123
125
|
end
|
|
124
126
|
|
|
125
|
-
# Extract attribute name from rule
|
|
126
|
-
#
|
|
127
|
-
# @param rule [CompiledRule, MappingRule] The rule
|
|
128
|
-
# @return [Symbol] The attribute name
|
|
129
127
|
def extract_attribute_name(rule)
|
|
130
|
-
if rule.
|
|
128
|
+
if rule.is_a?(CompiledRule)
|
|
131
129
|
rule.attribute_name
|
|
132
|
-
|
|
130
|
+
else
|
|
133
131
|
rule.to
|
|
134
132
|
end
|
|
135
133
|
end
|
|
136
134
|
|
|
137
|
-
# Check if rule defines a collection
|
|
138
|
-
#
|
|
139
|
-
# @param rule [CompiledRule, MappingRule] The rule
|
|
140
|
-
# @return [Boolean] true if collection
|
|
141
135
|
def collection?(rule)
|
|
142
|
-
if rule.
|
|
136
|
+
if rule.is_a?(CompiledRule) || rule.is_a?(MappingRule)
|
|
143
137
|
rule.collection?
|
|
144
138
|
else
|
|
145
139
|
false
|
|
@@ -101,7 +101,7 @@ module Lutaml
|
|
|
101
101
|
# because sequence requires a root element
|
|
102
102
|
if instance.is_a?(Sequence)
|
|
103
103
|
# Output sequence content directly without the wrapper
|
|
104
|
-
instance.
|
|
104
|
+
instance.xml_block_content(extended_indent)
|
|
105
105
|
else
|
|
106
106
|
instance.to_xml_mapping(extended_indent)
|
|
107
107
|
end
|
|
@@ -106,7 +106,7 @@ module Lutaml
|
|
|
106
106
|
"\n#{indent}# Resolve serialization mapping imports (sequence imports)\n" +
|
|
107
107
|
@classes.keys.filter_map do |name|
|
|
108
108
|
next if name.to_s.include?("Namespace")
|
|
109
|
-
next unless @classes[name].
|
|
109
|
+
next unless @classes[name].is_a?(Class) && @classes[name].include?(Lutaml::Model::Serialize)
|
|
110
110
|
|
|
111
111
|
class_name = Utils.camel_case(name)
|
|
112
112
|
"#{indent}#{class_name}.mappings[:xml].ensure_mappings_imported!(:#{@register_id}) if #{class_name}.mappings[:xml]&.respond_to?(:ensure_mappings_imported!)"
|
|
@@ -166,7 +166,7 @@ module Lutaml
|
|
|
166
166
|
require "#{dir}/registry"
|
|
167
167
|
|
|
168
168
|
# Call register_all to register all classes
|
|
169
|
-
GeneratedModels.register_all if
|
|
169
|
+
GeneratedModels.register_all if defined?(GeneratedModels)
|
|
170
170
|
end
|
|
171
171
|
end
|
|
172
172
|
|
|
@@ -210,7 +210,12 @@ module Lutaml
|
|
|
210
210
|
schema_to_models(schema.import) if schema.import&.any?
|
|
211
211
|
# Use schema's resolved_element_order which returns properly typed XSD objects
|
|
212
212
|
schema.resolved_element_order.each do |order_item|
|
|
213
|
-
item_name = order_item.name if order_item.
|
|
213
|
+
item_name = order_item.name if order_item.is_a?(Lutaml::Xml::Schema::Xsd::SimpleType) ||
|
|
214
|
+
order_item.is_a?(Lutaml::Xml::Schema::Xsd::Group) ||
|
|
215
|
+
order_item.is_a?(Lutaml::Xml::Schema::Xsd::ComplexType) ||
|
|
216
|
+
order_item.is_a?(Lutaml::Xml::Schema::Xsd::Element) ||
|
|
217
|
+
order_item.is_a?(Lutaml::Xml::Schema::Xsd::Attribute) ||
|
|
218
|
+
order_item.is_a?(Lutaml::Xml::Schema::Xsd::AttributeGroup)
|
|
214
219
|
case order_item
|
|
215
220
|
when Lutaml::Xml::Schema::Xsd::SimpleType
|
|
216
221
|
@simple_types[item_name] = setup_simple_type(order_item)
|
|
@@ -246,8 +251,6 @@ module Lutaml
|
|
|
246
251
|
end
|
|
247
252
|
|
|
248
253
|
def restriction_content(instance, restriction)
|
|
249
|
-
return instance unless restriction.respond_to?(:max_length)
|
|
250
|
-
|
|
251
254
|
restriction_min_max(restriction, instance, field: :max_length,
|
|
252
255
|
value_method: :min)
|
|
253
256
|
restriction_min_max(restriction, instance, field: :min_length,
|
|
@@ -271,7 +274,7 @@ module Lutaml
|
|
|
271
274
|
|
|
272
275
|
instance.public_send(
|
|
273
276
|
:"#{field}=",
|
|
274
|
-
field_value.map(&:value).
|
|
277
|
+
field_value.map(&:value).public_send(value_method).to_s,
|
|
275
278
|
)
|
|
276
279
|
end
|
|
277
280
|
|
|
@@ -447,12 +450,10 @@ module Lutaml
|
|
|
447
450
|
def setup_restriction(restriction)
|
|
448
451
|
Restriction.new.tap do |instance|
|
|
449
452
|
instance.base_class = restriction.base
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
instance)
|
|
453
|
-
end
|
|
453
|
+
restriction_patterns(restriction.pattern,
|
|
454
|
+
instance)
|
|
454
455
|
restriction_content(instance, restriction)
|
|
455
|
-
if restriction.
|
|
456
|
+
if restriction.enumeration&.any?
|
|
456
457
|
instance.enumerations = restriction.enumeration.map(&:value)
|
|
457
458
|
end
|
|
458
459
|
end
|
|
@@ -523,10 +524,9 @@ compiler_complex_type)
|
|
|
523
524
|
def resolved_element_order(object)
|
|
524
525
|
return [] if object.element_order.nil?
|
|
525
526
|
|
|
526
|
-
# If the object has its own resolved_element_order
|
|
527
|
+
# If the object is an XSD type (which has its own resolved_element_order),
|
|
527
528
|
# use it instead of processing element_order which returns generic XML elements
|
|
528
|
-
if object.
|
|
529
|
-
object.class.name.start_with?("Lutaml::Xml::Schema::Xsd", "Lutaml::Xml::Schema::Xsd")
|
|
529
|
+
if object.is_a?(Lutaml::Xml::Schema::Xsd::Base)
|
|
530
530
|
return object.resolved_element_order
|
|
531
531
|
end
|
|
532
532
|
|
|
@@ -538,7 +538,7 @@ compiler_complex_type)
|
|
|
538
538
|
next unless element == builder_instance
|
|
539
539
|
|
|
540
540
|
array[i] =
|
|
541
|
-
Array(object.
|
|
541
|
+
Array(object.public_send(Utils.snake_case(builder_instance.name)))[index]
|
|
542
542
|
index += 1
|
|
543
543
|
end
|
|
544
544
|
end
|
|
@@ -122,7 +122,7 @@ module Lutaml
|
|
|
122
122
|
else
|
|
123
123
|
# Builder-style: g.description(value) sets the value
|
|
124
124
|
value = args.first
|
|
125
|
-
|
|
125
|
+
public_send(:"#{name}=", value)
|
|
126
126
|
# Track order for mixed_content serialization
|
|
127
127
|
track_order(name, value, nil) if @__order_tracking__
|
|
128
128
|
value
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Serialize
|
|
6
|
+
# Encapsulates the parent-to-child options propagation contract.
|
|
7
|
+
#
|
|
8
|
+
# During deserialization, a parent passes context to its children via an
|
|
9
|
+
# options Hash. Only certain keys are safe to propagate — parent-internal
|
|
10
|
+
# keys (resolved_type, namespace_uri, converted) must be stripped so
|
|
11
|
+
# children derive their own context.
|
|
12
|
+
#
|
|
13
|
+
# This class is the single source of truth for which keys propagate,
|
|
14
|
+
# replacing scattered CHILD_PROPAGATION_KEYS constants and ad-hoc
|
|
15
|
+
# `.slice` calls.
|
|
16
|
+
#
|
|
17
|
+
# Usage:
|
|
18
|
+
# child_options = DeserializationContext.propagate(options).merge(register: register)
|
|
19
|
+
# klass.apply_mappings(value, format, child_options)
|
|
20
|
+
class DeserializationContext
|
|
21
|
+
# Keys that are safe to propagate from parent to child deserialization.
|
|
22
|
+
#
|
|
23
|
+
# Parent-internal keys (namespace_uri, resolved_type, converted, mappings)
|
|
24
|
+
# are intentionally excluded — children must derive their own context.
|
|
25
|
+
PROPAGATION_KEYS = %i[
|
|
26
|
+
lutaml_parent
|
|
27
|
+
lutaml_root
|
|
28
|
+
default_namespace
|
|
29
|
+
import_declaration_plan
|
|
30
|
+
polymorphic
|
|
31
|
+
collection
|
|
32
|
+
render_empty
|
|
33
|
+
render_nil
|
|
34
|
+
cdata
|
|
35
|
+
].freeze
|
|
36
|
+
|
|
37
|
+
# Extract propagable keys from a parent options hash.
|
|
38
|
+
#
|
|
39
|
+
# Returns a new Hash containing only the keys safe for child
|
|
40
|
+
# deserialization. Parent-internal keys are excluded.
|
|
41
|
+
#
|
|
42
|
+
# @param options [Hash] The parent's options hash
|
|
43
|
+
# @return [Hash] A new hash with only propagable keys
|
|
44
|
+
def self.propagate(options)
|
|
45
|
+
options.slice(*PROPAGATION_KEYS)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -23,7 +23,7 @@ module Lutaml
|
|
|
23
23
|
end
|
|
24
24
|
mappings[format].instance_eval(&)
|
|
25
25
|
|
|
26
|
-
if mappings[format].
|
|
26
|
+
if mappings[format].is_a?(Lutaml::Xml::Mapping)
|
|
27
27
|
mappings[format].finalize(self)
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -172,7 +172,7 @@ module Lutaml
|
|
|
172
172
|
|
|
173
173
|
if format == :yamls
|
|
174
174
|
mapping = mappings[format]
|
|
175
|
-
return true if mapping.
|
|
175
|
+
return true if mapping.is_a?(Lutaml::Yamls::Adapter::Mapping) && mapping.yamls_sequence
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
false
|
|
@@ -90,15 +90,15 @@ module Lutaml
|
|
|
90
90
|
#
|
|
91
91
|
# @param source_class [Class] The source class to copy from
|
|
92
92
|
def initialize_attrs(source_class)
|
|
93
|
-
@mappings = Utils.deep_dup(source_class.
|
|
94
|
-
@attributes = Utils.deep_dup(source_class.
|
|
93
|
+
@mappings = Utils.deep_dup(source_class.mappings) || {}
|
|
94
|
+
@attributes = Utils.deep_dup(source_class.class_attributes) || {}
|
|
95
95
|
@choice_attributes = deep_duplicate_choice_attributes(source_class)
|
|
96
96
|
@register_records = Utils.deep_dup(
|
|
97
|
-
source_class.
|
|
97
|
+
source_class.register_records,
|
|
98
98
|
) || ::Hash.new do |hash, key|
|
|
99
99
|
hash[key] = { attributes: {}, choice_attributes: [] }
|
|
100
100
|
end
|
|
101
|
-
|
|
101
|
+
model(self)
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
# Deep duplicate choice attributes from a source class
|
|
@@ -106,7 +106,7 @@ module Lutaml
|
|
|
106
106
|
# @param source_class [Class] The source class
|
|
107
107
|
# @return [Array] The duplicated choice attributes
|
|
108
108
|
def deep_duplicate_choice_attributes(source_class, register = nil)
|
|
109
|
-
choice_attrs = Array(source_class.
|
|
109
|
+
choice_attrs = Array(source_class.choice_attributes)
|
|
110
110
|
choice_attrs.map do |choice_attr|
|
|
111
111
|
choice_attr.deep_duplicate(self, register)
|
|
112
112
|
end
|
|
@@ -127,6 +127,13 @@ module Lutaml
|
|
|
127
127
|
end
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
+
# Raw class-level attributes without register merging.
|
|
131
|
+
# Used by initialize_attrs during class inheritance.
|
|
132
|
+
# @return [Hash] The raw attributes hash
|
|
133
|
+
def class_attributes
|
|
134
|
+
@attributes
|
|
135
|
+
end
|
|
136
|
+
|
|
130
137
|
# Get all choice attributes for this model
|
|
131
138
|
#
|
|
132
139
|
# Merges class-level choice attributes with register-specific choice attributes.
|
|
@@ -176,6 +183,12 @@ module Lutaml
|
|
|
176
183
|
GlobalContext.resolver.clear_cache(register_id)
|
|
177
184
|
end
|
|
178
185
|
|
|
186
|
+
# Clear per-Attribute type caches (stale entries from GC'd TypeContext objects)
|
|
187
|
+
class_attributes.each_value(&:clear_type_cache)
|
|
188
|
+
@register_records&.each_value do |record|
|
|
189
|
+
record[:attributes]&.each_value(&:clear_type_cache)
|
|
190
|
+
end
|
|
191
|
+
|
|
179
192
|
# Clear centralized mapping and transformation caches
|
|
180
193
|
# (Single Source of Truth - no longer uses instance variables)
|
|
181
194
|
TransformationRegistry.instance.clear
|
|
@@ -220,6 +233,12 @@ module Lutaml
|
|
|
220
233
|
@using_default[attribute_name] = false
|
|
221
234
|
end
|
|
222
235
|
|
|
236
|
+
Utils.add_method_if_not_defined(klass,
|
|
237
|
+
:values_set_for) do |attribute_names|
|
|
238
|
+
@using_default ||= {}
|
|
239
|
+
attribute_names.each { |name| @using_default[name] = false }
|
|
240
|
+
end
|
|
241
|
+
|
|
223
242
|
Utils.add_method_if_not_defined(klass,
|
|
224
243
|
:using_default?) do |attribute_name|
|
|
225
244
|
@using_default ||= {}
|
|
@@ -246,6 +265,24 @@ module Lutaml
|
|
|
246
265
|
value
|
|
247
266
|
end
|
|
248
267
|
|
|
268
|
+
# Whether instances of this class should be registered in the
|
|
269
|
+
# global Store for reference resolution. Defaults to true for
|
|
270
|
+
# backward compatibility. Use `skip_reference_registration` to
|
|
271
|
+
# opt out for classes that never participate in cross-referencing.
|
|
272
|
+
def reference_resolvable?
|
|
273
|
+
return true unless instance_variable_defined?(:@skip_reference_registration)
|
|
274
|
+
|
|
275
|
+
!@skip_reference_registration
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Opt out of Store registration for this class.
|
|
279
|
+
# Instances will not be tracked in the global Store, saving
|
|
280
|
+
# memory and registration overhead for classes that are never
|
|
281
|
+
# resolved by reference (no xml_id or similar attributes).
|
|
282
|
+
def skip_reference_registration
|
|
283
|
+
@skip_reference_registration = true
|
|
284
|
+
end
|
|
285
|
+
|
|
249
286
|
# Define a choice constraint
|
|
250
287
|
#
|
|
251
288
|
# @param min [Integer] Minimum number of choices
|
|
@@ -307,7 +344,7 @@ module Lutaml
|
|
|
307
344
|
reg_record = register_records[register_id]
|
|
308
345
|
return unless reg_record
|
|
309
346
|
|
|
310
|
-
default_attrs =
|
|
347
|
+
default_attrs = class_attributes || {}
|
|
311
348
|
reg_record_attrs = reg_record[:attributes] || {}
|
|
312
349
|
|
|
313
350
|
reg_record_attrs.each do |name, attr|
|
|
@@ -332,7 +369,7 @@ module Lutaml
|
|
|
332
369
|
if args.empty?
|
|
333
370
|
instance_variable_get(:"@#{name}")
|
|
334
371
|
else
|
|
335
|
-
|
|
372
|
+
public_send(:"#{name}=", args.first)
|
|
336
373
|
track_order(name, args.first, nil) if @__order_tracking__
|
|
337
374
|
args.first
|
|
338
375
|
end
|