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
@@ -298,7 +298,7 @@ module Lutaml
298
298
  def build_collection_item_plans(collection, _mapping, _needs)
299
299
  children_plans = {}
300
300
 
301
- return children_plans unless collection.respond_to?(:each)
301
+ return children_plans unless collection.is_a?(Array)
302
302
 
303
303
  # Get the item type from the collection class
304
304
  item_type = begin
@@ -595,7 +595,7 @@ mapper_class: nil)
595
595
 
596
596
  # Get child's element namespace if available
597
597
  child_type = attr_def.type(mapper_class ? register_for(mapper_class) : @register)
598
- if child_type.respond_to?(:<) && child_type < Lutaml::Model::Serialize
598
+ if child_type.is_a?(Class) && child_type < Lutaml::Model::Serialize
599
599
  child_reg = register_for(child_type)
600
600
  child_mapping = child_type.mappings_for(:xml, child_reg)
601
601
  if child_mapping&.namespace_class
@@ -675,7 +675,7 @@ mapper_class: nil)
675
675
  # prefix declarations are NOT needed by any child element.
676
676
  if is_root && !options.key?(:use_prefix)
677
677
  stored_plan = options[:stored_xml_declaration_plan]
678
- if stored_plan.respond_to?(:root_node) && stored_plan.root_node.respond_to?(:hoisted_declarations)
678
+ if stored_plan.is_a?(Lutaml::Xml::DeclarationPlan) && stored_plan.root_node.is_a?(Lutaml::Xml::DeclarationPlan::ElementNode)
679
679
  root_has_input_prefix = xml_element.xml_namespace_prefix.to_s != ""
680
680
 
681
681
  unless root_has_input_prefix
@@ -706,7 +706,7 @@ mapper_class: nil)
706
706
  #
707
707
  # Note: The transformation sets @needs_xmlns_blank on the XmlElement when the child
708
708
  # model has explicit `namespace :blank` declaration.
709
- element_marked_blank = xml_element.respond_to?(:needs_xmlns_blank) && xml_element.needs_xmlns_blank
709
+ element_marked_blank = xml_element.is_a?(Lutaml::Xml::DataModel::XmlElement) && xml_element.needs_xmlns_blank
710
710
 
711
711
  # Add xmlns="" when parent's effective namespace form is :qualified.
712
712
  # This implements the W3C XML Schema default behavior:
@@ -803,7 +803,7 @@ mapper_class: nil)
803
803
  if attr_def
804
804
  child_reg = mapper_class ? register_for(mapper_class) : @register
805
805
  child_type = attr_def.type(child_reg)
806
- if child_type.respond_to?(:<) && child_type < Lutaml::Model::Serialize
806
+ if child_type.is_a?(Class) && child_type < Lutaml::Model::Serialize
807
807
  child_type_reg = register_for(child_type)
808
808
  child_mapping_obj = child_type.mappings_for(:xml,
809
809
  child_type_reg)
@@ -1041,7 +1041,6 @@ mapper_class: nil)
1041
1041
  ns_key = xml_element.namespace_class.to_key
1042
1042
  ns_usage = needs.namespaces[ns_key]
1043
1043
  ns_from_wrapper = xml_element.is_a?(Lutaml::Xml::XmlElement) &&
1044
- xml_element.respond_to?(:namespace_prefix_explicit) &&
1045
1044
  xml_element.namespace_prefix_explicit
1046
1045
  if ns_from_wrapper
1047
1046
  used_prefix = ns_usage&.used_prefix
@@ -1195,7 +1194,7 @@ mapper_class: nil)
1195
1194
  # 3. Parent declared canonical/alias-A, child wants alias-B → NOT hoisted (child must declare its alias)
1196
1195
  # This ensures round-trip fidelity: if input used alias URI, output preserves it.
1197
1196
  ns_hoisted_by_parent = parent_hoisted.value?(ns_uri)
1198
- if !ns_hoisted_by_parent && ns_class.respond_to?(:all_uris) &&
1197
+ if !ns_hoisted_by_parent && ns_class.is_a?(Class) && ns_class < Lutaml::Xml::Namespace &&
1199
1198
  ns_uri == ns_class.uri # child is using canonical URI
1200
1199
  # Parent declared an alias of this namespace — child doesn't need to re-declare
1201
1200
  # because the alias already establishes the namespace binding
@@ -1227,7 +1226,7 @@ mapper_class: nil)
1227
1226
  # Namespace is already hoisted by parent, and we have an explicit prefix
1228
1227
  # Check if parent has the SAME prefix declaration (accounting for URI aliases)
1229
1228
  parent_uri_for_prefix = parent_hoisted[element_prefix]
1230
- all_uris = ns_class.respond_to?(:all_uris) ? ns_class.all_uris : [ns_uri]
1229
+ all_uris = ns_class.is_a?(Class) && ns_class < Lutaml::Xml::Namespace ? ns_class.all_uris : [ns_uri]
1231
1230
  parent_has_same_prefix = parent_uri_for_prefix && all_uris.include?(parent_uri_for_prefix)
1232
1231
  if parent_has_same_prefix
1233
1232
  # Parent already declared this namespace with the same prefix - don't re-declare
@@ -1256,7 +1255,7 @@ mapper_class: nil)
1256
1255
  # Use namespaces_at_path([]) to verify this namespace was actually at root level.
1257
1256
  # Note: namespace_locations only has child paths, not root. Root namespaces
1258
1257
  # are stored in root_node.hoisted_declarations, which namespaces_at_path returns.
1259
- if is_root && hoisted.key?(nil) && hoisted[nil] == ns_uri && xml_element.respond_to?(:children)
1258
+ if is_root && hoisted.key?(nil) && hoisted[nil] == ns_uri && xml_element.is_a?(Lutaml::Xml::DataModel::XmlElement)
1260
1259
  stored_plan = options[:stored_xml_declaration_plan]
1261
1260
  root_ns_at_input = stored_plan&.namespaces_at_path([])
1262
1261
  namespace_was_at_root = if root_ns_at_input
@@ -1405,7 +1404,7 @@ mapper_class: nil)
1405
1404
 
1406
1405
  # Only skip hoisting if child uses namespace AND parent doesn't use it for attributes
1407
1406
  # Check if parent element instance has attributes with this namespace
1408
- parent_has_attr_with_ns = if xml_element.respond_to?(:attributes)
1407
+ parent_has_attr_with_ns = if xml_element.is_a?(Lutaml::Xml::DataModel::XmlElement)
1409
1408
  xml_element.attributes.any? do |xml_attr|
1410
1409
  next false unless xml_attr.namespace_class
1411
1410
 
@@ -1454,7 +1453,7 @@ mapper_class: nil)
1454
1453
  end
1455
1454
 
1456
1455
  # Only skip hoisting if child uses namespace AND parent doesn't use it for attributes
1457
- parent_has_attr_with_ns = if xml_element.respond_to?(:attributes)
1456
+ parent_has_attr_with_ns = if xml_element.is_a?(Lutaml::Xml::DataModel::XmlElement)
1458
1457
  xml_element.attributes.any? do |xml_attr|
1459
1458
  next false unless xml_attr.namespace_class
1460
1459
 
@@ -1711,7 +1710,7 @@ mapper_class: nil)
1711
1710
 
1712
1711
  # Check all namespace classes in needs
1713
1712
  needs.all_namespace_classes.any? do |ns_class|
1714
- ns_class.respond_to?(:schema_location) && ns_class.schema_location
1713
+ ns_class.is_a?(Class) && ns_class < Lutaml::Xml::Namespace && ns_class.schema_location
1715
1714
  end
1716
1715
  end
1717
1716
 
@@ -1829,7 +1828,7 @@ mapper_class: nil)
1829
1828
 
1830
1829
  # Collect all namespace classes that have schema_location
1831
1830
  namespaces_with_schema = needs.all_namespace_classes.select do |ns_class|
1832
- ns_class.respond_to?(:schema_location) && ns_class.schema_location
1831
+ ns_class.is_a?(Class) && ns_class < Lutaml::Xml::Namespace && ns_class.schema_location
1833
1832
  end
1834
1833
 
1835
1834
  return nil if namespaces_with_schema.empty?
@@ -63,7 +63,7 @@ module Lutaml
63
63
  # @return [String, nil] the DOCTYPE declaration or nil if no DOCTYPE
64
64
  def doctype_declaration
65
65
  # Prefer native Nokogiri DOCTYPE
66
- if @parsed_doc.respond_to?(:internal_subset) && @parsed_doc.internal_subset
66
+ if defined?(::Nokogiri) && @parsed_doc.is_a?(::Nokogiri::XML::Document) && @parsed_doc.internal_subset
67
67
  return "#{@parsed_doc.internal_subset}\n"
68
68
  end
69
69
 
@@ -97,15 +97,15 @@ module Lutaml
97
97
  result.attribute_order = element.attribute_order
98
98
 
99
99
  element.children.each do |child|
100
- next if child.respond_to?(:comment?) && child.comment?
101
- next if child.respond_to?(:processing_instruction?) && child.processing_instruction?
100
+ next if child.is_a?(Lutaml::Xml::XmlElement) && child.comment?
101
+ next if child.is_a?(Lutaml::Xml::XmlElement) && child.processing_instruction?
102
102
 
103
103
  if klass&.<= Serialize
104
104
  attr = klass.attribute_for_child(self.class.name_of(child),
105
105
  format)
106
106
  end
107
107
 
108
- if child.respond_to?(:text?) && child.text?
108
+ if child.is_a?(Lutaml::Xml::XmlElement) && child.text?
109
109
  result.assign_or_append_value(
110
110
  self.class.name_of(child),
111
111
  self.class.text_of(child),
@@ -148,7 +148,7 @@ module Lutaml
148
148
  end
149
149
 
150
150
  def ordered?(element, options = {})
151
- return false unless element.respond_to?(:element_order)
151
+ return false unless element.is_a?(Lutaml::Model::Serialize)
152
152
 
153
153
  mapper_class = options[:mapper_class]
154
154
  xml_mapping = mapper_class&.mappings_for(:xml)
@@ -198,7 +198,7 @@ module Lutaml
198
198
  return unless content_rule
199
199
 
200
200
  if content_rule.custom_methods[:to]
201
- mapper_class.new.send(
201
+ mapper_class.new.public_send(
202
202
  content_rule.custom_methods[:to],
203
203
  element,
204
204
  xml.parent,
@@ -216,16 +216,16 @@ module Lutaml
216
216
  klass = mapper_class || element.class
217
217
  return klass.attributes[rule.to] unless rule.delegate
218
218
 
219
- delegated_obj = element.send(rule.delegate)
219
+ delegated_obj = element.public_send(rule.delegate)
220
220
  return nil if delegated_obj.nil?
221
221
 
222
222
  delegated_obj.class.attributes[rule.to]
223
223
  end
224
224
 
225
225
  def attribute_value_for(element, rule)
226
- return element.send(rule.to) unless rule.delegate
226
+ return element.public_send(rule.to) unless rule.delegate
227
227
 
228
- element.send(rule.delegate).send(rule.to)
228
+ element.public_send(rule.delegate).public_send(rule.to)
229
229
  end
230
230
 
231
231
  def self.type
@@ -254,7 +254,7 @@ module Lutaml
254
254
  # For text + entity without elements, return joined String.
255
255
  has_element_children = @root.children.any? do |child|
256
256
  !child.text? && !entity_reference_node?(child) &&
257
- !(child.respond_to?(:processing_instruction?) && child.processing_instruction?)
257
+ !(child.is_a?(Lutaml::Xml::XmlElement) && child.processing_instruction?)
258
258
  end
259
259
  return @root.text_children.map(&:text) if has_element_children
260
260
 
@@ -273,7 +273,7 @@ module Lutaml
273
273
  return false unless child.is_a?(Lutaml::Xml::NokogiriElement)
274
274
 
275
275
  node = child.adapter_node
276
- node.respond_to?(:entity_reference?) && node.entity_reference?
276
+ defined?(::Nokogiri) && node.is_a?(::Nokogiri::XML::EntityReference) && node.entity_reference?
277
277
  end
278
278
 
279
279
  def setup_register(register)
@@ -281,7 +281,7 @@ module Lutaml
281
281
 
282
282
  return_register = if register.is_a?(Lutaml::Model::Register)
283
283
  register.id
284
- elsif @root.respond_to?(:lutaml_register)
284
+ elsif @root.is_a?(Lutaml::Model::Serialize)
285
285
  @root.lutaml_register
286
286
  end
287
287
  return_register || Lutaml::Model::Config.default_register
@@ -377,7 +377,7 @@ module Lutaml
377
377
  ns_entry
378
378
  end
379
379
 
380
- ns_class.respond_to?(:uri) && ns_class.uri == namespace_uri
380
+ ns_class.is_a?(Class) && ns_class < Lutaml::Xml::XmlNamespace && ns_class.uri == namespace_uri
381
381
  end
382
382
  end
383
383
  end
@@ -60,7 +60,7 @@ module Lutaml
60
60
  # 3. Check if any child elements use :inherit
61
61
  # If they do and we have a prefix, use prefixed format
62
62
  # so children can properly reference the namespace
63
- if mapping.namespace_class.prefix_default && mapping.respond_to?(:elements)
63
+ if mapping.namespace_class.prefix_default && mapping.is_a?(Lutaml::Xml::Mapping)
64
64
  has_inherit_children = mapping.elements.any? do |elem_rule|
65
65
  elem_rule.namespace_param == :inherit
66
66
  end
@@ -99,7 +99,7 @@ module Lutaml
99
99
  has_explicit_pref = options.key?(:prefix) || options.key?(:use_prefix)
100
100
  if !has_explicit_pref && options[:stored_xml_declaration_plan]
101
101
  stored_plan = options[:stored_xml_declaration_plan]
102
- if stored_plan.respond_to?(:input_prefix_formats)
102
+ if stored_plan.is_a?(Lutaml::Xml::DeclarationPlan)
103
103
  # Check canonical URI and all aliases
104
104
  uris_to_check = [effective_ns_class.uri] + effective_ns_class.uri_aliases
105
105
  stored_plan.input_prefix_formats.each do |key, format|
@@ -165,7 +165,7 @@ module Lutaml
165
165
  end
166
166
 
167
167
  # 3. Check if any child elements use :inherit or form: :qualified
168
- if effective_ns_class.prefix_default && mapping.respond_to?(:elements)
168
+ if effective_ns_class.prefix_default && mapping.is_a?(Lutaml::Xml::Mapping)
169
169
  has_inherit_children = mapping.elements.any? do |elem_rule|
170
170
  elem_rule.namespace_param == :inherit
171
171
  end
@@ -44,7 +44,7 @@ module Lutaml
44
44
 
45
45
  # Check if element or its descendants use a namespace
46
46
  def element_needs_namespace?(element, namespace_class)
47
- return false unless element.respond_to?(:children)
47
+ return false unless element.is_a?(Lutaml::Xml::DataModel::XmlElement)
48
48
 
49
49
  element.children.each do |child|
50
50
  next unless child.is_a?(Lutaml::Xml::DataModel::XmlElement)
@@ -130,7 +130,7 @@ module Lutaml
130
130
  delegate = @content_mapping.delegate
131
131
  attr_def = if delegate
132
132
  delegate_obj = mapper_class.attributes[delegate]
133
- delegate_obj&.type&.attributes&.dig(attr_name) if delegate_obj&.type.respond_to?(:attributes)
133
+ delegate_obj&.type&.attributes&.dig(attr_name) if delegate_obj&.type.is_a?(Class) && delegate_obj&.type.include?(Lutaml::Model::Serialize)
134
134
  else
135
135
  mapper_class.attributes[attr_name]
136
136
  end
@@ -638,7 +638,7 @@ module Lutaml
638
638
 
639
639
  if name == "schemaLocation"
640
640
  location = caller_locations(1, 1)[0]
641
- caller_file = if defined?(File) && File.respond_to?(:basename)
641
+ caller_file = if defined?(File)
642
642
  File.basename(location.path)
643
643
  else
644
644
  location.path.to_s.split("/").last
@@ -90,6 +90,19 @@ module Lutaml
90
90
  !!@namespace_set
91
91
  end
92
92
 
93
+ # Pre-computed frozen Hash for the static namespace override case.
94
+ # Returns nil when no static override is needed — the caller should
95
+ # use the parent options directly, avoiding per-rule Hash allocation.
96
+ #
97
+ # Only rules with an explicit namespace (namespace_set? && != :inherit)
98
+ # produce a static override. The result is frozen and shared across
99
+ # all elements that evaluate this rule.
100
+ def static_namespace_option
101
+ return nil unless namespace_set? && @namespace_param != :inherit
102
+
103
+ @static_namespace_option ||= { default_namespace: namespace }.freeze
104
+ end
105
+
93
106
  def content_mapping?
94
107
  name.nil?
95
108
  end
@@ -215,14 +228,14 @@ module Lutaml
215
228
  ).tap do |dup_rule|
216
229
  # Manually preserve the exact @namespace_class object to avoid
217
230
  # recreating anonymous classes (which would have different object_ids)
218
- dup_rule.send(:namespace_class=, @namespace_class)
231
+ dup_rule.namespace_class = @namespace_class
219
232
 
220
233
  # Manually ensure @namespace and @prefix are new string objects
221
234
  if dup_rule.namespace
222
- dup_rule.send(:namespace=, dup_rule.namespace.dup)
235
+ dup_rule.namespace = dup_rule.namespace.dup
223
236
  end
224
237
  if dup_rule.prefix
225
- dup_rule.send(:prefix=, dup_rule.prefix.dup)
238
+ dup_rule.prefix = dup_rule.prefix.dup
226
239
  end
227
240
  end
228
241
  end
@@ -252,7 +252,7 @@ effective_register = nil, instance_is_serialize = nil)
252
252
  # Performance: Pre-build Set of child namespaced names for fast rule matching.
253
253
  # This allows skipping value_for_rule for ~91% of rules that have no matching child.
254
254
  child_names_set = nil
255
- if doc.respond_to?(:element_children)
255
+ if doc.is_a?(::Lutaml::Xml::XmlElement) || doc.is_a?(::Lutaml::Xml::Document)
256
256
  ec = doc.element_children
257
257
  unless ec.empty?
258
258
  child_names_set = Set.new
@@ -268,7 +268,7 @@ effective_register = nil, instance_is_serialize = nil)
268
268
  rule.name
269
269
  rule_to = rule.to
270
270
  rule_namespace_set = rule.namespace_set?
271
- rule_namespace_param = rule_namespace_set ? rule.namespace_param : nil
271
+ rule_namespace_set ? rule.namespace_param : nil
272
272
 
273
273
  attr = attribute_for_rule(rule)
274
274
  next if attr&.derived?
@@ -277,15 +277,14 @@ effective_register = nil, instance_is_serialize = nil)
277
277
  rule, attr
278
278
  )
279
279
 
280
- # Performance: Only create new_opts when we need to override default_namespace
281
- # Avoid dup for the common case where namespace is not set
282
- new_opts = if rule_namespace_set && rule_namespace_param != :inherit
283
- { default_namespace: rule.namespace }
284
- elsif default_namespace.nil? && namespace_uri
285
- { default_namespace: namespace_uri }
286
- else
287
- options
288
- end
280
+ # Performance: Use pre-computed frozen Hash for static namespace overrides.
281
+ # Falls back to per-element hash only for the dynamic namespace_uri case.
282
+ new_opts = rule.static_namespace_option ||
283
+ if default_namespace.nil? && namespace_uri
284
+ { default_namespace: namespace_uri }
285
+ else
286
+ options
287
+ end
289
288
 
290
289
  value = if rule.raw_mapping?
291
290
  doc.root.inner_xml
@@ -333,9 +332,6 @@ effective_register = nil, instance_is_serialize = nil)
333
332
  value = rule.transform_value(attr, value, :from, :xml)
334
333
  rule.deserialize(instance, value, attributes, context)
335
334
 
336
- # Mark attribute as set from XML (not using default).
337
- # Needed for instances created via allocate_for_deserialization
338
- # which use Hash.new(true) as @using_default default.
339
335
  instance.value_set_for(rule_to)
340
336
  end
341
337
 
@@ -364,7 +360,7 @@ effective_register = nil, instance_is_serialize = nil)
364
360
  mixed_content_option, xml_mapping = nil,
365
361
  instance_is_serialize = nil)
366
362
  instance.element_order = doc.root.order
367
- if doc.root.respond_to?(:attribute_order) && instance.respond_to?(:attribute_order=)
363
+ if instance_is_serialize && doc.root.is_a?(::Lutaml::Xml::XmlElement)
368
364
  instance.attribute_order = doc.root.attribute_order
369
365
  end
370
366
 
@@ -399,10 +395,12 @@ _effective_register)
399
395
  pi_mappings = xml_mapping.processing_instruction_mappings
400
396
  return if pi_mappings.empty?
401
397
 
402
- doc_pis = if doc.respond_to?(:processing_instructions)
398
+ doc_pis = if doc.is_a?(::Lutaml::Xml::XmlElement)
403
399
  doc.processing_instructions
404
- elsif doc.respond_to?(:root)
400
+ elsif doc.is_a?(::Lutaml::Xml::Document)
405
401
  doc.root.processing_instructions
402
+ else
403
+ []
406
404
  end || []
407
405
 
408
406
  pi_attr = Lutaml::Xml::DataModel::XmlProcessingInstruction
@@ -427,7 +425,7 @@ _effective_register)
427
425
  next if value.nil?
428
426
 
429
427
  instance.public_send(:"#{pi_mapping.to}=", value)
430
- instance.value_set_for(pi_mapping.to) if instance.respond_to?(:value_set_for)
428
+ instance.value_set_for(pi_mapping.to) if instance.is_a?(::Lutaml::Model::Serialize)
431
429
  end
432
430
  end
433
431
 
@@ -946,7 +944,7 @@ effective_register = lutaml_register)
946
944
  next unless collection.class.organization
947
945
 
948
946
  mappings = collection.class.mappings_for(:xml, register)
949
- next unless mappings.respond_to?(:consolidation_maps)
947
+ next unless mappings.is_a?(::Lutaml::Xml::Mapping)
950
948
  next if mappings.consolidation_maps.empty?
951
949
 
952
950
  mappings.consolidation_maps.each do |map|
@@ -61,15 +61,15 @@ module Lutaml
61
61
  needs = collect(nil, mapping)
62
62
 
63
63
  # If collection has instances, collect needs from instance type
64
- if collection.respond_to?(:instances) &&
65
- mapping.respond_to?(:find_element)
64
+ if collection.is_a?(Lutaml::Model::Collection) &&
65
+ mapping.is_a?(Lutaml::Xml::Mapping)
66
66
  instance_rule = mapping.find_element(:instances) ||
67
67
  mapping.elements.first
68
68
  if instance_rule
69
69
  attr_def = collection.class.attributes[instance_rule.to]
70
70
  if attr_def
71
71
  instance_type = attr_def.type(@register)
72
- if instance_type.respond_to?(:<) &&
72
+ if instance_type.is_a?(Class) &&
73
73
  instance_type < Lutaml::Model::Serialize
74
74
  instance_mapping = instance_type.mappings_for(:xml)
75
75
  if instance_mapping
@@ -314,7 +314,7 @@ module Lutaml
314
314
 
315
315
  # INSTANCE-AWARE COLLECTION
316
316
  if element
317
- value = element.send(attr_rule.to) if element.respond_to?(attr_rule.to)
317
+ value = element.public_send(attr_rule.to) if element.is_a?(Lutaml::Model::Serialize) && element.class.attributes.key?(attr_rule.to)
318
318
  next if value.nil? || ::Lutaml::Model::Utils.uninitialized?(value)
319
319
  end
320
320
 
@@ -346,7 +346,7 @@ module Lutaml
346
346
  # ==================================================================
347
347
  # PHASE 3: NAMESPACE_SCOPE CONFIGURATION
348
348
  # ==================================================================
349
- if mapping.respond_to?(:namespace_scope_config) && mapping.namespace_scope_config&.any?
349
+ if mapping.is_a?(Lutaml::Xml::Mapping) && mapping.namespace_scope_config&.any?
350
350
  mapping.namespace_scope_config.each do |cfg|
351
351
  config = NamespaceScopeConfig.new(cfg[:namespace],
352
352
  cfg[:declare] || :auto)
@@ -374,8 +374,8 @@ module Lutaml
374
374
  next unless child_type
375
375
 
376
376
  # Get child instance BEFORE adding Type ref
377
- child_instance = if element.respond_to?(elem_rule.to)
378
- element.send(elem_rule.to)
377
+ child_instance = if element.is_a?(Lutaml::Model::Serialize) && element.class.attributes.key?(elem_rule.to)
378
+ element.public_send(elem_rule.to)
379
379
  end
380
380
 
381
381
  # TYPE NAMESPACE INTEGRATION
@@ -390,7 +390,7 @@ module Lutaml
390
390
  needs.add_type_ref(ref)
391
391
  end
392
392
 
393
- next unless child_type.respond_to?(:<) &&
393
+ next unless child_type.is_a?(Class) &&
394
394
  child_type < Lutaml::Model::Serialize
395
395
 
396
396
  # Get child mapping
@@ -541,7 +541,7 @@ module Lutaml
541
541
  end
542
542
 
543
543
  # Collect namespace_scope configuration from mapping
544
- if mapping.respond_to?(:namespace_scope_config) && mapping.namespace_scope_config&.any?
544
+ if mapping.is_a?(Lutaml::Xml::Mapping) && mapping.namespace_scope_config&.any?
545
545
  mapping.namespace_scope_config.each do |cfg|
546
546
  config = NamespaceScopeConfig.new(cfg[:namespace],
547
547
  cfg[:declare] || :auto)
@@ -573,7 +573,7 @@ module Lutaml
573
573
  @register
574
574
  end
575
575
  child_type = attr_def.type(child_register)
576
- if child_type.respond_to?(:<) && child_type < Lutaml::Model::Serialize
576
+ if child_type.is_a?(Class) && child_type < Lutaml::Model::Serialize
577
577
  child_mapping = child_type.mappings_for(:xml)
578
578
  if child_mapping
579
579
  # Recursively collect with child's mapping and register context
@@ -131,7 +131,7 @@ module Lutaml
131
131
  #
132
132
  # @return [Symbol] :qualified or :unqualified
133
133
  def element_form_default
134
- if @ns_object.respond_to?(:element_form_default)
134
+ if @ns_object.is_a?(Class) && @ns_object < Lutaml::Xml::Namespace
135
135
  @ns_object.element_form_default
136
136
  else
137
137
  :qualified # W3C default
@@ -142,7 +142,7 @@ module Lutaml
142
142
  #
143
143
  # @return [Symbol] :qualified or :unqualified
144
144
  def attribute_form_default
145
- if @ns_object.respond_to?(:attribute_form_default)
145
+ if @ns_object.is_a?(Class) && @ns_object < Lutaml::Xml::Namespace
146
146
  @ns_object.attribute_form_default
147
147
  else
148
148
  :unqualified # W3C default
@@ -94,7 +94,7 @@ module Lutaml
94
94
  # Get element_form_default setting
95
95
  # @return [Symbol] :qualified or :unqualified
96
96
  def element_form_default
97
- if @namespace_class.respond_to?(:element_form_default)
97
+ if @namespace_class.is_a?(Class) && @namespace_class < Lutaml::Xml::Namespace
98
98
  @namespace_class.element_form_default
99
99
  else
100
100
  :qualified # W3C default
@@ -104,7 +104,7 @@ module Lutaml
104
104
  # Get attribute_form_default setting
105
105
  # @return [Symbol] :qualified or :unqualified
106
106
  def attribute_form_default
107
- if @namespace_class.respond_to?(:attribute_form_default)
107
+ if @namespace_class.is_a?(Class) && @namespace_class < Lutaml::Xml::Namespace
108
108
  @namespace_class.attribute_form_default
109
109
  else
110
110
  :unqualified # W3C default
@@ -169,12 +169,9 @@ module Lutaml
169
169
  end
170
170
 
171
171
  # Validate that namespace class has required methods
172
- unless @namespace_class.respond_to?(:uri)
173
- raise ArgumentError, "Namespace class must respond to :uri"
174
- end
175
-
176
- unless @namespace_class.respond_to?(:to_key)
177
- raise ArgumentError, "Namespace class must respond to :to_key"
172
+ unless @namespace_class.is_a?(Class) && @namespace_class < Lutaml::Xml::Namespace
173
+ raise ArgumentError,
174
+ "Namespace class must be a Lutaml::Xml::Namespace subclass"
178
175
  end
179
176
  end
180
177
  end
@@ -100,8 +100,9 @@ module Lutaml
100
100
  raise ArgumentError, "Namespace class cannot be nil"
101
101
  end
102
102
 
103
- unless @namespace_class.respond_to?(:to_key)
104
- raise ArgumentError, "Namespace class must respond to :to_key"
103
+ unless @namespace_class.is_a?(Class) && @namespace_class < Lutaml::Xml::Namespace
104
+ raise ArgumentError,
105
+ "Namespace class must be a Lutaml::Xml::Namespace subclass"
105
106
  end
106
107
  end
107
108
  end
@@ -58,7 +58,7 @@ module Lutaml
58
58
  # @example Find all types in a namespace
59
59
  # types = resolver.resolve_types_by_namespace("http://example.com/ns")
60
60
  def resolve_types_by_namespace(uri)
61
- return [] unless context.respond_to?(:registry)
61
+ return [] unless context.is_a?(Lutaml::Model::TypeContext)
62
62
 
63
63
  context.registry.types.select do |_name, type_class|
64
64
  next false unless type_class <= Lutaml::Model::Type::Value
@@ -92,7 +92,7 @@ module Lutaml
92
92
  type_ns = type_class.namespace_class
93
93
  return nil unless type_ns
94
94
 
95
- type_ns.respond_to?(:uri) ? type_ns.uri : nil
95
+ type_ns.is_a?(Class) && type_ns < Lutaml::Xml::Namespace ? type_ns.uri : nil
96
96
  end
97
97
 
98
98
  private
@@ -125,8 +125,8 @@ module Lutaml
125
125
  return true if ns1 == ns2
126
126
 
127
127
  # Compare by URI if both have it
128
- uri1 = ns1.respond_to?(:uri) ? ns1.uri : nil
129
- uri2 = ns2.respond_to?(:uri) ? ns2.uri : nil
128
+ uri1 = ns1.is_a?(Class) && ns1 < Lutaml::Xml::Namespace ? ns1.uri : nil
129
+ uri2 = ns2.is_a?(Class) && ns2 < Lutaml::Xml::Namespace ? ns2.uri : nil
130
130
 
131
131
  uri1 && uri2 && uri1 == uri2
132
132
  end
@@ -4,7 +4,7 @@ module Lutaml
4
4
  module Xml
5
5
  class NokogiriElement < AdapterElement
6
6
  def initialize(node, parent: nil, default_namespace: nil)
7
- @raw_text = node.respond_to?(:raw_content) ? node.raw_content : nil if node.is_a?(Moxml::Text)
7
+ @raw_text = node.raw_content if node.is_a?(Moxml::Text)
8
8
  super
9
9
  end
10
10
 
@@ -27,7 +27,7 @@ module Lutaml
27
27
  end
28
28
 
29
29
  def attribute_value_for_build(attr)
30
- attr.respond_to?(:raw_value) ? attr.raw_value : attr.value
30
+ attr.raw_value
31
31
  end
32
32
  end
33
33
  end
@@ -23,7 +23,7 @@ module Lutaml
23
23
  # @return [Boolean] true if value should use polymorphic serialization
24
24
  def polymorphic_value?(attribute, value)
25
25
  return false unless attribute
26
- return false unless value.respond_to?(:class)
26
+ return false unless value
27
27
 
28
28
  # Check if attribute explicitly declares polymorphism
29
29
  return true if attribute.options[:polymorphic] || attribute.polymorphic?
@@ -84,13 +84,13 @@ module Lutaml
84
84
  end
85
85
 
86
86
  def min_occurrences
87
- return unless respond_to?(:min_occurs)
87
+ return unless is_a?(Element) || is_a?(Group) || is_a?(Any)
88
88
 
89
89
  @min_occurs&.to_i || 1
90
90
  end
91
91
 
92
92
  def max_occurrences
93
- return unless respond_to?(:max_occurs)
93
+ return unless is_a?(Element) || is_a?(Group) || is_a?(Any)
94
94
  return "*" if @max_occurs == "unbounded"
95
95
 
96
96
  @max_occurs&.to_i || 1
@@ -106,13 +106,13 @@ module Lutaml
106
106
 
107
107
  seen[self] = true
108
108
  resolved_element_order&.each do |element|
109
- if element.respond_to?(:ref) && element.ref
109
+ if element.is_a?(Element) && element.ref
110
110
  if resolvable_reference?(element.ref)
111
111
  element.referenced_object&.unresolvable_items(array, seen)
112
112
  else
113
113
  array << element
114
114
  end
115
- elsif element.respond_to?(:unresolvable_items)
115
+ elsif element.is_a?(Base)
116
116
  element.unresolvable_items(array, seen)
117
117
  end
118
118
  end
@@ -174,7 +174,7 @@ module Lutaml
174
174
  end
175
175
 
176
176
  def find_object_for_ref(reference)
177
- if respond_to?(:referenced_object)
177
+ if is_a?(Element) || is_a?(Group) || is_a?(AttributeGroup) || is_a?(Attribute) || is_a?(ExtensionComplexContent)
178
178
  referenced_object
179
179
  else
180
180
  find_object(collection_for_reference(reference), reference)
@@ -196,14 +196,14 @@ module Lutaml
196
196
  next unless element == instance
197
197
 
198
198
  method_name = ::Lutaml::Model::Utils.snake_case(instance.name)
199
- array[i] = Array(send(method_name))[index]
199
+ array[i] = Array(public_send(method_name))[index]
200
200
  index += 1
201
201
  end
202
202
  end
203
203
 
204
204
  def assign_root_value!(value, root, seen)
205
205
  Array(value).each do |child|
206
- next unless child.respond_to?(:assign_root!)
206
+ next unless child.is_a?(Base)
207
207
 
208
208
  child_root = child.is_a?(Schema) ? child : root
209
209
  child.assign_root!(child_root, seen)