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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +12 -32
  3. data/lib/lutaml/key_value/transform.rb +5 -5
  4. data/lib/lutaml/key_value/transformation/collection_serializer.rb +25 -11
  5. data/lib/lutaml/key_value/transformation/value_serializer.rb +7 -7
  6. data/lib/lutaml/key_value/transformation.rb +27 -17
  7. data/lib/lutaml/model/adapter_resolver.rb +4 -6
  8. data/lib/lutaml/model/attribute.rb +26 -23
  9. data/lib/lutaml/model/cached_type_resolver.rb +10 -9
  10. data/lib/lutaml/model/cli.rb +1 -1
  11. data/lib/lutaml/model/collection.rb +4 -4
  12. data/lib/lutaml/model/comparable_model.rb +11 -11
  13. data/lib/lutaml/model/config.rb +1 -1
  14. data/lib/lutaml/model/consolidation/dispatcher.rb +1 -1
  15. data/lib/lutaml/model/consolidation/pattern_chunker.rb +3 -3
  16. data/lib/lutaml/model/format_registry.rb +6 -4
  17. data/lib/lutaml/model/global_context.rb +2 -2
  18. data/lib/lutaml/model/global_register.rb +1 -1
  19. data/lib/lutaml/model/instrumentation.rb +1 -1
  20. data/lib/lutaml/model/mapping/mapping_rule.rb +3 -3
  21. data/lib/lutaml/model/mapping/model_mapping.rb +1 -1
  22. data/lib/lutaml/model/mapping/model_mapping_rule.rb +1 -1
  23. data/lib/lutaml/model/register.rb +3 -3
  24. data/lib/lutaml/model/render_policy.rb +11 -17
  25. data/lib/lutaml/model/runtime_compatibility.rb +0 -1
  26. data/lib/lutaml/model/schema/xml_compiler/group.rb +1 -1
  27. data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +1 -1
  28. data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -2
  29. data/lib/lutaml/model/schema/xml_compiler.rb +14 -14
  30. data/lib/lutaml/model/serialize/attribute_definition.rb +1 -1
  31. data/lib/lutaml/model/serialize/deserialization_context.rb +50 -0
  32. data/lib/lutaml/model/serialize/format_conversion.rb +2 -2
  33. data/lib/lutaml/model/serialize/initialization.rb +44 -7
  34. data/lib/lutaml/model/serialize/model_import.rb +1 -1
  35. data/lib/lutaml/model/serialize.rb +8 -1
  36. data/lib/lutaml/model/services/rule_value_extractor.rb +2 -1
  37. data/lib/lutaml/model/store.rb +27 -21
  38. data/lib/lutaml/model/transformation_registry.rb +1 -1
  39. data/lib/lutaml/model/type_context.rb +7 -1
  40. data/lib/lutaml/model/type_resolver.rb +1 -6
  41. data/lib/lutaml/model/utils.rb +19 -6
  42. data/lib/lutaml/model/validation_framework.rb +1 -1
  43. data/lib/lutaml/model/value_transformer.rb +2 -2
  44. data/lib/lutaml/model/version.rb +1 -1
  45. data/lib/lutaml/xml/adapter/adapter_helpers.rb +1 -1
  46. data/lib/lutaml/xml/adapter/base_adapter.rb +10 -14
  47. data/lib/lutaml/xml/adapter/namespace_uri_collector.rb +3 -3
  48. data/lib/lutaml/xml/adapter/plan_based_builder.rb +14 -14
  49. data/lib/lutaml/xml/adapter/xml_serializer.rb +3 -3
  50. data/lib/lutaml/xml/configurable.rb +2 -1
  51. data/lib/lutaml/xml/data_model.rb +2 -2
  52. data/lib/lutaml/xml/decisions/decision_context.rb +3 -3
  53. data/lib/lutaml/xml/decisions/rules/element_form_default_unqualified_rule.rb +1 -1
  54. data/lib/lutaml/xml/decisions/rules/element_form_option_rule.rb +1 -1
  55. data/lib/lutaml/xml/decisions/rules/used_prefix_rule.rb +1 -1
  56. data/lib/lutaml/xml/declaration_plan.rb +2 -2
  57. data/lib/lutaml/xml/declaration_planner.rb +12 -13
  58. data/lib/lutaml/xml/document.rb +13 -13
  59. data/lib/lutaml/xml/format_chooser.rb +3 -3
  60. data/lib/lutaml/xml/hoisting_algorithm.rb +1 -1
  61. data/lib/lutaml/xml/mapping.rb +2 -2
  62. data/lib/lutaml/xml/mapping_rule.rb +16 -3
  63. data/lib/lutaml/xml/model_transform.rb +17 -19
  64. data/lib/lutaml/xml/namespace_collector.rb +10 -10
  65. data/lib/lutaml/xml/namespace_declaration.rb +2 -2
  66. data/lib/lutaml/xml/namespace_declaration_data.rb +5 -8
  67. data/lib/lutaml/xml/namespace_scope_config.rb +3 -2
  68. data/lib/lutaml/xml/namespace_type_resolver.rb +4 -4
  69. data/lib/lutaml/xml/nokogiri/element.rb +2 -2
  70. data/lib/lutaml/xml/polymorphic_value_handler.rb +1 -1
  71. data/lib/lutaml/xml/schema/xsd/base.rb +7 -7
  72. data/lib/lutaml/xml/schema/xsd/choice.rb +2 -2
  73. data/lib/lutaml/xml/schema/xsd/complex_type.rb +5 -5
  74. data/lib/lutaml/xml/schema/xsd/errors/message_builder.rb +3 -3
  75. data/lib/lutaml/xml/schema/xsd/group.rb +2 -2
  76. data/lib/lutaml/xml/schema/xsd/sequence.rb +2 -2
  77. data/lib/lutaml/xml/schema/xsd_schema.rb +5 -5
  78. data/lib/lutaml/xml/serialization/format_conversion.rb +4 -3
  79. data/lib/lutaml/xml/transformation/element_builder.rb +4 -2
  80. data/lib/lutaml/xml/transformation/rule_applier.rb +2 -2
  81. data/lib/lutaml/xml/transformation/value_serializer.rb +4 -6
  82. data/lib/lutaml/xml/transformation.rb +4 -4
  83. data/lib/lutaml/xml/type/configurable.rb +0 -4
  84. data/lib/lutaml/xml/xml_element.rb +21 -13
  85. data/lutaml-model.gemspec +1 -1
  86. data/spec/lutaml/model/cached_type_resolver_spec.rb +3 -3
  87. data/spec/lutaml/model/optimization_spec.rb +228 -0
  88. data/spec/lutaml/model/store_spec.rb +41 -0
  89. data/spec/lutaml/xml/data_model_spec.rb +10 -28
  90. 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.respond_to?(field) ? instance.public_send(field) : nil
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.respond_to?(field) ? instance.public_send(field) : nil
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.send(field_or_proc)
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.send(field) }
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 = send(attr)
23
- other_value = other.send(attr)
22
+ attr_value = public_send(attr)
23
+ other_value = other.public_send(attr)
24
24
 
25
- if attr_value.respond_to?(:eql?) && same_class?(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 = send(attr)
62
+ attr_value = public_send(attr)
63
63
 
64
- if attr_value.respond_to?(:calculate_hash)
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.send(name), obj2.send(name), index == obj1.class.attributes.length - 1
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.send(attr))}"
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.respond_to?(: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.send(attr)
443
- value2 = obj2.send(attr) if obj2.respond_to?(attr)
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.send(attr_name)
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)
@@ -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 respond_to?(:"#{format}_adapter_type=")
128
+ return if method_defined?(:"#{format}_adapter_type=")
129
129
 
130
130
  # Adapter class getter (returns Class)
131
131
  define_method(:"#{format}_adapter") do
@@ -31,7 +31,7 @@ module Lutaml
31
31
 
32
32
  value = if target_type == item.class
33
33
  item
34
- elsif item.respond_to?(:value)
34
+ elsif item.is_a?(Lutaml::Model::Serialize) && item.class.attributes.key?(:value)
35
35
  item.value
36
36
  else
37
37
  item
@@ -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.respond_to?(:node_type)
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.respond_to?(:name)
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.respond_to?(:text)
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.send(:alias_method, :"#{format}_adapter_type=",
167
- :"#{format}_adapter=")
168
- cfg.send(:alias_method, :"#{format}_adapter_type",
169
- :"#{format}_adapter")
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.respond_to?(:clear!)
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.respond_to?(:clear!)
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.respond_to?(:clear_cache)
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 GC.respond_to?(:compact)
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.send(custom_methods[:to], model, element, doc)
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.send(custom_methods[:to], model, parent, doc)
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.send(custom_methods[:from], model, value)
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.send(from_attr.name)
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)
@@ -47,7 +47,7 @@ module Lutaml
47
47
  when Proc
48
48
  transform.call(value)
49
49
  when String, Symbol
50
- transformer.new.send(transform, value)
50
+ transformer.new.public_send(transform, value)
51
51
  when Class
52
52
  transform.public_send(transform_method, value, mapping: true)
53
53
  end
@@ -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.respond_to?(:lutaml_default_register)
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.respond_to?(:set_register_context)
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.respond_to?(:set_register_context)
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&.class.respond_to?(:attributes)
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.respond_to?(:lutaml_register) ? context_obj.lutaml_register : nil
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.respond_to?(:using_default?) &&
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.respond_to?(:option)
120
+ if rule.is_a?(CompiledRule)
119
121
  rule.option(option_name)
120
- elsif rule.respond_to?(option_name)
121
- rule.send(option_name)
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.respond_to?(:attribute_name)
128
+ if rule.is_a?(CompiledRule)
131
129
  rule.attribute_name
132
- elsif rule.respond_to?(:to)
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.respond_to?(:collection?)
136
+ if rule.is_a?(CompiledRule) || rule.is_a?(MappingRule)
143
137
  rule.collection?
144
138
  else
145
139
  false
@@ -11,7 +11,6 @@ module Lutaml
11
11
 
12
12
  def self.windows?
13
13
  defined?(Gem) &&
14
- Gem.respond_to?(:win_platform?) &&
15
14
  Gem.win_platform?
16
15
  end
17
16
 
@@ -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.send(:xml_block_content, extended_indent)
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].respond_to?(:mappings)
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!)"
@@ -40,8 +40,6 @@ module Lutaml
40
40
  @instances.map(&:required_files)
41
41
  end
42
42
 
43
- private
44
-
45
43
  def xml_block_content(indent)
46
44
  instances.filter_map do |instance|
47
45
  instance.to_xml_mapping(indent * 2)
@@ -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 GeneratedModels.respond_to?(:register_all)
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.respond_to?(:name)
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).send(value_method).to_s,
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
- if restriction.respond_to?(:pattern)
451
- restriction_patterns(restriction.pattern,
452
- instance)
453
- end
453
+ restriction_patterns(restriction.pattern,
454
+ instance)
454
455
  restriction_content(instance, restriction)
455
- if restriction.respond_to?(:enumeration) && restriction.enumeration&.any?
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 method (like XSD objects),
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.respond_to?(:resolved_element_order) &&
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.send(Utils.snake_case(builder_instance.name)))[index]
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
- send(:"#{name}=", value)
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].respond_to?(:finalize)
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.respond_to?(:yamls_sequence) && mapping.yamls_sequence
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.instance_variable_get(:@mappings)) || {}
94
- @attributes = Utils.deep_dup(source_class.instance_variable_get(:@attributes)) || {}
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.instance_variable_get(:@register_records),
97
+ source_class.register_records,
98
98
  ) || ::Hash.new do |hash, key|
99
99
  hash[key] = { attributes: {}, choice_attributes: [] }
100
100
  end
101
- instance_variable_set(:@model, self)
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.instance_variable_get(:@choice_attributes))
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 = instance_variable_get(:@attributes) || {}
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
- send(:"#{name}=", args.first)
372
+ public_send(:"#{name}=", args.first)
336
373
  track_order(name, args.first, nil) if @__order_tracking__
337
374
  args.first
338
375
  end