lutaml-model 0.8.8 → 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/schema_validator.rb +3 -0
- 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
|
@@ -41,7 +41,7 @@ module Lutaml
|
|
|
41
41
|
resolved_element_order&.each do |child|
|
|
42
42
|
if child.is_a?(Element)
|
|
43
43
|
array << child
|
|
44
|
-
elsif child.
|
|
44
|
+
elsif child.is_a?(Group) || child.is_a?(Sequence) || child.is_a?(Choice) || child.is_a?(ComplexType)
|
|
45
45
|
child.child_elements(array)
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -54,7 +54,7 @@ module Lutaml
|
|
|
54
54
|
resolved_element_order&.any? do |child|
|
|
55
55
|
if child.is_a?(Element)
|
|
56
56
|
reference_matches?(element_name, child.ref || child.name)
|
|
57
|
-
elsif child.
|
|
57
|
+
elsif child.is_a?(Group) || child.is_a?(Sequence) || child.is_a?(Choice) || child.is_a?(ComplexType)
|
|
58
58
|
child.find_elements_used(element_name)
|
|
59
59
|
end
|
|
60
60
|
end || false
|
|
@@ -105,7 +105,7 @@ except: DIRECT_CHILD_ELEMENTS_EXCEPTION)
|
|
|
105
105
|
resolved_element_order&.each do |child|
|
|
106
106
|
if child.is_a?(Element)
|
|
107
107
|
array << child
|
|
108
|
-
elsif child.
|
|
108
|
+
elsif child.is_a?(Group) || child.is_a?(Sequence) || child.is_a?(Choice) || child.is_a?(ComplexType)
|
|
109
109
|
child.child_elements(array)
|
|
110
110
|
end
|
|
111
111
|
end
|
|
@@ -118,7 +118,7 @@ except: DIRECT_CHILD_ELEMENTS_EXCEPTION)
|
|
|
118
118
|
resolved_element_order&.any? do |child|
|
|
119
119
|
if child.is_a?(Element)
|
|
120
120
|
reference_matches?(element_name, child.ref || child.name)
|
|
121
|
-
elsif child.
|
|
121
|
+
elsif child.is_a?(Group) || child.is_a?(Sequence) || child.is_a?(Choice) || child.is_a?(ComplexType)
|
|
122
122
|
child.find_elements_used(element_name)
|
|
123
123
|
end
|
|
124
124
|
end || false
|
|
@@ -139,9 +139,9 @@ except: DIRECT_CHILD_ELEMENTS_EXCEPTION)
|
|
|
139
139
|
# Get elements from the primary content model (sequence, choice, or all).
|
|
140
140
|
# @return [Array<Element>] Elements exposed by the active content model
|
|
141
141
|
def elements
|
|
142
|
-
return sequence.element if sequence
|
|
143
|
-
return choice.element if choice
|
|
144
|
-
return all.element if all
|
|
142
|
+
return sequence.element if sequence
|
|
143
|
+
return choice.element if choice
|
|
144
|
+
return all.element if all
|
|
145
145
|
|
|
146
146
|
[]
|
|
147
147
|
end
|
|
@@ -28,8 +28,8 @@ module Lutaml
|
|
|
28
28
|
parts = []
|
|
29
29
|
parts << header
|
|
30
30
|
parts << context_details if @error.context && !@error.context.to_h.empty?
|
|
31
|
-
parts << suggestions_section if @error.
|
|
32
|
-
parts << troubleshooting_section if @error.
|
|
31
|
+
parts << suggestions_section if @error.is_a?(EnhancedError) && @error.suggestions.any?
|
|
32
|
+
parts << troubleshooting_section if @error.is_a?(EnhancedError) && @error.troubleshooting_tips.any?
|
|
33
33
|
parts.compact.join("\n\n")
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -39,7 +39,7 @@ module Lutaml
|
|
|
39
39
|
#
|
|
40
40
|
# @return [String] Error header
|
|
41
41
|
def header
|
|
42
|
-
severity = @error.
|
|
42
|
+
severity = @error.is_a?(EnhancedError) ? @error.severity.to_s.upcase : "ERROR"
|
|
43
43
|
"#{severity}: #{@error.message}"
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -44,7 +44,7 @@ module Lutaml
|
|
|
44
44
|
group.resolved_element_order&.each do |child|
|
|
45
45
|
if child.is_a?(Element)
|
|
46
46
|
array << child
|
|
47
|
-
elsif child.
|
|
47
|
+
elsif child.is_a?(Group) || child.is_a?(Sequence) || child.is_a?(Choice) || child.is_a?(ComplexType)
|
|
48
48
|
child.child_elements(array)
|
|
49
49
|
end
|
|
50
50
|
end
|
|
@@ -57,7 +57,7 @@ module Lutaml
|
|
|
57
57
|
resolved_element_order&.any? do |child|
|
|
58
58
|
if child.is_a?(Element)
|
|
59
59
|
reference_matches?(element_name, child.ref || child.name)
|
|
60
|
-
elsif child.
|
|
60
|
+
elsif child.is_a?(Group) || child.is_a?(Sequence) || child.is_a?(Choice) || child.is_a?(ComplexType)
|
|
61
61
|
child.find_elements_used(element_name)
|
|
62
62
|
end
|
|
63
63
|
end || false
|
|
@@ -6,6 +6,9 @@ module Lutaml
|
|
|
6
6
|
module Xml
|
|
7
7
|
module Schema
|
|
8
8
|
module Xsd
|
|
9
|
+
# Ensure error classes are loaded before SchemaValidator references them
|
|
10
|
+
Error # trigger autoload
|
|
11
|
+
|
|
9
12
|
# Validates XSD schema documents before parsing
|
|
10
13
|
#
|
|
11
14
|
# This validator checks that XML content is a valid XSD schema document
|
|
@@ -41,7 +41,7 @@ module Lutaml
|
|
|
41
41
|
resolved_element_order&.each do |child|
|
|
42
42
|
if child.is_a?(Element)
|
|
43
43
|
array << child
|
|
44
|
-
elsif child.
|
|
44
|
+
elsif child.is_a?(Group) || child.is_a?(Sequence) || child.is_a?(Choice) || child.is_a?(ComplexType)
|
|
45
45
|
child.child_elements(array)
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -54,7 +54,7 @@ module Lutaml
|
|
|
54
54
|
resolved_element_order&.any? do |child|
|
|
55
55
|
if child.is_a?(Element)
|
|
56
56
|
reference_matches?(element_name, child.ref || child.name)
|
|
57
|
-
elsif child.
|
|
57
|
+
elsif child.is_a?(Group) || child.is_a?(Sequence) || child.is_a?(Choice) || child.is_a?(ComplexType)
|
|
58
58
|
child.find_elements_used(element_name)
|
|
59
59
|
end
|
|
60
60
|
end || false
|
|
@@ -82,7 +82,7 @@ module Lutaml
|
|
|
82
82
|
attr_type = attr.type(register)
|
|
83
83
|
|
|
84
84
|
# Validate Type::Value xsd_type
|
|
85
|
-
if attr_type.
|
|
85
|
+
if attr_type.is_a?(Class) && attr_type < Lutaml::Model::Type::Value
|
|
86
86
|
type_name = attr_type.xsd_type
|
|
87
87
|
classification = classify_xsd_type(type_name, klass, register)
|
|
88
88
|
|
|
@@ -413,7 +413,7 @@ _mapping_rule = nil)
|
|
|
413
413
|
return attr.options[:xsd_type] if attr.options[:xsd_type]
|
|
414
414
|
|
|
415
415
|
# 2. Check if type has xsd_type method (Type-level)
|
|
416
|
-
if attr_type.
|
|
416
|
+
if attr_type.is_a?(Class) && attr_type < Lutaml::Model::Type::Value
|
|
417
417
|
# Special handling for Reference type
|
|
418
418
|
if attr_type == Lutaml::Model::Type::Reference
|
|
419
419
|
target_xsd_type = get_target_xsd_type(attr, register)
|
|
@@ -466,7 +466,7 @@ _mapping_rule = nil)
|
|
|
466
466
|
|
|
467
467
|
# Get namespace info from Model class (Serializable)
|
|
468
468
|
def get_model_namespace_info(klass)
|
|
469
|
-
mapping = klass.
|
|
469
|
+
mapping = klass.is_a?(Class) && klass.include?(Lutaml::Model::Serialize) ? klass.mappings_for(:xml) : nil
|
|
470
470
|
return {} unless mapping
|
|
471
471
|
|
|
472
472
|
{
|
|
@@ -487,8 +487,8 @@ _mapping_rule = nil)
|
|
|
487
487
|
|
|
488
488
|
# XmlNamespace class
|
|
489
489
|
{
|
|
490
|
-
uri: ns.
|
|
491
|
-
prefix: ns.
|
|
490
|
+
uri: ns.is_a?(Class) && ns < Lutaml::Xml::Namespace ? ns.uri : nil,
|
|
491
|
+
prefix: ns.is_a?(Class) && ns < Lutaml::Xml::Namespace ? ns.prefix_default : nil,
|
|
492
492
|
class: ns,
|
|
493
493
|
}
|
|
494
494
|
end
|
|
@@ -97,7 +97,7 @@ module Lutaml
|
|
|
97
97
|
raise Lutaml::Model::NoRootMappingError.new(self) unless valid
|
|
98
98
|
|
|
99
99
|
options[:encoding] = doc.encoding
|
|
100
|
-
if doc.
|
|
100
|
+
if doc.is_a?(Lutaml::Xml::Document) && doc.doctype
|
|
101
101
|
options[:doctype] = doc.doctype
|
|
102
102
|
end
|
|
103
103
|
end
|
|
@@ -214,7 +214,7 @@ module Lutaml
|
|
|
214
214
|
def process_xml_mapping_class_inheritance(mapping_class, &block)
|
|
215
215
|
# Start with a copy of the parent class's XML mapping (if any).
|
|
216
216
|
parent_class = superclass_with_xml_mapping(self)
|
|
217
|
-
parent_xml_mapping = if parent_class.
|
|
217
|
+
parent_xml_mapping = if parent_class.is_a?(Class) && parent_class.include?(Lutaml::Model::Serialize)
|
|
218
218
|
parent_class.mappings[:xml]
|
|
219
219
|
end
|
|
220
220
|
@xml_mapping = if parent_xml_mapping
|
|
@@ -224,7 +224,8 @@ module Lutaml
|
|
|
224
224
|
end
|
|
225
225
|
|
|
226
226
|
# Get the parent mapping instance (DSL already evaluated via xml_mapping_instance)
|
|
227
|
-
parent_mapping = if mapping_class.
|
|
227
|
+
parent_mapping = if mapping_class.is_a?(Class) &&
|
|
228
|
+
(mapping_class < Lutaml::Xml::Mapping || mapping_class.include?(Lutaml::Model::Serialize)) &&
|
|
228
229
|
mapping_class.xml_mapping_instance
|
|
229
230
|
mapping_class.xml_mapping_instance
|
|
230
231
|
else
|
|
@@ -91,7 +91,7 @@ parent_element_form_default)
|
|
|
91
91
|
if attr_type.is_a?(Class) && attr_type.include?(Lutaml::Model::Serialize)
|
|
92
92
|
attr_mapping = attr_type.mappings_for(:xml)
|
|
93
93
|
attr_ns = attr_mapping&.namespace_class
|
|
94
|
-
attr_ns_param = attr_mapping&.
|
|
94
|
+
attr_ns_param = attr_mapping&.namespace_param
|
|
95
95
|
# Use child's explicit namespace if it differs from parent's
|
|
96
96
|
# If child has no namespace declaration (nil) or explicit blank (:blank),
|
|
97
97
|
# do NOT inherit parent's namespace - return nil
|
|
@@ -165,7 +165,9 @@ is_polymorphic_subtype)
|
|
|
165
165
|
if polymorphic_config.is_a?(Hash)
|
|
166
166
|
poly_attr = polymorphic_config[:attribute]
|
|
167
167
|
poly_class_map = polymorphic_config[:class_map]
|
|
168
|
-
poly_value = value.
|
|
168
|
+
poly_value = if poly_attr && value.is_a?(Lutaml::Model::Serialize) && value.class.attributes.key?(poly_attr)
|
|
169
|
+
value.public_send(poly_attr)
|
|
170
|
+
end
|
|
169
171
|
if poly_value && poly_class_map && (klass_name = poly_class_map[poly_value.to_s])
|
|
170
172
|
Object.const_get(klass_name)
|
|
171
173
|
else
|
|
@@ -241,8 +241,8 @@ register_id)
|
|
|
241
241
|
def apply_custom_method(parent, rule, model_class, model_instance)
|
|
242
242
|
wrapper = ::Lutaml::Xml::CustomMethodWrapper.new(parent)
|
|
243
243
|
mapper_instance = model_class.new
|
|
244
|
-
mapper_instance.
|
|
245
|
-
|
|
244
|
+
mapper_instance.public_send(rule.custom_methods[:to], model_instance,
|
|
245
|
+
parent, wrapper)
|
|
246
246
|
end
|
|
247
247
|
|
|
248
248
|
# Apply element value handling collections
|
|
@@ -54,7 +54,7 @@ module Lutaml
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
# Use type's serialization if available
|
|
57
|
-
if rule.attribute_type.
|
|
57
|
+
if rule.attribute_type.is_a?(Class) && rule.attribute_type < Lutaml::Model::Type::Value
|
|
58
58
|
rule.attribute_type.serialize(value)
|
|
59
59
|
else
|
|
60
60
|
value.to_s
|
|
@@ -101,7 +101,7 @@ module Lutaml
|
|
|
101
101
|
# @param attribute_type [Class, nil] The attribute type
|
|
102
102
|
# @return [Boolean] true if custom Value type
|
|
103
103
|
def custom_value_type?(attribute_type)
|
|
104
|
-
attribute_type.
|
|
104
|
+
attribute_type.is_a?(Class) &&
|
|
105
105
|
attribute_type < Lutaml::Model::Type::Value
|
|
106
106
|
end
|
|
107
107
|
|
|
@@ -112,14 +112,12 @@ module Lutaml
|
|
|
112
112
|
# @return [String, nil] Serialized value or nil
|
|
113
113
|
def serialize_custom_value(value, attribute_type)
|
|
114
114
|
# Skip wrapping if value is already the correct type
|
|
115
|
-
if value.is_a?(attribute_type)
|
|
115
|
+
if value.is_a?(attribute_type)
|
|
116
116
|
return value.to_xml
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
wrapped_value = attribute_type.new(value)
|
|
120
|
-
|
|
121
|
-
wrapped_value.to_xml
|
|
122
|
-
end
|
|
120
|
+
wrapped_value.to_xml
|
|
123
121
|
end
|
|
124
122
|
|
|
125
123
|
# Find attribute definition from model class
|
|
@@ -139,9 +139,9 @@ module Lutaml
|
|
|
139
139
|
# @param model_instance [Object] The model instance
|
|
140
140
|
def handle_schema_location(root, model_instance)
|
|
141
141
|
# Case 1: SchemaLocation object (programmatic creation)
|
|
142
|
-
if model_instance.
|
|
142
|
+
if model_instance.is_a?(Lutaml::Model::Serialize) && model_instance.schema_location
|
|
143
143
|
schema_loc = model_instance.schema_location
|
|
144
|
-
if schema_loc.
|
|
144
|
+
if schema_loc.is_a?(Lutaml::Xml::SchemaLocation)
|
|
145
145
|
schema_attrs = schema_loc.to_xml_attributes
|
|
146
146
|
schema_attrs.each do |attr_name, attr_value|
|
|
147
147
|
attr = ::Lutaml::Xml::DataModel::XmlAttribute.new(attr_name,
|
|
@@ -150,7 +150,7 @@ module Lutaml
|
|
|
150
150
|
end
|
|
151
151
|
end
|
|
152
152
|
# Case 2: @raw_schema_location string (from parsing/round-trip)
|
|
153
|
-
elsif model_instance.
|
|
153
|
+
elsif model_instance.is_a?(Lutaml::Model::Serialize) && model_instance.raw_schema_location
|
|
154
154
|
raw_schema_loc = model_instance.raw_schema_location
|
|
155
155
|
if raw_schema_loc && !raw_schema_loc.empty?
|
|
156
156
|
add_raw_schema_location(root, raw_schema_loc)
|
|
@@ -292,7 +292,7 @@ module Lutaml
|
|
|
292
292
|
# @param model_instance [Object] The model instance
|
|
293
293
|
# @param options [Hash] Options
|
|
294
294
|
def apply_standard_rules(root, model_instance, options)
|
|
295
|
-
attr_order = model_instance.
|
|
295
|
+
attr_order = model_instance.is_a?(Lutaml::Model::Serialize) &&
|
|
296
296
|
model_instance.attribute_order
|
|
297
297
|
|
|
298
298
|
rules = if attr_order
|
|
@@ -104,9 +104,7 @@ module Lutaml
|
|
|
104
104
|
# @return [String, nil] parent's xsd_type if set
|
|
105
105
|
def inherited_xsd_type
|
|
106
106
|
return nil if superclass == Lutaml::Model::Type::Value
|
|
107
|
-
return nil unless superclass.respond_to?(:xsd_type)
|
|
108
107
|
|
|
109
|
-
# Get parent's @xsd_type directly (not default_xsd_type)
|
|
110
108
|
parent_xsd = superclass.instance_variable_get(:@xsd_type)
|
|
111
109
|
parent_xsd || superclass.inherited_xsd_type
|
|
112
110
|
end
|
|
@@ -116,7 +114,6 @@ module Lutaml
|
|
|
116
114
|
# @return [Class, Symbol, nil] parent's namespace class if set
|
|
117
115
|
def inherited_namespace
|
|
118
116
|
return nil if superclass == Lutaml::Model::Type::Value
|
|
119
|
-
return nil unless superclass.respond_to?(:xml_mapping)
|
|
120
117
|
|
|
121
118
|
parent_mapping = superclass.xml_mapping
|
|
122
119
|
parent_mapping&.namespace_class || superclass.inherited_namespace
|
|
@@ -136,7 +133,6 @@ module Lutaml
|
|
|
136
133
|
# @return [Lutaml::Xml::Type::ValueXmlMapping] the new XML mapping
|
|
137
134
|
def inherit_xml_mapping_from_parent
|
|
138
135
|
return create_xml_mapping if superclass == Lutaml::Model::Type::Value
|
|
139
|
-
return create_xml_mapping unless superclass.respond_to?(:xml_mapping)
|
|
140
136
|
|
|
141
137
|
parent_mapping = superclass.xml_mapping
|
|
142
138
|
return create_xml_mapping unless parent_mapping
|
|
@@ -157,7 +157,7 @@ module Lutaml
|
|
|
157
157
|
def name
|
|
158
158
|
return @name unless namespace_prefix
|
|
159
159
|
|
|
160
|
-
@prefixed_name ||= "#{namespace_prefix}:#{@name}" # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
160
|
+
@prefixed_name ||= -"#{namespace_prefix}:#{@name}" # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
161
161
|
end
|
|
162
162
|
|
|
163
163
|
def namespaced_name
|
|
@@ -174,15 +174,18 @@ module Lutaml
|
|
|
174
174
|
# 3. Fall back to namespaces[nil] if exists
|
|
175
175
|
# 4. Return unprefixed name
|
|
176
176
|
ns = namespaces
|
|
177
|
-
if namespace_prefix && ns[namespace_prefix]
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
177
|
+
raw = if namespace_prefix && ns[namespace_prefix]
|
|
178
|
+
"#{ns[namespace_prefix].uri}:#{@name}"
|
|
179
|
+
elsif @default_namespace
|
|
180
|
+
"#{@default_namespace}:#{@name}"
|
|
181
|
+
elsif ns[nil]
|
|
182
|
+
"#{ns[nil].uri}:#{@name}"
|
|
183
|
+
else
|
|
184
|
+
@name
|
|
185
|
+
end
|
|
186
|
+
# Deduplicate the string to reduce GC pressure from repeated
|
|
187
|
+
# namespace URIs across thousands of elements.
|
|
188
|
+
-raw
|
|
186
189
|
end
|
|
187
190
|
end
|
|
188
191
|
|
|
@@ -236,7 +239,8 @@ module Lutaml
|
|
|
236
239
|
|
|
237
240
|
@namespace_uri = begin
|
|
238
241
|
ns = namespace
|
|
239
|
-
ns&.uri
|
|
242
|
+
uri = ns&.uri
|
|
243
|
+
uri ? -uri : nil
|
|
240
244
|
end
|
|
241
245
|
end
|
|
242
246
|
|
|
@@ -296,9 +300,13 @@ module Lutaml
|
|
|
296
300
|
|
|
297
301
|
def text
|
|
298
302
|
return @text if children.empty?
|
|
299
|
-
return
|
|
303
|
+
return @computed_text if defined?(@computed_text)
|
|
300
304
|
|
|
301
|
-
|
|
305
|
+
@computed_text = if content_bearing_children_count > 1
|
|
306
|
+
text_children.map(&:text)
|
|
307
|
+
else
|
|
308
|
+
text_children.map(&:text).join
|
|
309
|
+
end
|
|
302
310
|
end
|
|
303
311
|
|
|
304
312
|
def cdata
|
data/lutaml-model.gemspec
CHANGED
|
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
|
|
|
36
36
|
spec.add_dependency "canon"
|
|
37
37
|
spec.add_dependency "concurrent-ruby"
|
|
38
38
|
spec.add_dependency "liquid", ">= 4.0", "< 6.0"
|
|
39
|
-
spec.add_dependency "moxml", ">= 0.1.
|
|
39
|
+
spec.add_dependency "moxml", ">= 0.1.20"
|
|
40
40
|
spec.add_dependency "ostruct"
|
|
41
41
|
spec.add_dependency "rubyzip", "~> 2.3"
|
|
42
42
|
spec.add_dependency "thor"
|
|
@@ -129,14 +129,14 @@ RSpec.describe Lutaml::Model::CachedTypeResolver do
|
|
|
129
129
|
expect(result2).to eq(result1)
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
-
it "
|
|
132
|
+
it "caches Class objects with substitution applied" do
|
|
133
133
|
klass = Class.new
|
|
134
134
|
result = resolver.resolve(klass, context)
|
|
135
135
|
expect(result).to eq(klass)
|
|
136
136
|
|
|
137
|
-
# Should
|
|
137
|
+
# Should be cached
|
|
138
138
|
stats = resolver.cache_stats
|
|
139
|
-
expect(stats[:size]).to eq(
|
|
139
|
+
expect(stats[:size]).to eq(1)
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
it "raises UnknownTypeError for unknown types" do
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe "Optimization behaviors" do
|
|
6
|
+
before do
|
|
7
|
+
Lutaml::Model::GlobalContext.clear_caches
|
|
8
|
+
Lutaml::Model::TransformationRegistry.instance.clear
|
|
9
|
+
Lutaml::Model::GlobalRegister.instance.reset
|
|
10
|
+
Lutaml::Model::Store.clear
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
after do
|
|
14
|
+
Lutaml::Model::GlobalContext.clear_caches
|
|
15
|
+
Lutaml::Model::TransformationRegistry.instance.clear
|
|
16
|
+
Lutaml::Model::GlobalRegister.instance.reset
|
|
17
|
+
Lutaml::Model::Store.clear
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe "DeserializationContext propagation" do
|
|
21
|
+
let(:propagation_keys) do
|
|
22
|
+
Lutaml::Model::Serialize::DeserializationContext::PROPAGATION_KEYS
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "propagates only whitelisted keys" do
|
|
26
|
+
options = {
|
|
27
|
+
lutaml_parent: "parent",
|
|
28
|
+
lutaml_root: "root",
|
|
29
|
+
default_namespace: "http://example.com",
|
|
30
|
+
namespace_uri: "http://internal.com",
|
|
31
|
+
resolved_type: String,
|
|
32
|
+
converted: true,
|
|
33
|
+
polymorphic: :by_type,
|
|
34
|
+
collection: true,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
propagated = Lutaml::Model::Serialize::DeserializationContext.propagate(options)
|
|
38
|
+
|
|
39
|
+
expect(propagated).to eq({
|
|
40
|
+
lutaml_parent: "parent",
|
|
41
|
+
lutaml_root: "root",
|
|
42
|
+
default_namespace: "http://example.com",
|
|
43
|
+
polymorphic: :by_type,
|
|
44
|
+
collection: true,
|
|
45
|
+
})
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "excludes parent-internal keys" do
|
|
49
|
+
options = {
|
|
50
|
+
namespace_uri: "http://internal.com",
|
|
51
|
+
resolved_type: String,
|
|
52
|
+
converted: true,
|
|
53
|
+
mappings: double("mappings"),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
propagated = Lutaml::Model::Serialize::DeserializationContext.propagate(options)
|
|
57
|
+
|
|
58
|
+
expect(propagated).to be_empty
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "CHILD_PROPAGATION_KEYS matches PROPAGATION_KEYS" do
|
|
62
|
+
expect(Lutaml::Model::Attribute::CHILD_PROPAGATION_KEYS).to eq(propagation_keys)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe "conditional reference store registration" do
|
|
67
|
+
it "registers instances by default" do
|
|
68
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
69
|
+
attribute :id, :string
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
obj = klass.new(id: "test")
|
|
73
|
+
result = Lutaml::Model::Store.resolve(klass, :id, "test")
|
|
74
|
+
expect(result).to eq(obj)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "skips registration when skip_reference_registration is declared" do
|
|
78
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
79
|
+
attribute :id, :string
|
|
80
|
+
skip_reference_registration
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
klass.new(id: "test")
|
|
84
|
+
result = Lutaml::Model::Store.resolve(klass, :id, "test")
|
|
85
|
+
expect(result).to be_nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "reference_resolvable? returns true by default" do
|
|
89
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
90
|
+
attribute :id, :string
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
expect(klass.reference_resolvable?).to be true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "reference_resolvable? returns false after skip_reference_registration" do
|
|
97
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
98
|
+
attribute :id, :string
|
|
99
|
+
skip_reference_registration
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
expect(klass.reference_resolvable?).to be false
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "skips registration during allocate_for_deserialization" do
|
|
106
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
107
|
+
attribute :id, :string
|
|
108
|
+
skip_reference_registration
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
instance = klass.allocate_for_deserialization
|
|
112
|
+
instance.public_send(:id=, "deserialized")
|
|
113
|
+
result = Lutaml::Model::Store.resolve(klass, :id, "deserialized")
|
|
114
|
+
expect(result).to be_nil
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe "class_attributes accessor" do
|
|
119
|
+
it "returns raw attributes without register merging" do
|
|
120
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
121
|
+
attribute :name, :string
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
expect(klass.class_attributes).to be_a(Hash)
|
|
125
|
+
expect(klass.class_attributes.keys).to include(:name)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe "MappingRule#static_namespace_option" do
|
|
130
|
+
it "returns frozen hash for rules with explicit namespace" do
|
|
131
|
+
rule = Lutaml::Xml::MappingRule.new(
|
|
132
|
+
"test",
|
|
133
|
+
to: :test,
|
|
134
|
+
namespace: ExampleComNamespace,
|
|
135
|
+
namespace_set: true,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
result = rule.static_namespace_option
|
|
139
|
+
expect(result).to eq({ default_namespace: "http://example.com" })
|
|
140
|
+
expect(result).to be_frozen
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "returns the same object on repeated calls (cached)" do
|
|
144
|
+
rule = Lutaml::Xml::MappingRule.new(
|
|
145
|
+
"test",
|
|
146
|
+
to: :test,
|
|
147
|
+
namespace: ExampleComNamespace,
|
|
148
|
+
namespace_set: true,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
first = rule.static_namespace_option
|
|
152
|
+
second = rule.static_namespace_option
|
|
153
|
+
expect(first).to equal(second) # same object identity
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "returns nil when no namespace is set" do
|
|
157
|
+
rule = Lutaml::Xml::MappingRule.new("test", to: :test)
|
|
158
|
+
|
|
159
|
+
expect(rule.static_namespace_option).to be_nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "returns nil when namespace is :inherit" do
|
|
163
|
+
rule = Lutaml::Xml::MappingRule.new(
|
|
164
|
+
"test",
|
|
165
|
+
to: :test,
|
|
166
|
+
namespace: :inherit,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
expect(rule.static_namespace_option).to be_nil
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
describe "String deduplication in namespace URIs" do
|
|
174
|
+
it "XmlElement#namespace_uri deduplicates via unary minus" do
|
|
175
|
+
ns_instance = ExampleComNamespace.new
|
|
176
|
+
parent = Lutaml::Xml::XmlElement.new(nil, {}, [], nil,
|
|
177
|
+
name: "root",
|
|
178
|
+
parent_document: double(namespaces: { nil => ns_instance }))
|
|
179
|
+
uri = parent.namespace_uri
|
|
180
|
+
|
|
181
|
+
# The returned string should be the deduplicated version (-uri)
|
|
182
|
+
expect(uri).to eq("http://example.com")
|
|
183
|
+
expect(uri).to be_frozen
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it "XmlElement#namespaced_name deduplicates the result string" do
|
|
187
|
+
ExampleComNamespace.new
|
|
188
|
+
parent = Lutaml::Xml::XmlElement.new(nil, {}, [], nil,
|
|
189
|
+
name: "root",
|
|
190
|
+
default_namespace: "http://example.com",
|
|
191
|
+
parent_document: double(namespaces: {}))
|
|
192
|
+
result = parent.namespaced_name
|
|
193
|
+
|
|
194
|
+
expect(result).to eq("http://example.com:root")
|
|
195
|
+
expect(result).to be_frozen
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
describe "value_set_for" do
|
|
200
|
+
it "transitions from nil (all defaults) to per-attribute tracking" do
|
|
201
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
202
|
+
attribute :name, :string
|
|
203
|
+
attribute :age, :integer
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
obj = klass.allocate_for_deserialization
|
|
207
|
+
expect(obj.using_default?(:name)).to be true
|
|
208
|
+
expect(obj.using_default?(:age)).to be true
|
|
209
|
+
|
|
210
|
+
obj.value_set_for(:name)
|
|
211
|
+
expect(obj.using_default?(:name)).to be false
|
|
212
|
+
expect(obj.using_default?(:age)).to be true
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it "values_set_for sets multiple attributes at once" do
|
|
216
|
+
klass = Class.new(Lutaml::Model::Serializable) do
|
|
217
|
+
attribute :name, :string
|
|
218
|
+
attribute :age, :integer
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
obj = klass.allocate_for_deserialization
|
|
222
|
+
obj.values_set_for(%i[name age])
|
|
223
|
+
|
|
224
|
+
expect(obj.using_default?(:name)).to be false
|
|
225
|
+
expect(obj.using_default?(:age)).to be false
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|