lutaml-model 0.8.17 → 0.8.19
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/downstream-performance.yml +27 -1
- data/.github/workflows/js-pr-check.yml +102 -0
- data/.github/workflows/js-sync.yml +51 -0
- data/.github/workflows/opal.yml +26 -6
- data/.github/workflows/performance.yml +32 -7
- data/.github/workflows/rake.yml +78 -32
- data/.gitmodules +6 -0
- data/.rubocop_todo.yml +8 -0
- data/Gemfile +16 -1
- data/README.adoc +267 -0
- data/Rakefile +166 -4
- data/docs/_guides/builder-dsl.adoc +127 -0
- data/docs/_guides/schema-import.adoc +42 -0
- data/docs/_guides/xml-mapping.adoc +21 -0
- data/docs/_pages/validation.adoc +65 -0
- data/lib/compat/opal/generate_boot.rb +123 -0
- data/lib/compat/opal/js_bundle_entry.rb +42 -0
- data/lib/compat/opal/lutaml_model_boot.rb +497 -0
- data/lib/compat/opal/moxml_boot.rb +68 -0
- data/lib/compat/opal/yaml_compat.rb +32 -0
- data/lib/lutaml/json.rb +1 -1
- data/lib/lutaml/key_value/transform.rb +3 -8
- data/lib/lutaml/key_value/transformation/value_serializer.rb +14 -1
- data/lib/lutaml/key_value/transformation.rb +17 -5
- data/lib/lutaml/model/adapter_resolver.rb +2 -2
- data/lib/lutaml/model/attribute.rb +88 -11
- data/lib/lutaml/model/cached_type_resolver.rb +1 -1
- data/lib/lutaml/model/error/union_schema_unsupported_error.rb +11 -0
- data/lib/lutaml/model/global_context.rb +11 -8
- data/lib/lutaml/model/instrumentation.rb +2 -2
- data/lib/lutaml/model/mapping/mapping_rule.rb +0 -6
- data/lib/lutaml/model/register.rb +2 -2
- data/lib/lutaml/model/runtime_compatibility.rb +50 -0
- data/lib/lutaml/model/schema/class_loader.rb +58 -0
- data/lib/lutaml/model/schema/compiled_output.rb +83 -0
- data/lib/lutaml/model/schema/definitions/attribute.rb +32 -0
- data/lib/lutaml/model/schema/definitions/choice.rb +20 -0
- data/lib/lutaml/model/schema/definitions/facet.rb +40 -0
- data/lib/lutaml/model/schema/definitions/group_import.rb +22 -0
- data/lib/lutaml/model/schema/definitions/member_walk.rb +33 -0
- data/lib/lutaml/model/schema/definitions/model.rb +40 -0
- data/lib/lutaml/model/schema/definitions/namespace.rb +23 -0
- data/lib/lutaml/model/schema/definitions/restricted_type.rb +30 -0
- data/lib/lutaml/model/schema/definitions/sequence.rb +19 -0
- data/lib/lutaml/model/schema/definitions/simple_content.rb +22 -0
- data/lib/lutaml/model/schema/definitions/transform_facet.rb +21 -0
- data/lib/lutaml/model/schema/definitions/type_ref.rb +20 -0
- data/lib/lutaml/model/schema/definitions/union_type.rb +28 -0
- data/lib/lutaml/model/schema/definitions/xml_root.rb +20 -0
- data/lib/lutaml/model/schema/definitions.rb +24 -0
- data/lib/lutaml/model/schema/file_writer.rb +70 -0
- data/lib/lutaml/model/schema/generator/definitions_collection.rb +16 -2
- data/lib/lutaml/model/schema/generator/property.rb +21 -4
- data/lib/lutaml/model/schema/module_nesting.rb +31 -0
- data/lib/lutaml/model/schema/namespace_naming.rb +46 -0
- data/lib/lutaml/model/schema/registry_generator.rb +115 -0
- data/lib/lutaml/model/schema/renderers/base.rb +40 -0
- data/lib/lutaml/model/schema/renderers/mappings.rb +72 -0
- data/lib/lutaml/model/schema/renderers/member_decls.rb +115 -0
- data/lib/lutaml/model/schema/renderers/model.rb +121 -0
- data/lib/lutaml/model/schema/renderers/namespace.rb +30 -0
- data/lib/lutaml/model/schema/renderers/registration.rb +64 -0
- data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +93 -0
- data/lib/lutaml/model/schema/renderers/restricted_type.rb +88 -0
- data/lib/lutaml/model/schema/renderers/union.rb +83 -0
- data/lib/lutaml/model/schema/renderers.rb +19 -0
- data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +47 -0
- data/lib/lutaml/model/schema/rnc_compiler.rb +62 -0
- data/lib/lutaml/model/schema/rng_compiler/define_classifier.rb +91 -0
- data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +392 -0
- data/lib/lutaml/model/schema/rng_compiler/member_collector.rb +30 -0
- data/lib/lutaml/model/schema/rng_compiler/rng_helpers.rb +152 -0
- data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +125 -0
- data/lib/lutaml/model/schema/rng_compiler.rb +220 -0
- data/lib/lutaml/model/schema/templates.rb +162 -0
- data/lib/lutaml/model/schema/xml_compiler/element_order.rb +41 -0
- data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +31 -107
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +191 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +216 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/simple_types.rb +153 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +167 -0
- data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +87 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +38 -517
- data/lib/lutaml/model/schema.rb +19 -0
- data/lib/lutaml/model/serialize/attribute_definition.rb +23 -11
- data/lib/lutaml/model/serialize/builder.rb +70 -2
- data/lib/lutaml/model/serialize/initialization.rb +3 -2
- data/lib/lutaml/model/serialize/value_mapping.rb +9 -57
- data/lib/lutaml/model/serialize.rb +6 -9
- data/lib/lutaml/model/services/rule_value_extractor.rb +2 -3
- data/lib/lutaml/model/services.rb +0 -2
- data/lib/lutaml/model/store.rb +2 -1
- data/lib/lutaml/model/toml.rb +1 -1
- data/lib/lutaml/model/transform.rb +17 -39
- data/lib/lutaml/model/transformation_registry.rb +1 -1
- data/lib/lutaml/model/type.rb +1 -0
- data/lib/lutaml/model/union.rb +320 -0
- data/lib/lutaml/model/utils.rb +6 -0
- data/lib/lutaml/model/validation.rb +1 -4
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +18 -0
- data/lib/lutaml/toml.rb +1 -1
- data/lib/lutaml/xml/adapter.rb +1 -1
- data/lib/lutaml/xml/adapter_loader.rb +1 -1
- data/lib/lutaml/xml/builder/base.rb +1 -1
- data/lib/lutaml/xml/builder.rb +1 -1
- data/lib/lutaml/xml/error/schema_validation_error.rb +25 -0
- data/lib/lutaml/xml/model_transform.rb +5 -1
- data/lib/lutaml/xml/schema/relaxng_schema.rb +5 -0
- data/lib/lutaml/xml/schema/xsd_schema.rb +4 -0
- data/lib/lutaml/xml/serialization/format_conversion.rb +48 -0
- data/lib/lutaml/xml/serialization/instance_methods.rb +13 -0
- data/lib/lutaml/xml/transformation/element_builder.rb +21 -8
- data/lib/lutaml/xml/transformation/ordered_applier.rb +104 -4
- data/lib/lutaml/xml/xsd_validator.rb +66 -0
- data/lib/lutaml/xml.rb +53 -12
- data/lutaml-model.gemspec +4 -2
- data/spec/fixtures/xml/schema/rnc/address_book.rnc +15 -0
- data/spec/fixtures/xml/schema/rnc/book_features.rnc +18 -0
- data/spec/fixtures/xml/schema/rnc/includes/book.rnc +6 -0
- data/spec/fixtures/xml/schema/rnc/includes/circular_a.rnc +8 -0
- data/spec/fixtures/xml/schema/rnc/includes/circular_b.rnc +8 -0
- data/spec/fixtures/xml/schema/rnc/includes/library.rnc +8 -0
- data/spec/fixtures/xml/schema/rng/address_book.rng +16 -0
- data/spec/fixtures/xml/schema/rng/book.rng +27 -0
- data/spec/fixtures/xml/schema/rng/fixed_value.rng +11 -0
- data/spec/fixtures/xml/schema/rng/fragment_in_choice.rng +30 -0
- data/spec/fixtures/xml/schema/rng/full_name.rng +39 -0
- data/spec/fixtures/xml/schema/rng/integer_range.rng +32 -0
- data/spec/fixtures/xml/schema/rng/list_type.rng +12 -0
- data/spec/fixtures/xml/schema/rng/namespaced.rng +9 -0
- data/spec/fixtures/xml/schema/rng/paragraph.rng +13 -0
- data/spec/fixtures/xml/schema/rng/person.rng +23 -0
- data/spec/fixtures/xml/schema/rng/union.rng +14 -0
- data/spec/fixtures/xml/validate_xml_with_address.xsd +13 -0
- data/spec/fixtures/xml/validate_xml_with_contact.xsd +11 -0
- data/spec/fixtures/xml/validate_xml_with_person.xsd +11 -0
- data/spec/fixtures/xml/validate_xml_with_person_strict.xsd +17 -0
- data/spec/fixtures/xml/validate_xml_with_product.xsd +37 -0
- data/spec/lutaml/model/attribute_apply_value_map_spec.rb +134 -0
- data/spec/lutaml/model/attribute_spec.rb +169 -12
- data/spec/lutaml/model/boot_manifest_spec.rb +30 -0
- data/spec/lutaml/model/collection_spec.rb +18 -0
- data/spec/lutaml/model/mixed_content_spec.rb +171 -0
- data/spec/lutaml/model/opal_smoke_spec.rb +67 -2
- data/spec/lutaml/model/ordered_content_spec.rb +41 -0
- data/spec/lutaml/model/raw_element_spec.rb +125 -0
- data/spec/lutaml/model/rule_value_extractor_spec.rb +7 -14
- data/spec/lutaml/model/runtime_compatibility_spec.rb +25 -0
- data/spec/lutaml/model/schema/renderers/model_spec.rb +149 -0
- data/spec/lutaml/model/schema/renderers/namespace_spec.rb +36 -0
- data/spec/lutaml/model/schema/renderers/restricted_type_spec.rb +75 -0
- data/spec/lutaml/model/schema/renderers/union_spec.rb +58 -0
- data/spec/lutaml/model/serialize/builder_spec.rb +358 -0
- data/spec/lutaml/model/transform_apply_value_map_spec.rb +64 -0
- data/spec/lutaml/model/union_attribute_spec.rb +658 -0
- data/spec/lutaml/model/union_spec.rb +257 -0
- data/spec/lutaml/model/utils_spec.rb +14 -0
- data/spec/lutaml/xml/opal_xml_spec.rb +22 -20
- data/spec/lutaml/xml/schema/compiler_spec.rb +21 -4
- data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +404 -0
- data/spec/lutaml/xml/schema/rng_compiler_spec.rb +893 -0
- data/spec/lutaml/xml/validate_xml_with_constructs_spec.rb +222 -0
- data/spec/lutaml/xml/validate_xml_with_spec.rb +186 -0
- data/spec/spec_helper.rb +28 -11
- data/spec/support/opal.rb +5 -2
- metadata +99 -31
- data/lib/lutaml/model/schema/xml_compiler/attribute.rb +0 -106
- data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +0 -47
- data/lib/lutaml/model/schema/xml_compiler/choice.rb +0 -67
- data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +0 -27
- data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +0 -34
- data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +0 -173
- data/lib/lutaml/model/schema/xml_compiler/element.rb +0 -121
- data/lib/lutaml/model/schema/xml_compiler/group.rb +0 -113
- data/lib/lutaml/model/schema/xml_compiler/restriction.rb +0 -104
- data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -52
- data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +0 -40
- data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +0 -265
- data/lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb +0 -110
- data/lib/lutaml/model/services/default_value_resolver.rb +0 -60
- data/spec/lutaml/model/services/default_value_resolver_spec.rb +0 -162
- data/spec/lutaml/xml/schema/compiler/attribute_group_spec.rb +0 -71
- data/spec/lutaml/xml/schema/compiler/attribute_spec.rb +0 -67
- data/spec/lutaml/xml/schema/compiler/choice_spec.rb +0 -74
- data/spec/lutaml/xml/schema/compiler/complex_content_restriction_spec.rb +0 -60
- data/spec/lutaml/xml/schema/compiler/complex_content_spec.rb +0 -41
- data/spec/lutaml/xml/schema/compiler/complex_type_spec.rb +0 -202
- data/spec/lutaml/xml/schema/compiler/element_spec.rb +0 -68
- data/spec/lutaml/xml/schema/compiler/group_spec.rb +0 -90
- data/spec/lutaml/xml/schema/compiler/restriction_spec.rb +0 -77
- data/spec/lutaml/xml/schema/compiler/sequence_spec.rb +0 -65
- data/spec/lutaml/xml/schema/compiler/simple_content_spec.rb +0 -61
- data/spec/lutaml/xml/schema/compiler/simple_type_spec.rb +0 -184
|
@@ -108,31 +108,34 @@ 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
|
|
132
128
|
|
|
133
129
|
enum_shorthand_names = instance_variable_get(:@__enum_shorthand_names__) || Set.new
|
|
134
|
-
|
|
135
|
-
|
|
130
|
+
# Opal's Module#method_defined? only accepts 1 arg (MRI accepts
|
|
131
|
+
# the optional `inherit` flag). Skip the flag there — the
|
|
132
|
+
# difference only matters for inherited-method filtering.
|
|
133
|
+
setter_defined = if Lutaml::Model.opal?
|
|
134
|
+
method_defined?(:"#{name}=")
|
|
135
|
+
else
|
|
136
|
+
method_defined?(:"#{name}=", false)
|
|
137
|
+
end
|
|
138
|
+
unless setter_defined && !enum_shorthand_names.include?(name.to_s)
|
|
136
139
|
if attr.collection?
|
|
137
140
|
define_method(:"#{name}=") do |value|
|
|
138
141
|
value_set_for(name)
|
|
@@ -149,12 +152,16 @@ module Lutaml
|
|
|
149
152
|
else
|
|
150
153
|
instance_variable_set(:"@#{name}", value)
|
|
151
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)
|
|
152
158
|
end
|
|
153
159
|
else
|
|
154
160
|
define_method(:"#{name}=") do |value|
|
|
155
161
|
value_set_for(name)
|
|
156
162
|
value = attr.cast_value(value, lutaml_register)
|
|
157
163
|
instance_variable_set(:"@#{name}", value)
|
|
164
|
+
record_mutation(name, value)
|
|
158
165
|
end
|
|
159
166
|
end
|
|
160
167
|
end
|
|
@@ -169,6 +176,11 @@ module Lutaml
|
|
|
169
176
|
def attribute(name, type, options = {})
|
|
170
177
|
type, options = process_type_hash(type, options) if type.is_a?(::Hash)
|
|
171
178
|
|
|
179
|
+
if type.is_a?(::Array)
|
|
180
|
+
options = options.merge(union_member_types: type)
|
|
181
|
+
type = Lutaml::Model::Type::Union
|
|
182
|
+
end
|
|
183
|
+
|
|
172
184
|
# Handle direct method option in options hash
|
|
173
185
|
if options[:method]
|
|
174
186
|
options[:method_name] = options.delete(:method)
|
|
@@ -39,8 +39,10 @@ module Lutaml
|
|
|
39
39
|
|
|
40
40
|
return self unless block
|
|
41
41
|
|
|
42
|
-
# Enable order tracking for mixed_content models
|
|
43
|
-
|
|
42
|
+
# Enable order tracking for ordered and mixed_content models.
|
|
43
|
+
# `ordered?` is true for both `ordered` and `mixed_content`
|
|
44
|
+
# (mixed_content sets @ordered = true in addition to @mixed_content).
|
|
45
|
+
@__order_tracking__ = ordered?
|
|
44
46
|
|
|
45
47
|
# Evaluate the block - use instance_eval for no-receiver style
|
|
46
48
|
# The block's first parameter determines the style:
|
|
@@ -62,6 +64,72 @@ module Lutaml
|
|
|
62
64
|
mapping&.mixed_content? || false
|
|
63
65
|
end
|
|
64
66
|
|
|
67
|
+
# Check if this model has ordered or mixed_content enabled.
|
|
68
|
+
# Both set @ordered = true on the XML mapping; mixed_content
|
|
69
|
+
# additionally sets @mixed_content = true. Order tracking is
|
|
70
|
+
# needed for either mode so the builder can emit elements in
|
|
71
|
+
# setter-call order rather than declaration order.
|
|
72
|
+
# @return [Boolean]
|
|
73
|
+
def ordered?
|
|
74
|
+
mapping = self.class.mappings_for(:xml, lutaml_register)
|
|
75
|
+
mapping&.ordered? || false
|
|
76
|
+
end
|
|
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
|
+
|
|
65
133
|
private
|
|
66
134
|
|
|
67
135
|
# Intercept method calls to track order for mixed_content
|
|
@@ -370,7 +370,6 @@ module Lutaml
|
|
|
370
370
|
instance_variable_get(:"@#{name}")
|
|
371
371
|
else
|
|
372
372
|
public_send(:"#{name}=", args.first)
|
|
373
|
-
track_order(name, args.first, nil) if @__order_tracking__
|
|
374
373
|
args.first
|
|
375
374
|
end
|
|
376
375
|
end
|
|
@@ -380,6 +379,7 @@ module Lutaml
|
|
|
380
379
|
reg_attr = resolve_register_attr(name)
|
|
381
380
|
value = reg_attr.cast_value(value, lutaml_register)
|
|
382
381
|
instance_variable_set(:"@#{name}", value)
|
|
382
|
+
record_mutation(name, value)
|
|
383
383
|
end
|
|
384
384
|
end
|
|
385
385
|
|
|
@@ -395,7 +395,7 @@ module Lutaml
|
|
|
395
395
|
current = [] if current.equal?(LAZY_EMPTY_COLLECTION)
|
|
396
396
|
new_value = current.is_a?(Array) ? current + [value] : value
|
|
397
397
|
instance_variable_set(:"@#{name}", new_value)
|
|
398
|
-
|
|
398
|
+
record_mutation(name, value)
|
|
399
399
|
value
|
|
400
400
|
end
|
|
401
401
|
end
|
|
@@ -411,6 +411,7 @@ module Lutaml
|
|
|
411
411
|
else
|
|
412
412
|
instance_variable_set(:"@#{name}", value)
|
|
413
413
|
end
|
|
414
|
+
record_mutation_collection(name, value)
|
|
414
415
|
end
|
|
415
416
|
end
|
|
416
417
|
|
|
@@ -71,71 +71,19 @@ module Lutaml
|
|
|
71
71
|
propagated.merge(register: instance.lutaml_register))
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
-
# Apply a value map to transform a value
|
|
74
|
+
# Apply a value map to transform a value.
|
|
75
75
|
#
|
|
76
|
-
#
|
|
76
|
+
# Delegates to Attribute#apply_value_map. See attribute.rb for the
|
|
77
|
+
# canonical implementation.
|
|
77
78
|
#
|
|
78
79
|
# @param value [Object] The value to transform
|
|
79
80
|
# @param value_map [Hash] The value map configuration
|
|
80
81
|
# @param attr [Attribute] The attribute definition
|
|
81
82
|
# @return [Object] The transformed value
|
|
82
83
|
def apply_value_map(value, value_map, attr)
|
|
83
|
-
|
|
84
|
-
value_for_option(value_map[:nil], attr)
|
|
85
|
-
elsif Utils.empty?(value)
|
|
86
|
-
# Check for new boolean value_map format (from: { empty: true/false })
|
|
87
|
-
# Only use new format if the value is explicitly boolean (TrueClass or FalseClass)
|
|
88
|
-
if value_map[:from] && (value_map[:from][:empty].is_a?(TrueClass) || value_map[:from][:empty].is_a?(FalseClass))
|
|
89
|
-
return value_map[:from][:empty]
|
|
90
|
-
end
|
|
91
|
-
# Check for direct boolean format (rule.value_map(:from) returns { empty: true })
|
|
92
|
-
# Only return directly if it's a boolean value (TrueClass/FalseClass), not a symbol
|
|
93
|
-
if value_map[:empty].is_a?(TrueClass) || value_map[:empty].is_a?(FalseClass)
|
|
94
|
-
return value_map[:empty]
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Fall back to legacy value_map format
|
|
98
|
-
value_for_option(value_map[:empty], attr, value)
|
|
99
|
-
elsif Utils.uninitialized?(value)
|
|
100
|
-
# Check for new boolean value_map format (from: { omitted: true/false })
|
|
101
|
-
# Only use new format if the value is explicitly boolean (TrueClass or FalseClass)
|
|
102
|
-
if value_map[:from] && (value_map[:from][:omitted].is_a?(TrueClass) || value_map[:from][:omitted].is_a?(FalseClass))
|
|
103
|
-
return value_map[:from][:omitted]
|
|
104
|
-
end
|
|
105
|
-
# Check for direct boolean format (rule.value_map(:from) returns { omitted: false })
|
|
106
|
-
# Only return directly if it's a boolean value (TrueClass/FalseClass), not a symbol
|
|
107
|
-
if value_map[:omitted].is_a?(TrueClass) || value_map[:omitted].is_a?(FalseClass)
|
|
108
|
-
return value_map[:omitted]
|
|
109
|
-
end
|
|
84
|
+
return value unless attr
|
|
110
85
|
|
|
111
|
-
|
|
112
|
-
value_for_option(value_map[:omitted], attr)
|
|
113
|
-
else
|
|
114
|
-
value
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Get the value for a specific option in a value map
|
|
119
|
-
#
|
|
120
|
-
# @param option [Symbol] The option (:nil, :empty, etc.)
|
|
121
|
-
# @param attr [Attribute] The attribute definition
|
|
122
|
-
# @param empty_value [Object] The empty value to use (optional)
|
|
123
|
-
# @return [Object, nil, UninitializedClass] The appropriate value
|
|
124
|
-
def value_for_option(option, attr, empty_value = nil)
|
|
125
|
-
return nil if option == :nil
|
|
126
|
-
return empty_value || empty_object(attr) if option == :empty
|
|
127
|
-
|
|
128
|
-
Lutaml::Model::UninitializedClass.instance
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Get an empty object for an attribute
|
|
132
|
-
#
|
|
133
|
-
# @param attr [Attribute] The attribute definition
|
|
134
|
-
# @return [String, Array] Empty string or collection
|
|
135
|
-
def empty_object(attr)
|
|
136
|
-
return attr.build_collection if attr.collection?
|
|
137
|
-
|
|
138
|
-
""
|
|
86
|
+
attr.apply_value_map(value, value_map)
|
|
139
87
|
end
|
|
140
88
|
|
|
141
89
|
# Ensure a value is UTF-8 encoded
|
|
@@ -145,6 +93,10 @@ module Lutaml
|
|
|
145
93
|
def ensure_utf8(value)
|
|
146
94
|
case value
|
|
147
95
|
when String
|
|
96
|
+
# Opal strings are JS strings with no per-string encoding;
|
|
97
|
+
# String#encode there doesn't accept MRI's keyword options.
|
|
98
|
+
return value if Lutaml::Model.opal?
|
|
99
|
+
|
|
148
100
|
value.encode("UTF-8", invalid: :replace, undef: :replace,
|
|
149
101
|
replace: "")
|
|
150
102
|
when Array
|
|
@@ -202,9 +202,7 @@ module Lutaml
|
|
|
202
202
|
def validate_attribute!(attr_name)
|
|
203
203
|
attr = self.class.attributes[attr_name]
|
|
204
204
|
value = instance_variable_get(:"@#{attr_name}")
|
|
205
|
-
|
|
206
|
-
self)
|
|
207
|
-
attr.validate_value!(value, lutaml_register, resolver)
|
|
205
|
+
attr.validate_value!(value, lutaml_register, instance_object: self)
|
|
208
206
|
end
|
|
209
207
|
|
|
210
208
|
def key_exist?(hash, key)
|
|
@@ -301,12 +299,11 @@ module Lutaml
|
|
|
301
299
|
return attr_value(attrs, name, attr)
|
|
302
300
|
end
|
|
303
301
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
end
|
|
302
|
+
resolved = attr.default_value(lutaml_register, self)
|
|
303
|
+
return Lutaml::Model::UninitializedClass.instance if Lutaml::Model::Utils.uninitialized?(resolved)
|
|
304
|
+
|
|
305
|
+
using_default_for(name)
|
|
306
|
+
attr.cast_value(resolved, lutaml_register)
|
|
310
307
|
end
|
|
311
308
|
|
|
312
309
|
def resolve_reference_key(ref)
|
|
@@ -50,9 +50,8 @@ instance_object)
|
|
|
50
50
|
return fetch_value(name) if Utils.string_or_symbol_key?(doc, name)
|
|
51
51
|
|
|
52
52
|
if attr
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return resolver.default_value if resolver.default_set?
|
|
53
|
+
resolved = attr.default_value(register, instance_object)
|
|
54
|
+
return resolved unless Utils.uninitialized?(resolved)
|
|
56
55
|
end
|
|
57
56
|
|
|
58
57
|
uninitialized_value
|
data/lib/lutaml/model/store.rb
CHANGED
data/lib/lutaml/model/toml.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Lutaml
|
|
|
16
16
|
Transform = ::Lutaml::Toml::Adapter::Transform
|
|
17
17
|
|
|
18
18
|
def self.detect_toml_adapter
|
|
19
|
-
return nil if Lutaml::Model
|
|
19
|
+
return nil if Lutaml::Model.opal?
|
|
20
20
|
|
|
21
21
|
# Skip tomlib on Windows entirely due to segfault issues
|
|
22
22
|
if Lutaml::Model::RuntimeCompatibility.windows?
|
|
@@ -85,48 +85,26 @@ module Lutaml
|
|
|
85
85
|
protected
|
|
86
86
|
|
|
87
87
|
def apply_value_map(value, value_map, attr)
|
|
88
|
-
if
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
# Check if attribute is a Boolean type
|
|
106
|
-
attr_type = attr&.type || attr&.unresolved_type
|
|
107
|
-
if attr_type == Lutaml::Model::Type::Boolean
|
|
108
|
-
return value_map[:omitted]
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
value_for_option(value_map[:omitted], attr)
|
|
112
|
-
else
|
|
113
|
-
value
|
|
88
|
+
return attr.apply_value_map(value, value_map) if attr
|
|
89
|
+
|
|
90
|
+
# Attribute-less dispatch for custom-method-only rules. Mirrors
|
|
91
|
+
# Attribute#apply_value_map for the options that need no attribute
|
|
92
|
+
# info: :nil -> nil and :omitted -> uninitialized. A typed :empty needs
|
|
93
|
+
# an Attribute (a crash case pre-consolidation), so the value passes
|
|
94
|
+
# through unchanged.
|
|
95
|
+
key = if value.nil? then :nil
|
|
96
|
+
elsif Utils.empty?(value) then :empty
|
|
97
|
+
elsif Utils.uninitialized?(value) then :omitted
|
|
98
|
+
end
|
|
99
|
+
return value unless key
|
|
100
|
+
|
|
101
|
+
case value_map[key]
|
|
102
|
+
when :nil then nil
|
|
103
|
+
when :empty then value
|
|
104
|
+
else Lutaml::Model::UninitializedClass.instance
|
|
114
105
|
end
|
|
115
106
|
end
|
|
116
107
|
|
|
117
|
-
def value_for_option(option, attr, empty_value = nil)
|
|
118
|
-
return nil if option == :nil
|
|
119
|
-
return empty_value || empty_object(attr) if option == :empty
|
|
120
|
-
|
|
121
|
-
Lutaml::Model::UninitializedClass.instance
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def empty_object(attr)
|
|
125
|
-
return [] if attr.collection?
|
|
126
|
-
|
|
127
|
-
""
|
|
128
|
-
end
|
|
129
|
-
|
|
130
108
|
def mappings_for(format, register = nil)
|
|
131
109
|
context.mappings_for(format, register)
|
|
132
110
|
end
|
data/lib/lutaml/model/type.rb
CHANGED
|
@@ -16,6 +16,7 @@ module Lutaml
|
|
|
16
16
|
autoload :Boolean, "#{__dir__}/type/boolean"
|
|
17
17
|
autoload :Decimal, "#{__dir__}/type/decimal"
|
|
18
18
|
autoload :Reference, "#{__dir__}/type/reference"
|
|
19
|
+
autoload :Union, "#{__dir__}/union"
|
|
19
20
|
autoload :Hash, "#{__dir__}/type/hash"
|
|
20
21
|
autoload :Symbol, "#{__dir__}/type/symbol"
|
|
21
22
|
autoload :Duration, "#{__dir__}/type/duration"
|