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