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
|
@@ -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
|
|
@@ -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"
|
|
@@ -208,4 +208,243 @@ RSpec.describe Lutaml::JsonLd::Transform do
|
|
|
208
208
|
expect(restored.tags).to eq(["en", "fr"])
|
|
209
209
|
end
|
|
210
210
|
end
|
|
211
|
+
|
|
212
|
+
describe "multiple types" do
|
|
213
|
+
before do
|
|
214
|
+
stub_const("DctermsTestNs", Class.new(Lutaml::Rdf::Namespace) do
|
|
215
|
+
uri "http://purl.org/dc/terms/"
|
|
216
|
+
prefix "dcterms"
|
|
217
|
+
end)
|
|
218
|
+
|
|
219
|
+
stub_const("MultiTypeJsonLdModel", Class.new(Lutaml::Model::Serializable) do
|
|
220
|
+
attribute :name, :string
|
|
221
|
+
|
|
222
|
+
rdf do
|
|
223
|
+
namespace TestSkosNs, DctermsTestNs
|
|
224
|
+
|
|
225
|
+
subject { |m| "http://example.org/#{m.name}" } # rubocop:disable RSpec/NamedSubject
|
|
226
|
+
|
|
227
|
+
type ["skos:Concept", "dcterms:Agent"]
|
|
228
|
+
|
|
229
|
+
predicate :name, namespace: TestExNs, to: :name
|
|
230
|
+
end
|
|
231
|
+
end)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it "generates @type as array for multiple types" do
|
|
235
|
+
instance = MultiTypeJsonLdModel.new(name: "multi")
|
|
236
|
+
parsed = JSON.parse(instance.to_jsonld)
|
|
237
|
+
expect(parsed["@type"]).to eq(["skos:Concept", "dcterms:Agent"])
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "generates @type as string for single type" do
|
|
241
|
+
instance = JsonLdTestModel.new(name: "single")
|
|
242
|
+
parsed = JSON.parse(instance.to_jsonld)
|
|
243
|
+
expect(parsed["@type"]).to eq("skos:Concept")
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
describe "URI reference predicates" do
|
|
248
|
+
before do
|
|
249
|
+
stub_const("UriRefJsonLdModel", Class.new(Lutaml::Model::Serializable) do
|
|
250
|
+
attribute :name, :string
|
|
251
|
+
attribute :related, :string, collection: true
|
|
252
|
+
|
|
253
|
+
rdf do
|
|
254
|
+
namespace TestSkosNs, TestExNs
|
|
255
|
+
|
|
256
|
+
subject { |m| "http://example.org/#{m.name}" } # rubocop:disable RSpec/NamedSubject
|
|
257
|
+
|
|
258
|
+
type "skos:Concept"
|
|
259
|
+
|
|
260
|
+
predicate :name, namespace: TestExNs, to: :name
|
|
261
|
+
predicate :related, namespace: TestSkosNs, to: :related,
|
|
262
|
+
uri_reference: true
|
|
263
|
+
end
|
|
264
|
+
end)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it "generates @type @id in context for uri_reference predicates" do
|
|
268
|
+
instance = UriRefJsonLdModel.new(name: "test", related: ["skos:other"])
|
|
269
|
+
parsed = JSON.parse(instance.to_jsonld)
|
|
270
|
+
expect(parsed["@context"]["related"]).to eq({
|
|
271
|
+
"@id" => "http://www.w3.org/2004/02/skos/core#related",
|
|
272
|
+
"@type" => "@id",
|
|
273
|
+
})
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it "serializes URI reference as @id object" do
|
|
277
|
+
instance = UriRefJsonLdModel.new(name: "test", related: ["skos:other"])
|
|
278
|
+
parsed = JSON.parse(instance.to_jsonld)
|
|
279
|
+
expect(parsed["related"]).to eq([{ "@id" => "skos:other" }])
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it "serializes single URI reference value as @id object" do
|
|
283
|
+
stub_const("SingleUriRefModel", Class.new(Lutaml::Model::Serializable) do
|
|
284
|
+
attribute :name, :string
|
|
285
|
+
attribute :link, :string
|
|
286
|
+
|
|
287
|
+
rdf do
|
|
288
|
+
namespace TestSkosNs, TestExNs
|
|
289
|
+
|
|
290
|
+
subject { |m| "http://example.org/#{m.name}" } # rubocop:disable RSpec/NamedSubject
|
|
291
|
+
|
|
292
|
+
type "skos:Concept"
|
|
293
|
+
|
|
294
|
+
predicate :name, namespace: TestExNs, to: :name
|
|
295
|
+
predicate :related, namespace: TestSkosNs, to: :link,
|
|
296
|
+
uri_reference: true
|
|
297
|
+
end
|
|
298
|
+
end)
|
|
299
|
+
|
|
300
|
+
instance = SingleUriRefModel.new(name: "test", link: "skos:something")
|
|
301
|
+
parsed = JSON.parse(instance.to_jsonld)
|
|
302
|
+
expect(parsed["related"]).to eq({ "@id" => "skos:something" })
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
describe "member linking predicates" do
|
|
307
|
+
before do
|
|
308
|
+
stub_const("JsonLdChildModel", Class.new(Lutaml::Model::Serializable) do
|
|
309
|
+
attribute :cid, :string
|
|
310
|
+
attribute :label, :string
|
|
311
|
+
|
|
312
|
+
rdf do
|
|
313
|
+
namespace TestSkosNs, TestExNs
|
|
314
|
+
|
|
315
|
+
subject { |m| "http://example.org/item/#{m.cid}" } # rubocop:disable RSpec/NamedSubject, RSpec/MultipleSubjects
|
|
316
|
+
|
|
317
|
+
type "skos:Concept"
|
|
318
|
+
|
|
319
|
+
predicate :prefLabel, namespace: TestSkosNs, to: :label
|
|
320
|
+
end
|
|
321
|
+
end)
|
|
322
|
+
|
|
323
|
+
stub_const("JsonLdParentModel", Class.new(Lutaml::Model::Serializable) do
|
|
324
|
+
attribute :name, :string
|
|
325
|
+
attribute :children, JsonLdChildModel, collection: true
|
|
326
|
+
|
|
327
|
+
rdf do
|
|
328
|
+
namespace TestSkosNs, TestExNs
|
|
329
|
+
|
|
330
|
+
subject { |m| "http://example.org/group/#{m.name}" } # rubocop:disable RSpec/NamedSubject, RSpec/MultipleSubjects
|
|
331
|
+
|
|
332
|
+
type "skos:Collection"
|
|
333
|
+
|
|
334
|
+
predicate :prefLabel, namespace: TestSkosNs, to: :name
|
|
335
|
+
|
|
336
|
+
members :children,
|
|
337
|
+
predicate_name: :member,
|
|
338
|
+
namespace: TestSkosNs
|
|
339
|
+
end
|
|
340
|
+
end)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
it "includes linking term in @context with @type @id" do
|
|
344
|
+
parent = JsonLdParentModel.new(
|
|
345
|
+
name: "grp1",
|
|
346
|
+
children: [JsonLdChildModel.new(cid: "a", label: "Alpha")],
|
|
347
|
+
)
|
|
348
|
+
parsed = JSON.parse(parent.to_jsonld)
|
|
349
|
+
expect(parsed["@context"]["member"]).to eq({
|
|
350
|
+
"@id" => "http://www.w3.org/2004/02/skos/core#member",
|
|
351
|
+
"@type" => "@id",
|
|
352
|
+
})
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it "generates @id references for linked members in @graph" do
|
|
356
|
+
parent = JsonLdParentModel.new(
|
|
357
|
+
name: "grp1",
|
|
358
|
+
children: [
|
|
359
|
+
JsonLdChildModel.new(cid: "a", label: "Alpha"),
|
|
360
|
+
JsonLdChildModel.new(cid: "b", label: "Beta"),
|
|
361
|
+
],
|
|
362
|
+
)
|
|
363
|
+
parsed = JSON.parse(parent.to_jsonld)
|
|
364
|
+
parent_resource = parsed["@graph"].find { |r| r["@type"] }
|
|
365
|
+
expect(parent_resource["member"]).to eq([
|
|
366
|
+
{ "@id" => "http://example.org/item/a" },
|
|
367
|
+
{ "@id" => "http://example.org/item/b" },
|
|
368
|
+
])
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
it "includes member resources in @graph" do
|
|
372
|
+
parent = JsonLdParentModel.new(
|
|
373
|
+
name: "grp1",
|
|
374
|
+
children: [JsonLdChildModel.new(cid: "a", label: "Alpha")],
|
|
375
|
+
)
|
|
376
|
+
parsed = JSON.parse(parent.to_jsonld)
|
|
377
|
+
member = parsed["@graph"].find { |r| r["prefLabel"] == "Alpha" }
|
|
378
|
+
expect(member).not_to be_nil
|
|
379
|
+
expect(member["@id"]).to eq("http://example.org/item/a")
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
it "merges child namespaces into @context" do
|
|
383
|
+
parent = JsonLdParentModel.new(
|
|
384
|
+
name: "grp1",
|
|
385
|
+
children: [JsonLdChildModel.new(cid: "a", label: "Alpha")],
|
|
386
|
+
)
|
|
387
|
+
parsed = JSON.parse(parent.to_jsonld)
|
|
388
|
+
expect(parsed["@context"]["skos"]).to eq("http://www.w3.org/2004/02/skos/core#")
|
|
389
|
+
expect(parsed["@context"]["ex"]).to eq("http://example.org/")
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
it "omits linking key when members have no linking predicate" do
|
|
393
|
+
stub_const("UnlinkedChild", Class.new(Lutaml::Model::Serializable) do
|
|
394
|
+
attribute :label, :string
|
|
395
|
+
|
|
396
|
+
rdf do
|
|
397
|
+
namespace TestSkosNs
|
|
398
|
+
|
|
399
|
+
subject { |m| "http://example.org/#{m.label}" } # rubocop:disable RSpec/NamedSubject, RSpec/MultipleSubjects
|
|
400
|
+
|
|
401
|
+
predicate :prefLabel, namespace: TestSkosNs, to: :label
|
|
402
|
+
end
|
|
403
|
+
end)
|
|
404
|
+
|
|
405
|
+
stub_const("UnlinkedParent", Class.new(Lutaml::Model::Serializable) do
|
|
406
|
+
attribute :name, :string
|
|
407
|
+
attribute :items, UnlinkedChild, collection: true
|
|
408
|
+
|
|
409
|
+
rdf do
|
|
410
|
+
namespace TestSkosNs
|
|
411
|
+
|
|
412
|
+
subject { |m| "http://example.org/#{m.name}" } # rubocop:disable RSpec/NamedSubject, RSpec/MultipleSubjects
|
|
413
|
+
|
|
414
|
+
predicate :prefLabel, namespace: TestSkosNs, to: :name
|
|
415
|
+
|
|
416
|
+
members :items
|
|
417
|
+
end
|
|
418
|
+
end)
|
|
419
|
+
|
|
420
|
+
parent = UnlinkedParent.new(
|
|
421
|
+
name: "grp",
|
|
422
|
+
items: [UnlinkedChild.new(label: "a")],
|
|
423
|
+
)
|
|
424
|
+
parsed = JSON.parse(parent.to_jsonld)
|
|
425
|
+
expect(parsed["@context"]).not_to have_key("member")
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
describe "empty type array" do
|
|
430
|
+
before do
|
|
431
|
+
stub_const("NoTypeJsonLdModel", Class.new(Lutaml::Model::Serializable) do
|
|
432
|
+
attribute :name, :string
|
|
433
|
+
|
|
434
|
+
rdf do
|
|
435
|
+
namespace TestExNs
|
|
436
|
+
|
|
437
|
+
subject { |m| "http://example.org/#{m.name}" } # rubocop:disable RSpec/NamedSubject
|
|
438
|
+
|
|
439
|
+
predicate :name, namespace: TestExNs, to: :name
|
|
440
|
+
end
|
|
441
|
+
end)
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
it "omits @type when no types declared" do
|
|
445
|
+
instance = NoTypeJsonLdModel.new(name: "test")
|
|
446
|
+
parsed = JSON.parse(instance.to_jsonld)
|
|
447
|
+
expect(parsed).not_to have_key("@type")
|
|
448
|
+
end
|
|
449
|
+
end
|
|
211
450
|
end
|
|
@@ -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
|