lutaml-model 0.8.19 → 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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +1 -1
  3. data/.github/workflows/rake.yml +1 -0
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +133 -72
  6. data/bench/bench_common.rb +8 -5
  7. data/bench/bench_niso.rb +1 -2
  8. data/bench/gate_config.rb +4 -1
  9. data/benchmark/serialization_benchmark.rb +4 -4
  10. data/docs/_guides/value-transformations.adoc +27 -0
  11. data/lib/compat/opal/generate_boot.rb +3 -1
  12. data/lib/compat/opal/lutaml_model_boot.rb +10 -7
  13. data/lib/compat/opal/moxml_boot.rb +6 -1
  14. data/lib/lutaml/hash_format/format.rb +29 -0
  15. data/lib/lutaml/hash_format.rb +5 -27
  16. data/lib/lutaml/json/format.rb +61 -0
  17. data/lib/lutaml/json.rb +4 -59
  18. data/lib/lutaml/jsonl/format.rb +22 -0
  19. data/lib/lutaml/jsonl.rb +5 -19
  20. data/lib/lutaml/key_value/format.rb +26 -0
  21. data/lib/lutaml/key_value/transform.rb +4 -1
  22. data/lib/lutaml/key_value/transformation/value_serializer.rb +0 -2
  23. data/lib/lutaml/key_value.rb +4 -21
  24. data/lib/lutaml/model/attribute.rb +38 -4
  25. data/lib/lutaml/model/collection.rb +2 -2
  26. data/lib/lutaml/model/compiled_rule.rb +1 -1
  27. data/lib/lutaml/model/config.rb +2 -2
  28. data/lib/lutaml/model/consolidation_map.rb +2 -2
  29. data/lib/lutaml/model/consolidation_rule/dispatch_block.rb +2 -2
  30. data/lib/lutaml/model/errors.rb +2 -2
  31. data/lib/lutaml/model/global_context.rb +4 -2
  32. data/lib/lutaml/model/mapping/mapping_rule.rb +4 -1
  33. data/lib/lutaml/model/mapping/model_mapping.rb +2 -2
  34. data/lib/lutaml/model/runtime_compatibility.rb +2 -2
  35. data/lib/lutaml/model/schema/class_loader.rb +2 -1
  36. data/lib/lutaml/model/schema/decorators/definition_collection.rb +4 -4
  37. data/lib/lutaml/model/schema/definitions/namespace.rb +2 -1
  38. data/lib/lutaml/model/schema/file_writer.rb +5 -2
  39. data/lib/lutaml/model/schema/renderers/base.rb +4 -3
  40. data/lib/lutaml/model/schema/renderers/mappings.rb +9 -3
  41. data/lib/lutaml/model/schema/renderers/member_decls.rb +9 -3
  42. data/lib/lutaml/model/schema/renderers/model.rb +18 -6
  43. data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +2 -1
  44. data/lib/lutaml/model/schema/renderers.rb +2 -1
  45. data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +6 -2
  46. data/lib/lutaml/model/schema/rnc_compiler.rb +3 -1
  47. data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +35 -15
  48. data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +4 -2
  49. data/lib/lutaml/model/schema/rng_compiler.rb +10 -5
  50. data/lib/lutaml/model/schema/xml_compiler/element_order.rb +2 -1
  51. data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +4 -2
  52. data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +39 -13
  53. data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +29 -10
  54. data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +2 -2
  55. data/lib/lutaml/model/schema/xml_compiler.rb +10 -5
  56. data/lib/lutaml/model/sequence.rb +4 -4
  57. data/lib/lutaml/model/serialize/attribute_definition.rb +1 -0
  58. data/lib/lutaml/model/serialize/format_conversion.rb +2 -2
  59. data/lib/lutaml/model/serialize/initialization.rb +9 -1
  60. data/lib/lutaml/model/serialize/model_import.rb +1 -0
  61. data/lib/lutaml/model/serialize.rb +4 -3
  62. data/lib/lutaml/model/type/uninitialized_class_guard.rb +1 -1
  63. data/lib/lutaml/model/type.rb +9 -0
  64. data/lib/lutaml/model/type_registry.rb +2 -2
  65. data/lib/lutaml/model/uninitialized_class.rb +1 -1
  66. data/lib/lutaml/model/union.rb +5 -2
  67. data/lib/lutaml/model/utils.rb +2 -2
  68. data/lib/lutaml/model/value_policy.rb +27 -0
  69. data/lib/lutaml/model/version.rb +1 -1
  70. data/lib/lutaml/model.rb +17 -16
  71. data/lib/lutaml/rdf/mapping.rb +2 -2
  72. data/lib/lutaml/rdf/namespace_set.rb +2 -2
  73. data/lib/lutaml/rdf/transform.rb +2 -2
  74. data/lib/lutaml/toml/format.rb +48 -0
  75. data/lib/lutaml/toml.rb +5 -46
  76. data/lib/lutaml/xml/adapter/base_adapter.rb +2 -2
  77. data/lib/lutaml/xml/builder/base.rb +2 -2
  78. data/lib/lutaml/xml/format.rb +331 -0
  79. data/lib/lutaml/xml/mapping.rb +2 -2
  80. data/lib/lutaml/xml/mapping_rule.rb +62 -3
  81. data/lib/lutaml/xml/model_transform.rb +132 -55
  82. data/lib/lutaml/xml/namespace_collector.rb +3 -3
  83. data/lib/lutaml/xml/parse_session.rb +59 -0
  84. data/lib/lutaml/xml/parsed_namespace_set.rb +2 -2
  85. data/lib/lutaml/xml/schema/builder.rb +5 -5
  86. data/lib/lutaml/xml/schema/xsd/schema_location_mapping.rb +2 -2
  87. data/lib/lutaml/xml/schema/xsd.rb +0 -2
  88. data/lib/lutaml/xml/schema/xsd_schema.rb +2 -1
  89. data/lib/lutaml/xml/serialization/format_conversion.rb +3 -3
  90. data/lib/lutaml/xml/transformation/element_builder.rb +2 -1
  91. data/lib/lutaml/xml/transformation/ordered_applier.rb +2 -1
  92. data/lib/lutaml/xml/xml_element.rb +2 -0
  93. data/lib/lutaml/xml.rb +4 -328
  94. data/lib/lutaml/yaml/format.rb +30 -0
  95. data/lib/lutaml/yaml.rb +5 -28
  96. data/lib/lutaml/yamls/adapter/mapping.rb +2 -2
  97. data/lib/lutaml/yamls/format.rb +22 -0
  98. data/lib/lutaml/yamls.rb +5 -19
  99. data/lib/tasks/benchmark_runner.rb +2 -2
  100. data/lutaml-model.gemspec +1 -1
  101. data/spec/lutaml/model/attribute_apply_value_map_spec.rb +26 -13
  102. data/spec/lutaml/model/attribute_spec.rb +8 -3
  103. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +2 -2
  104. data/spec/lutaml/model/custom_from_empty_values_spec.rb +74 -0
  105. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +8 -8
  106. data/spec/lutaml/model/defaults_spec.rb +45 -0
  107. data/spec/lutaml/model/json_spec.rb +1 -2
  108. data/spec/lutaml/model/serialize/builder_spec.rb +6 -3
  109. data/spec/lutaml/model/store_spec.rb +3 -1
  110. data/spec/lutaml/model/transform_apply_value_map_spec.rb +6 -3
  111. data/spec/lutaml/model/union_attribute_spec.rb +4 -2
  112. data/spec/lutaml/model/value_policy_spec.rb +82 -0
  113. data/spec/lutaml/require_load_order_spec.rb +25 -0
  114. data/spec/lutaml/xml/attribute_element_namespace_disjoint_spec.rb +88 -0
  115. data/spec/lutaml/xml/attribute_namespace_disjointness_spec.rb +125 -0
  116. data/spec/lutaml/xml/out_of_namespace_parsing_spec.rb +123 -0
  117. data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +17 -6
  118. data/spec/lutaml/xml/schema/rng_compiler_spec.rb +15 -6
  119. data/spec/lutaml/xml/w3c_types_spec.rb +3 -6
  120. metadata +20 -4
@@ -121,6 +121,7 @@ module Lutaml
121
121
  @register_records[register_id][:choice_attributes].concat(
122
122
  deep_duplicate_choice_attributes(model, register_id),
123
123
  )
124
+ @merged_attributes_cache = nil
124
125
  end
125
126
  private :register_only_import_model_attributes
126
127
 
@@ -175,18 +175,19 @@ module Lutaml
175
175
  @using_default[attribute_name]
176
176
  end
177
177
 
178
- # rubocop:disable Style/ArgumentsForwarding -- anonymous * requires Ruby 3.2+, but required_ruby_version >= 3.0
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
 
@@ -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.
@@ -127,8 +127,8 @@ module Lutaml
127
127
  #
128
128
  # @example
129
129
  # registry.each { |name, klass| puts "#{name}: #{klass}" }
130
- def each(&)
131
- @types.each(&)
130
+ def each(&block)
131
+ @types.each(&block)
132
132
  end
133
133
 
134
134
  # Create a copy of this registry
@@ -48,7 +48,7 @@ module Lutaml
48
48
  "".encoding
49
49
  end
50
50
 
51
- def method_missing(method, *_args, &)
51
+ def method_missing(method, *_args)
52
52
  return false if method.end_with?("?")
53
53
 
54
54
  nil
@@ -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, "union type must be a non-empty array of types"
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? { |key| field_names.include?(key) }
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
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.8.19"
5
+ VERSION = "0.8.22"
6
6
  end
7
7
  end
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
@@ -19,8 +19,8 @@ module Lutaml
19
19
  @namespace_set = Lutaml::Rdf::NamespaceSet.new(*namespace_classes)
20
20
  end
21
21
 
22
- def subject(&)
23
- @rdf_subject = Proc.new(&) if block_given?
22
+ def subject(&block)
23
+ @rdf_subject = Proc.new(&block) if block
24
24
  end
25
25
 
26
26
  def type(value)
@@ -38,8 +38,8 @@ module Lutaml
38
38
  nil
39
39
  end
40
40
 
41
- def each(&)
42
- @by_prefix.each_value(&)
41
+ def each(&block)
42
+ @by_prefix.each_value(&block)
43
43
  end
44
44
 
45
45
  def size
@@ -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 module
4
- # Provides Lutaml::Toml namespace for TOML serialization
5
-
6
- require "lutaml/model/runtime_compatibility"
7
- require_relative "key_value"
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"
@@ -215,8 +215,8 @@ module Lutaml
215
215
 
216
216
  private
217
217
 
218
- def attribute_values(element, &)
219
- element.attributes.each_value(&)
218
+ def attribute_values(element, &block)
219
+ element.attributes.each_value(&block)
220
220
  end
221
221
 
222
222
  def schema_location_attribute?(attr)
@@ -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)