lutaml-model 0.8.18 → 0.8.22
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-tests.yml +1 -1
- data/.github/workflows/rake.yml +1 -0
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +133 -72
- data/bench/bench_common.rb +8 -5
- data/bench/bench_niso.rb +1 -2
- data/bench/gate_config.rb +4 -1
- data/benchmark/serialization_benchmark.rb +4 -4
- data/docs/_guides/builder-dsl.adoc +127 -0
- data/docs/_guides/value-transformations.adoc +27 -0
- data/lib/compat/opal/generate_boot.rb +3 -1
- data/lib/compat/opal/lutaml_model_boot.rb +10 -7
- data/lib/compat/opal/moxml_boot.rb +6 -1
- data/lib/lutaml/hash_format/format.rb +29 -0
- data/lib/lutaml/hash_format.rb +5 -27
- data/lib/lutaml/json/format.rb +61 -0
- data/lib/lutaml/json.rb +4 -59
- data/lib/lutaml/jsonl/format.rb +22 -0
- data/lib/lutaml/jsonl.rb +5 -19
- data/lib/lutaml/key_value/format.rb +26 -0
- data/lib/lutaml/key_value/transform.rb +4 -1
- data/lib/lutaml/key_value/transformation/value_serializer.rb +0 -2
- data/lib/lutaml/key_value.rb +4 -21
- data/lib/lutaml/model/attribute.rb +38 -4
- data/lib/lutaml/model/collection.rb +2 -2
- data/lib/lutaml/model/compiled_rule.rb +1 -1
- data/lib/lutaml/model/config.rb +2 -2
- data/lib/lutaml/model/consolidation_map.rb +2 -2
- data/lib/lutaml/model/consolidation_rule/dispatch_block.rb +2 -2
- data/lib/lutaml/model/errors.rb +2 -2
- data/lib/lutaml/model/global_context.rb +4 -2
- data/lib/lutaml/model/mapping/mapping_rule.rb +4 -1
- data/lib/lutaml/model/mapping/model_mapping.rb +2 -2
- data/lib/lutaml/model/runtime_compatibility.rb +2 -2
- data/lib/lutaml/model/schema/class_loader.rb +2 -1
- data/lib/lutaml/model/schema/decorators/definition_collection.rb +4 -4
- data/lib/lutaml/model/schema/definitions/namespace.rb +2 -1
- data/lib/lutaml/model/schema/file_writer.rb +5 -2
- data/lib/lutaml/model/schema/renderers/base.rb +4 -3
- data/lib/lutaml/model/schema/renderers/mappings.rb +9 -3
- data/lib/lutaml/model/schema/renderers/member_decls.rb +9 -3
- data/lib/lutaml/model/schema/renderers/model.rb +18 -6
- data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +2 -1
- data/lib/lutaml/model/schema/renderers.rb +2 -1
- data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +6 -2
- data/lib/lutaml/model/schema/rnc_compiler.rb +3 -1
- data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +35 -15
- data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +4 -2
- data/lib/lutaml/model/schema/rng_compiler.rb +10 -5
- data/lib/lutaml/model/schema/xml_compiler/element_order.rb +2 -1
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +4 -2
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +39 -13
- data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +29 -10
- data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +2 -2
- data/lib/lutaml/model/schema/xml_compiler.rb +10 -5
- data/lib/lutaml/model/sequence.rb +4 -4
- data/lib/lutaml/model/serialize/attribute_definition.rb +10 -9
- data/lib/lutaml/model/serialize/builder.rb +55 -0
- data/lib/lutaml/model/serialize/format_conversion.rb +2 -2
- data/lib/lutaml/model/serialize/initialization.rb +12 -3
- data/lib/lutaml/model/serialize/model_import.rb +1 -0
- data/lib/lutaml/model/serialize.rb +4 -3
- data/lib/lutaml/model/type/uninitialized_class_guard.rb +1 -1
- data/lib/lutaml/model/type.rb +9 -0
- data/lib/lutaml/model/type_registry.rb +2 -2
- data/lib/lutaml/model/uninitialized_class.rb +1 -1
- data/lib/lutaml/model/union.rb +5 -2
- data/lib/lutaml/model/utils.rb +2 -2
- data/lib/lutaml/model/value_policy.rb +27 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +17 -16
- data/lib/lutaml/rdf/mapping.rb +2 -2
- data/lib/lutaml/rdf/namespace_set.rb +2 -2
- data/lib/lutaml/rdf/transform.rb +2 -2
- data/lib/lutaml/toml/format.rb +48 -0
- data/lib/lutaml/toml.rb +5 -46
- data/lib/lutaml/xml/adapter/base_adapter.rb +2 -2
- data/lib/lutaml/xml/builder/base.rb +2 -2
- data/lib/lutaml/xml/format.rb +331 -0
- data/lib/lutaml/xml/mapping.rb +2 -2
- data/lib/lutaml/xml/mapping_rule.rb +62 -3
- data/lib/lutaml/xml/model_transform.rb +132 -55
- data/lib/lutaml/xml/namespace_collector.rb +3 -3
- data/lib/lutaml/xml/parse_session.rb +59 -0
- data/lib/lutaml/xml/parsed_namespace_set.rb +2 -2
- data/lib/lutaml/xml/schema/builder.rb +5 -5
- data/lib/lutaml/xml/schema/xsd/schema_location_mapping.rb +2 -2
- data/lib/lutaml/xml/schema/xsd.rb +0 -2
- data/lib/lutaml/xml/schema/xsd_schema.rb +2 -1
- data/lib/lutaml/xml/serialization/format_conversion.rb +3 -3
- data/lib/lutaml/xml/transformation/element_builder.rb +2 -1
- data/lib/lutaml/xml/transformation/ordered_applier.rb +99 -4
- data/lib/lutaml/xml/xml_element.rb +2 -0
- data/lib/lutaml/xml.rb +4 -328
- data/lib/lutaml/yaml/format.rb +30 -0
- data/lib/lutaml/yaml.rb +5 -28
- data/lib/lutaml/yamls/adapter/mapping.rb +2 -2
- data/lib/lutaml/yamls/format.rb +22 -0
- data/lib/lutaml/yamls.rb +5 -19
- data/lib/tasks/benchmark_runner.rb +2 -2
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/model/attribute_apply_value_map_spec.rb +26 -13
- data/spec/lutaml/model/attribute_spec.rb +8 -3
- data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +2 -2
- data/spec/lutaml/model/custom_from_empty_values_spec.rb +74 -0
- data/spec/lutaml/model/custom_vobject_adapter_spec.rb +8 -8
- data/spec/lutaml/model/defaults_spec.rb +45 -0
- data/spec/lutaml/model/json_spec.rb +1 -2
- data/spec/lutaml/model/ordered_content_spec.rb +18 -7
- data/spec/lutaml/model/serialize/builder_spec.rb +361 -0
- data/spec/lutaml/model/store_spec.rb +3 -1
- data/spec/lutaml/model/transform_apply_value_map_spec.rb +6 -3
- data/spec/lutaml/model/union_attribute_spec.rb +4 -2
- data/spec/lutaml/model/value_policy_spec.rb +82 -0
- data/spec/lutaml/require_load_order_spec.rb +25 -0
- data/spec/lutaml/xml/attribute_element_namespace_disjoint_spec.rb +88 -0
- data/spec/lutaml/xml/attribute_namespace_disjointness_spec.rb +125 -0
- data/spec/lutaml/xml/out_of_namespace_parsing_spec.rb +123 -0
- data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +17 -6
- data/spec/lutaml/xml/schema/rng_compiler_spec.rb +15 -6
- data/spec/lutaml/xml/w3c_types_spec.rb +3 -6
- metadata +22 -4
|
@@ -9,10 +9,12 @@ module Lutaml
|
|
|
9
9
|
module XmlCompiler
|
|
10
10
|
extend self
|
|
11
11
|
|
|
12
|
-
autoload :SpecBuilder,
|
|
13
|
-
autoload :RegistryGenerator,
|
|
14
|
-
|
|
15
|
-
autoload :
|
|
12
|
+
autoload :SpecBuilder, "#{__dir__}/xml_compiler/spec_builder"
|
|
13
|
+
autoload :RegistryGenerator,
|
|
14
|
+
"#{__dir__}/xml_compiler/registry_generator"
|
|
15
|
+
autoload :SupportedDataTypes,
|
|
16
|
+
"#{__dir__}/xml_compiler/supported_data_types"
|
|
17
|
+
autoload :ElementOrder, "#{__dir__}/xml_compiler/element_order"
|
|
16
18
|
|
|
17
19
|
ELEMENT_ORDER_IGNORABLE = %w[import include].freeze
|
|
18
20
|
|
|
@@ -60,7 +62,10 @@ module Lutaml
|
|
|
60
62
|
FileWriter.write(output, dir, registry_generator: RegistryGenerator)
|
|
61
63
|
true
|
|
62
64
|
else
|
|
63
|
-
|
|
65
|
+
if options[:load_classes]
|
|
66
|
+
ClassLoader.load(output,
|
|
67
|
+
registry_generator: RegistryGenerator)
|
|
68
|
+
end
|
|
64
69
|
output.sources
|
|
65
70
|
end
|
|
66
71
|
end
|
|
@@ -27,12 +27,12 @@ module Lutaml
|
|
|
27
27
|
@model.attribute(name, type, options)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def sequence(&)
|
|
31
|
-
instance_eval(&)
|
|
30
|
+
def sequence(&block)
|
|
31
|
+
instance_eval(&block)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def map_element(name, **)
|
|
35
|
-
@attributes << @model.map_element(name, **)
|
|
34
|
+
def map_element(name, **options)
|
|
35
|
+
@attributes << @model.map_element(name, **options)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def import_model_mappings(model, register = nil)
|
|
@@ -108,24 +108,20 @@ module Lutaml
|
|
|
108
108
|
current = instance_variable_get(:"@#{name}") || []
|
|
109
109
|
new_value = current.is_a?(Array) ? current + [value] : value
|
|
110
110
|
instance_variable_set(:"@#{name}", new_value)
|
|
111
|
-
|
|
112
|
-
track_order(name, value, nil) if @__order_tracking__
|
|
111
|
+
record_mutation(name, value)
|
|
113
112
|
value
|
|
114
113
|
end
|
|
115
114
|
end
|
|
116
115
|
else
|
|
117
116
|
# For non-collection attributes, getter accepts optional argument
|
|
118
|
-
# for builder-style syntax: g.description(value) sets the value
|
|
117
|
+
# for builder-style syntax: g.description(value) sets the value.
|
|
118
|
+
# Tracking happens inside the setter, so no duplicate call here.
|
|
119
119
|
define_method(name) do |*args|
|
|
120
120
|
if args.empty?
|
|
121
121
|
instance_variable_get(:"@#{name}")
|
|
122
122
|
else
|
|
123
|
-
#
|
|
124
|
-
|
|
125
|
-
public_send(:"#{name}=", value)
|
|
126
|
-
# Track order for mixed_content serialization
|
|
127
|
-
track_order(name, value, nil) if @__order_tracking__
|
|
128
|
-
value
|
|
123
|
+
public_send(:"#{name}=", args.first)
|
|
124
|
+
args.first
|
|
129
125
|
end
|
|
130
126
|
end
|
|
131
127
|
end
|
|
@@ -156,12 +152,16 @@ module Lutaml
|
|
|
156
152
|
else
|
|
157
153
|
instance_variable_set(:"@#{name}", value)
|
|
158
154
|
end
|
|
155
|
+
# Track one entry per item so element_order reflects the
|
|
156
|
+
# number of <name> elements that will be emitted.
|
|
157
|
+
record_mutation_collection(name, value)
|
|
159
158
|
end
|
|
160
159
|
else
|
|
161
160
|
define_method(:"#{name}=") do |value|
|
|
162
161
|
value_set_for(name)
|
|
163
162
|
value = attr.cast_value(value, lutaml_register)
|
|
164
163
|
instance_variable_set(:"@#{name}", value)
|
|
164
|
+
record_mutation(name, value)
|
|
165
165
|
end
|
|
166
166
|
end
|
|
167
167
|
end
|
|
@@ -188,6 +188,7 @@ module Lutaml
|
|
|
188
188
|
|
|
189
189
|
attr = Attribute.new(name, type, options)
|
|
190
190
|
@attributes[name] = attr
|
|
191
|
+
@merged_attributes_cache = nil
|
|
191
192
|
define_attribute_methods(attr)
|
|
192
193
|
|
|
193
194
|
attr
|
|
@@ -75,6 +75,61 @@ module Lutaml
|
|
|
75
75
|
mapping&.ordered? || false
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
# Whether this instance was constructed via a builder block and
|
|
79
|
+
# therefore records mutations into element_order. Parsed models
|
|
80
|
+
# and instances constructed without a block do not track; their
|
|
81
|
+
# element_order (if any) comes from the parser and is treated as
|
|
82
|
+
# the complete source of truth by the serializer.
|
|
83
|
+
# @return [Boolean]
|
|
84
|
+
def order_tracking_enabled?
|
|
85
|
+
@__order_tracking__ ? true : false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Record a singular attribute mutation in element_order.
|
|
89
|
+
#
|
|
90
|
+
# No-op unless order tracking is enabled (i.e. the instance was
|
|
91
|
+
# constructed via a builder block on an ordered/mixed_content
|
|
92
|
+
# model). This is the single entry point for singular mutations
|
|
93
|
+
# and is called from generated setters and getter-with-arg paths
|
|
94
|
+
# so that direct setters (`x.foo = v`) and appender calls
|
|
95
|
+
# (`x.foo(v)`) behave identically.
|
|
96
|
+
#
|
|
97
|
+
# Returns `value` so generated setters preserve Ruby's setter
|
|
98
|
+
# contract: `obj.foo = v` evaluates to `v`. Callers like
|
|
99
|
+
# `obj.foo || (obj.foo = [])` depend on this.
|
|
100
|
+
#
|
|
101
|
+
# @param attribute_name [Symbol] The attribute being mutated
|
|
102
|
+
# @param value [Object, nil] The value being set; stored as text
|
|
103
|
+
# content for content-mapped attributes, ignored otherwise
|
|
104
|
+
# @return [Object] the passed value
|
|
105
|
+
def record_mutation(attribute_name, value = nil)
|
|
106
|
+
return value unless @__order_tracking__
|
|
107
|
+
|
|
108
|
+
track_order(attribute_name, value, nil)
|
|
109
|
+
value
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Record a collection-attribute mutation in element_order.
|
|
113
|
+
#
|
|
114
|
+
# Emits one entry per item so that element_order length matches
|
|
115
|
+
# the number of serialized child elements. No-op when tracking
|
|
116
|
+
# is disabled, when the value is nil/empty, or when the
|
|
117
|
+
# collection's frozen sentinel is preserved (no real data).
|
|
118
|
+
#
|
|
119
|
+
# Returns `value` so generated setters preserve Ruby's setter
|
|
120
|
+
# contract (see {record_mutation}).
|
|
121
|
+
#
|
|
122
|
+
# @param attribute_name [Symbol] The collection attribute
|
|
123
|
+
# @param value [Object, nil] The value assigned to the collection
|
|
124
|
+
# @return [Object] the passed value
|
|
125
|
+
def record_mutation_collection(attribute_name, value)
|
|
126
|
+
return value unless @__order_tracking__
|
|
127
|
+
return value if value.nil? || Lutaml::Model::Utils.uninitialized?(value)
|
|
128
|
+
|
|
129
|
+
Array(value).each { |item| track_order(attribute_name, item, nil) }
|
|
130
|
+
value
|
|
131
|
+
end
|
|
132
|
+
|
|
78
133
|
private
|
|
79
134
|
|
|
80
135
|
# Intercept method calls to track order for mixed_content
|
|
@@ -13,7 +13,7 @@ module Lutaml
|
|
|
13
13
|
# @param format [Symbol] The format (:xml, :json, etc.)
|
|
14
14
|
# @param args [Array] Additional arguments (e.g., mapping class for XML)
|
|
15
15
|
# @param block [Proc] The DSL block to evaluate
|
|
16
|
-
def process_mapping(format, *_args, &)
|
|
16
|
+
def process_mapping(format, *_args, &block)
|
|
17
17
|
klass = ::Lutaml::Model::Config.mappings_class_for(format)
|
|
18
18
|
existing = mappings[format]
|
|
19
19
|
mappings[format] = if existing.nil? || !existing.is_a?(klass)
|
|
@@ -21,7 +21,7 @@ module Lutaml
|
|
|
21
21
|
else
|
|
22
22
|
existing
|
|
23
23
|
end
|
|
24
|
-
mappings[format].instance_eval(&)
|
|
24
|
+
mappings[format].instance_eval(&block)
|
|
25
25
|
|
|
26
26
|
if mappings[format].is_a?(Lutaml::Xml::Mapping)
|
|
27
27
|
mappings[format].finalize(self)
|
|
@@ -115,13 +115,18 @@ module Lutaml
|
|
|
115
115
|
# Get all attributes for this model
|
|
116
116
|
#
|
|
117
117
|
# Merges class-level attributes with register-specific attributes.
|
|
118
|
+
# Memoized per register: this is on the per-element deserialization
|
|
119
|
+
# hot path, and the merged hash is stable until an attribute, import
|
|
120
|
+
# or register record mutates (those clear @merged_attributes_cache).
|
|
118
121
|
#
|
|
119
122
|
# @param register [Symbol, nil] The register context
|
|
120
123
|
# @return [Hash] The attributes hash
|
|
121
124
|
def attributes(register = nil)
|
|
122
125
|
ensure_imports!(register) if finalized?
|
|
123
126
|
if @register_records&.any?
|
|
124
|
-
|
|
127
|
+
register_id = extract_register_id(register)
|
|
128
|
+
(@merged_attributes_cache ||= {})[register_id] ||=
|
|
129
|
+
@attributes.merge(@register_records[register_id][:attributes])
|
|
125
130
|
else
|
|
126
131
|
@attributes
|
|
127
132
|
end
|
|
@@ -183,6 +188,9 @@ module Lutaml
|
|
|
183
188
|
GlobalContext.resolver.clear_cache(register_id)
|
|
184
189
|
end
|
|
185
190
|
|
|
191
|
+
# Clear memoized attribute merge (see .attributes)
|
|
192
|
+
@merged_attributes_cache = nil
|
|
193
|
+
|
|
186
194
|
# Clear per-Attribute type caches (stale entries from GC'd TypeContext objects)
|
|
187
195
|
class_attributes.each_value(&:clear_type_cache)
|
|
188
196
|
@register_records&.each_value do |record|
|
|
@@ -370,7 +378,6 @@ module Lutaml
|
|
|
370
378
|
instance_variable_get(:"@#{name}")
|
|
371
379
|
else
|
|
372
380
|
public_send(:"#{name}=", args.first)
|
|
373
|
-
track_order(name, args.first, nil) if @__order_tracking__
|
|
374
381
|
args.first
|
|
375
382
|
end
|
|
376
383
|
end
|
|
@@ -380,6 +387,7 @@ module Lutaml
|
|
|
380
387
|
reg_attr = resolve_register_attr(name)
|
|
381
388
|
value = reg_attr.cast_value(value, lutaml_register)
|
|
382
389
|
instance_variable_set(:"@#{name}", value)
|
|
390
|
+
record_mutation(name, value)
|
|
383
391
|
end
|
|
384
392
|
end
|
|
385
393
|
|
|
@@ -395,7 +403,7 @@ module Lutaml
|
|
|
395
403
|
current = [] if current.equal?(LAZY_EMPTY_COLLECTION)
|
|
396
404
|
new_value = current.is_a?(Array) ? current + [value] : value
|
|
397
405
|
instance_variable_set(:"@#{name}", new_value)
|
|
398
|
-
|
|
406
|
+
record_mutation(name, value)
|
|
399
407
|
value
|
|
400
408
|
end
|
|
401
409
|
end
|
|
@@ -411,6 +419,7 @@ module Lutaml
|
|
|
411
419
|
else
|
|
412
420
|
instance_variable_set(:"@#{name}", value)
|
|
413
421
|
end
|
|
422
|
+
record_mutation_collection(name, value)
|
|
414
423
|
end
|
|
415
424
|
end
|
|
416
425
|
|
|
@@ -175,18 +175,19 @@ module Lutaml
|
|
|
175
175
|
@using_default[attribute_name]
|
|
176
176
|
end
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
def method_missing(method_name, *args)
|
|
178
|
+
def method_missing(method_name, *args, &block)
|
|
180
179
|
if method_name.to_s.end_with?("=") && attribute_exist?(method_name)
|
|
181
180
|
define_singleton_method(method_name) do |value|
|
|
182
181
|
instance_variable_set(:"@#{method_name.to_s.chomp('=')}", value)
|
|
183
182
|
end
|
|
184
183
|
send(method_name, *args)
|
|
184
|
+
elsif ::Lutaml::Model::Attribute.default_evaluation? &&
|
|
185
|
+
self.class.respond_to?(method_name)
|
|
186
|
+
self.class.public_send(method_name, *args, &block)
|
|
185
187
|
else
|
|
186
188
|
super
|
|
187
189
|
end
|
|
188
190
|
end
|
|
189
|
-
# rubocop:enable Style/ArgumentsForwarding
|
|
190
191
|
|
|
191
192
|
def respond_to_missing?(method_name, include_private = false)
|
|
192
193
|
(method_name.to_s.end_with?("=") && attribute_exist?(method_name)) ||
|
|
@@ -17,7 +17,7 @@ module Lutaml
|
|
|
17
17
|
#
|
|
18
18
|
# The prepend ensures this method runs BEFORE the subclass's
|
|
19
19
|
# cast implementation, even if the subclass doesn't call super.
|
|
20
|
-
def cast(value, *args, **kwargs, &)
|
|
20
|
+
def cast(value, *args, **kwargs, &block)
|
|
21
21
|
# Return UninitializedClass unchanged - don't transform it
|
|
22
22
|
return value if Utils.uninitialized?(value)
|
|
23
23
|
|
data/lib/lutaml/model/type.rb
CHANGED
|
@@ -55,7 +55,16 @@ module Lutaml
|
|
|
55
55
|
hex_binary: "Lutaml::Model::Type::HexBinary",
|
|
56
56
|
}.freeze
|
|
57
57
|
|
|
58
|
+
BUILTIN_CLASSES =
|
|
59
|
+
TYPE_CODES.each_value.map { |type_class_name| const_get(type_class_name) }.freeze
|
|
60
|
+
|
|
58
61
|
class << self
|
|
62
|
+
# @param type [Class] a candidate type class
|
|
63
|
+
# @return [Boolean] whether the class is one of the built-ins
|
|
64
|
+
def builtin?(type)
|
|
65
|
+
BUILTIN_CLASSES.include?(type)
|
|
66
|
+
end
|
|
67
|
+
|
|
59
68
|
# Register all built-in types into any TypeRegistry.
|
|
60
69
|
#
|
|
61
70
|
# This method is used by the new type resolution architecture.
|
data/lib/lutaml/model/union.rb
CHANGED
|
@@ -131,7 +131,8 @@ module Lutaml
|
|
|
131
131
|
|
|
132
132
|
def validate_members!(members)
|
|
133
133
|
unless members.is_a?(::Array) && !members.empty?
|
|
134
|
-
raise ArgumentError,
|
|
134
|
+
raise ArgumentError,
|
|
135
|
+
"union type must be a non-empty array of types"
|
|
135
136
|
end
|
|
136
137
|
|
|
137
138
|
resolved = members.map { |member| resolve_member!(member) }
|
|
@@ -166,7 +167,9 @@ module Lutaml
|
|
|
166
167
|
return :no_match if keys.nil? || keys.empty?
|
|
167
168
|
|
|
168
169
|
field_names = member_field_names(member, format, register)
|
|
169
|
-
return :no_match unless keys.all?
|
|
170
|
+
return :no_match unless keys.all? do |key|
|
|
171
|
+
field_names.include?(key)
|
|
172
|
+
end
|
|
170
173
|
|
|
171
174
|
# Plain-Ruby assignment (format nil) builds straight from the
|
|
172
175
|
# attribute hash, mirroring how non-union model attributes accept
|
data/lib/lutaml/model/utils.rb
CHANGED
|
@@ -201,10 +201,10 @@ module Lutaml
|
|
|
201
201
|
end
|
|
202
202
|
end
|
|
203
203
|
|
|
204
|
-
def add_singleton_method_if_not_defined(instance, method_name, &)
|
|
204
|
+
def add_singleton_method_if_not_defined(instance, method_name, &block)
|
|
205
205
|
return if instance.singleton_class.method_defined?(method_name, false)
|
|
206
206
|
|
|
207
|
-
instance.define_singleton_method(method_name, &)
|
|
207
|
+
instance.define_singleton_method(method_name, &block)
|
|
208
208
|
end
|
|
209
209
|
|
|
210
210
|
# Determines the appropriate register for a child model during deserialization.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
# Decides how an attribute shapes incoming raw values: whether an
|
|
6
|
+
# Array is split element-wise (collection semantics) or handed whole
|
|
7
|
+
# to the declared type (value-owning semantics).
|
|
8
|
+
#
|
|
9
|
+
# A user-defined Type::Value subclass may legitimately wrap lists or
|
|
10
|
+
# maps in a singular slot (#752); built-in scalar types keep the
|
|
11
|
+
# "collection: true is missing" guidance error.
|
|
12
|
+
class ValuePolicy
|
|
13
|
+
def initialize(collection:, union:, reference:)
|
|
14
|
+
@collection = collection
|
|
15
|
+
@union = union
|
|
16
|
+
@reference = reference
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @param type [Class, Symbol] the resolved (or unresolved) type
|
|
20
|
+
# @return [Boolean] whether raw values must reach the type whole
|
|
21
|
+
def whole_value?(type)
|
|
22
|
+
!@collection && !@union && !@reference &&
|
|
23
|
+
type.is_a?(Class) && type < Type::Value && !Type.builtin?(type)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/lutaml/model/version.rb
CHANGED
data/lib/lutaml/model.rb
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module Lutaml
|
|
4
4
|
# Autoload top-level modules (outside Model namespace)
|
|
5
|
-
autoload :KeyValue, "#{__dir__}/key_value"
|
|
6
|
-
autoload :Json, "#{__dir__}/json"
|
|
7
|
-
autoload :Yaml, "#{__dir__}/yaml"
|
|
8
|
-
autoload :Toml, "#{__dir__}/toml"
|
|
9
|
-
autoload :HashFormat, "#{__dir__}/hash_format"
|
|
10
|
-
autoload :Jsonl, "#{__dir__}/jsonl"
|
|
11
|
-
autoload :Yamls, "#{__dir__}/yamls"
|
|
12
|
-
autoload :Xml, "#{__dir__}/xml"
|
|
5
|
+
autoload :KeyValue, "#{__dir__}/key_value/format"
|
|
6
|
+
autoload :Json, "#{__dir__}/json/format"
|
|
7
|
+
autoload :Yaml, "#{__dir__}/yaml/format"
|
|
8
|
+
autoload :Toml, "#{__dir__}/toml/format"
|
|
9
|
+
autoload :HashFormat, "#{__dir__}/hash_format/format"
|
|
10
|
+
autoload :Jsonl, "#{__dir__}/jsonl/format"
|
|
11
|
+
autoload :Yamls, "#{__dir__}/yamls/format"
|
|
12
|
+
autoload :Xml, "#{__dir__}/xml/format"
|
|
13
13
|
autoload :JsonLd, "#{__dir__}/jsonld"
|
|
14
14
|
autoload :YamlLd, "#{__dir__}/yamlld"
|
|
15
15
|
autoload :Turtle, "#{__dir__}/turtle"
|
|
@@ -72,6 +72,7 @@ module Lutaml
|
|
|
72
72
|
autoload :CollectionHandler, "#{__dir__}/model/collection_handler"
|
|
73
73
|
autoload :AttributeValidator, "#{__dir__}/model/attribute_validator"
|
|
74
74
|
autoload :Attribute, "#{__dir__}/model/attribute"
|
|
75
|
+
autoload :ValuePolicy, "#{__dir__}/model/value_policy"
|
|
75
76
|
autoload :JsonAdapter, "#{__dir__}/model/json_adapter"
|
|
76
77
|
autoload :SchemaLocation, "#{__dir__}/model/schema_location"
|
|
77
78
|
autoload :Validation, "#{__dir__}/model/validation"
|
|
@@ -258,14 +259,14 @@ end
|
|
|
258
259
|
|
|
259
260
|
# Format files register DSL methods - use require to ensure proper loading order
|
|
260
261
|
# These files set up FormatRegistry and detect/configure adapters
|
|
261
|
-
require "#{__dir__}/key_value"
|
|
262
|
-
require "#{__dir__}/json"
|
|
263
|
-
require "#{__dir__}/yaml"
|
|
264
|
-
require "#{__dir__}/toml"
|
|
265
|
-
require "#{__dir__}/hash_format"
|
|
266
|
-
require "#{__dir__}/jsonl"
|
|
267
|
-
require "#{__dir__}/yamls"
|
|
268
|
-
require "#{__dir__}/xml"
|
|
262
|
+
require "#{__dir__}/key_value/format"
|
|
263
|
+
require "#{__dir__}/json/format"
|
|
264
|
+
require "#{__dir__}/yaml/format"
|
|
265
|
+
require "#{__dir__}/toml/format"
|
|
266
|
+
require "#{__dir__}/hash_format/format"
|
|
267
|
+
require "#{__dir__}/jsonl/format"
|
|
268
|
+
require "#{__dir__}/yamls/format"
|
|
269
|
+
require "#{__dir__}/xml/format"
|
|
269
270
|
|
|
270
271
|
# Optional formats: require "lutaml/jsonld" or "lutaml/turtle" to enable.
|
|
271
272
|
# These are not eagerly loaded because they depend on optional gems
|
data/lib/lutaml/rdf/mapping.rb
CHANGED
data/lib/lutaml/rdf/transform.rb
CHANGED
|
@@ -17,9 +17,9 @@ module Lutaml
|
|
|
17
17
|
mapping.rdf_type.map { |t| resolve_single_type_uri(mapping, t) }
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def each_member(instance, member_rule, &)
|
|
20
|
+
def each_member(instance, member_rule, &block)
|
|
21
21
|
collection = Array(instance.public_send(member_rule.attr_name))
|
|
22
|
-
collection.each(&)
|
|
22
|
+
collection.each(&block)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def member_mapping_for(member, format)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Internal TOML format implementation. Loaded from lib/lutaml/toml.rb
|
|
4
|
+
# (the public entrypoint) and from lib/lutaml/model.rb (so the model
|
|
5
|
+
# entrypoint triggers the registration side effects).
|
|
6
|
+
# No require_relative "model" here — keeps the require graph acyclic.
|
|
7
|
+
|
|
8
|
+
require "lutaml/model/runtime_compatibility"
|
|
9
|
+
require_relative "../key_value/format"
|
|
10
|
+
require_relative "adapter"
|
|
11
|
+
|
|
12
|
+
module Lutaml
|
|
13
|
+
module Toml
|
|
14
|
+
class Error < StandardError; end
|
|
15
|
+
|
|
16
|
+
# Convenience aliases for common classes at the module level
|
|
17
|
+
# Allows Lutaml::Toml::Mapping to resolve to Lutaml::Toml::Adapter::Mapping
|
|
18
|
+
def self.const_missing(name)
|
|
19
|
+
if Adapter.const_defined?(name, false)
|
|
20
|
+
Adapter.const_get(name, false)
|
|
21
|
+
else
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Lutaml::Model::FormatRegistry.register(
|
|
29
|
+
:toml,
|
|
30
|
+
mapping_class: Lutaml::Toml::Adapter::Mapping,
|
|
31
|
+
adapter_class: nil,
|
|
32
|
+
transformer: Lutaml::Toml::Adapter::Transform,
|
|
33
|
+
key_value: true,
|
|
34
|
+
adapter_options: if Lutaml::Model.opal?
|
|
35
|
+
nil
|
|
36
|
+
else
|
|
37
|
+
{
|
|
38
|
+
available: %i[tomlib toml_rb],
|
|
39
|
+
default: Lutaml::Model::RuntimeCompatibility.windows? ? :toml_rb : :tomlib,
|
|
40
|
+
}
|
|
41
|
+
end,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
require_relative "type/serializers"
|
|
45
|
+
Lutaml::Toml::Type::Serializers.register_all!
|
|
46
|
+
|
|
47
|
+
# Auto-detection is now handled lazily by AdapterResolver on first use.
|
|
48
|
+
# No eager adapter selection at require time.
|
data/lib/lutaml/toml.rb
CHANGED
|
@@ -1,48 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# TOML format
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
require_relative "
|
|
8
|
-
require_relative "toml/adapter"
|
|
9
|
-
|
|
10
|
-
module Lutaml
|
|
11
|
-
module Toml
|
|
12
|
-
class Error < StandardError; end
|
|
13
|
-
|
|
14
|
-
# Convenience aliases for common classes at the module level
|
|
15
|
-
# Allows Lutaml::Toml::Mapping to resolve to Lutaml::Toml::Adapter::Mapping
|
|
16
|
-
def self.const_missing(name)
|
|
17
|
-
if Adapter.const_defined?(name, false)
|
|
18
|
-
Adapter.const_get(name, false)
|
|
19
|
-
else
|
|
20
|
-
super
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Register TOML format with the format registry
|
|
27
|
-
Lutaml::Model::FormatRegistry.register(
|
|
28
|
-
:toml,
|
|
29
|
-
mapping_class: Lutaml::Toml::Adapter::Mapping,
|
|
30
|
-
adapter_class: nil,
|
|
31
|
-
transformer: Lutaml::Toml::Adapter::Transform,
|
|
32
|
-
key_value: true,
|
|
33
|
-
adapter_options: if Lutaml::Model.opal?
|
|
34
|
-
nil
|
|
35
|
-
else
|
|
36
|
-
{
|
|
37
|
-
available: %i[tomlib toml_rb],
|
|
38
|
-
default: Lutaml::Model::RuntimeCompatibility.windows? ? :toml_rb : :tomlib,
|
|
39
|
-
}
|
|
40
|
-
end,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
# Register TOML type serializers
|
|
44
|
-
require_relative "toml/type/serializers"
|
|
45
|
-
Lutaml::Toml::Type::Serializers.register_all!
|
|
46
|
-
|
|
47
|
-
# Auto-detection is now handled lazily by AdapterResolver on first use.
|
|
48
|
-
# No eager adapter selection at require time.
|
|
3
|
+
# Public TOML format entrypoint. Loads the base model and the internal
|
|
4
|
+
# implementation. The internal file does not require the model back,
|
|
5
|
+
# which keeps the require graph acyclic.
|
|
6
|
+
require_relative "model"
|
|
7
|
+
require_relative "toml/format"
|
|
@@ -180,9 +180,9 @@ module Lutaml
|
|
|
180
180
|
result
|
|
181
181
|
end
|
|
182
182
|
|
|
183
|
-
def method_missing(method_name, *args, &)
|
|
183
|
+
def method_missing(method_name, *args, &block)
|
|
184
184
|
attrs = args.first.is_a?(Hash) ? args.first : {}
|
|
185
|
-
create_and_add_element(method_name.to_s, attributes: attrs, &)
|
|
185
|
+
create_and_add_element(method_name.to_s, attributes: attrs, &block)
|
|
186
186
|
end
|
|
187
187
|
|
|
188
188
|
def respond_to_missing?(_method_name, _include_private = false)
|