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
data/lib/lutaml/xml.rb CHANGED
@@ -1,331 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Lutaml::Xml - XML serialization module for LutaML
4
- #
5
- # This module provides XML-specific serialization functionality.
6
- # It requires lutaml/model to be loaded first for base classes.
7
-
8
- # Ensure base model is loaded
3
+ # Public XML 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.
9
6
  require_relative "model"
10
-
11
- # XML requires moxml for parsing
12
- require "moxml"
13
-
14
- # Under Opal, moxml's autoloads don't fire (Opal ignores autoload).
15
- # Pull in the eager-load boot that ships at lib/compat/opal/moxml_boot.rb
16
- # so Moxml::Adapter::{Oga,Rexml} etc. are actually defined. Without
17
- # this, `Config.adapter = :oga` later fails with
18
- # `NameError: uninitialized constant Moxml::Adapter::Oga`.
19
- if Lutaml::Model.opal?
20
- require "moxml_boot"
21
- end
22
-
23
- module Lutaml
24
- module Xml
25
- # Error module for XML-specific errors
26
- module Error
27
- autoload :XmlError, "#{__dir__}/xml/error/xml_error"
28
- autoload :InvalidNamespaceError,
29
- "#{__dir__}/xml/error/invalid_namespace_error"
30
- autoload :InvalidXsdTypeError,
31
- "#{__dir__}/xml/error/invalid_xsd_type_error"
32
- autoload :XmlConfigurationError,
33
- "#{__dir__}/xml/error/xml_configuration_error"
34
- autoload :NamespaceMismatchError,
35
- "#{__dir__}/xml/error/namespace_mismatch_error"
36
- autoload :SchemaValidationError,
37
- "#{__dir__}/xml/error/schema_validation_error"
38
- end
39
-
40
- # XML Configuration modules
41
- autoload :Configurable, "#{__dir__}/xml/configurable"
42
- autoload :NamespaceTypeResolver, "#{__dir__}/xml/namespace_type_resolver"
43
-
44
- # XML Type modules
45
- module Type
46
- autoload :ValueXmlMapping, "#{__dir__}/xml/type/value_xml_mapping"
47
- autoload :Configurable, "#{__dir__}/xml/type/configurable"
48
- end
49
-
50
- # XML Serialization modules
51
- autoload :Serialization, "#{__dir__}/xml/serialization"
52
-
53
- # XML Schema modules
54
- autoload :Schema, "#{__dir__}/xml/schema"
55
-
56
- # Detect available XML adapter.
57
- # Delegates to moxml, which is the authority on XML adapter
58
- # availability and platform constraints.
59
- #
60
- # @return [Symbol] adapter type name
61
- def self.detect_xml_adapter
62
- Moxml::Config.runtime_default_adapter
63
- end
64
-
65
- # Get the current XML adapter
66
- #
67
- # Provides unified access to the configured XML adapter.
68
- # This is a convenience method for consistent adapter access across
69
- # Model and Type classes.
70
- #
71
- # @return [Object] the configured XML adapter instance
72
- #
73
- # @example Using the adapter
74
- # adapter = Lutaml::Xml.adapter
75
- # doc = adapter.parse(xml_string)
76
- #
77
- def self.adapter
78
- Lutaml::Model::Config.adapter_for(:xml)
79
- end
80
-
81
- # Get the current XML adapter type
82
- #
83
- # @return [Symbol] the configured XML adapter type (:nokogiri, :ox, etc.)
84
- def self.adapter_type
85
- Lutaml::Model::Config.xml_adapter_type
86
- end
87
-
88
- # Autoload core classes
89
- autoload :Location, "#{__dir__}/xml/schema_location"
90
- autoload :SchemaLocation, "#{__dir__}/xml/schema_location"
91
- autoload :Namespace, "#{__dir__}/xml/namespace"
92
- autoload :Mapping, "#{__dir__}/xml/mapping"
93
- autoload :MappingRule, "#{__dir__}/xml/mapping_rule"
94
- autoload :Listener, "#{__dir__}/xml/listener"
95
- autoload :Document, "#{__dir__}/xml/document"
96
- autoload :Transformation, "#{__dir__}/xml/transformation"
97
- autoload :CustomMethodWrapper,
98
- "#{__dir__}/xml/transformation/custom_method_wrapper"
99
- autoload :Transform, "#{__dir__}/xml/transform"
100
- autoload :XsdValidator, "#{__dir__}/xml/xsd_validator"
101
- autoload :Adapter, "#{__dir__}/xml/adapter"
102
- autoload :XmlElement, "#{__dir__}/xml/xml_element"
103
- autoload :AdapterElement, "#{__dir__}/xml/adapter_element"
104
- autoload :XmlAttribute, "#{__dir__}/xml/xml_attribute"
105
- autoload :Decisions, "#{__dir__}/xml/decisions"
106
- autoload :DeclarationPlan, "#{__dir__}/xml/declaration_plan"
107
- autoload :DeclarationPlanner, "#{__dir__}/xml/declaration_planner"
108
- autoload :NamespaceCollector, "#{__dir__}/xml/namespace_collector"
109
- autoload :NamespaceResolver, "#{__dir__}/xml/namespace_resolver"
110
- autoload :NamespaceDeclaration, "#{__dir__}/xml/namespace_declaration"
111
- autoload :NamespaceClassRegistry, "#{__dir__}/xml/namespace_class_registry"
112
- autoload :BlankNamespace, "#{__dir__}/xml/blank_namespace"
113
- autoload :EncodingNormalizer, "#{__dir__}/xml/encoding_normalizer"
114
- autoload :W3c, "#{__dir__}/xml/w3c"
115
- autoload :NamespaceResolutionStrategy,
116
- "#{__dir__}/xml/namespace_resolution_strategy"
117
- autoload :NamespaceInheritanceStrategy,
118
- "#{__dir__}/xml/namespace_inheritance_strategy"
119
- autoload :QualifiedInheritanceStrategy,
120
- "#{__dir__}/xml/qualified_inheritance_strategy"
121
- autoload :UnqualifiedInheritanceStrategy,
122
- "#{__dir__}/xml/unqualified_inheritance_strategy"
123
- autoload :DataModel, "#{__dir__}/xml/data_model"
124
- autoload :TransformationBuilder, "#{__dir__}/xml/transformation_builder"
125
- autoload :AdapterLoader, "#{__dir__}/xml/adapter_loader"
126
- autoload :Element, "#{__dir__}/xml/element"
127
- autoload :ModelTransform, "#{__dir__}/xml/model_transform"
128
- autoload :TypeNamespaceResolver, "#{__dir__}/xml/type_namespace_resolver"
129
- autoload :NamespaceNeeds, "#{__dir__}/xml/namespace_needs"
130
- autoload :NamespaceUsage, "#{__dir__}/xml/namespace_usage"
131
- autoload :NamespaceDeclarationData,
132
- "#{__dir__}/xml/namespace_declaration_data"
133
- autoload :ParsedNamespaceDeclaration,
134
- "#{__dir__}/xml/parsed_namespace_declaration"
135
- autoload :ParsedNamespaceSet, "#{__dir__}/xml/parsed_namespace_set"
136
- autoload :DeclarationHandler, "#{__dir__}/xml/declaration_handler"
137
- autoload :PolymorphicValueHandler,
138
- "#{__dir__}/xml/polymorphic_value_handler"
139
- autoload :AttributeNamespaceResolver,
140
- "#{__dir__}/xml/attribute_namespace_resolver"
141
- autoload :BlankNamespaceHandler, "#{__dir__}/xml/blank_namespace_handler"
142
- autoload :DeclarationPlanQuery, "#{__dir__}/xml/declaration_plan_query"
143
- autoload :DocTypeExtractor, "#{__dir__}/xml/doctype_extractor"
144
- autoload :NamespaceDeclarationBuilder,
145
- "#{__dir__}/xml/namespace_declaration_builder"
146
- autoload :ElementPrefixResolver, "#{__dir__}/xml/element_prefix_resolver"
147
- autoload :FormatChooser, "#{__dir__}/xml/format_chooser"
148
- autoload :HoistingAlgorithm, "#{__dir__}/xml/hoisting_algorithm"
149
- autoload :NamespaceInheritanceResolver,
150
- "#{__dir__}/xml/namespace_inheritance_resolver"
151
- autoload :NamespaceScopeConfig, "#{__dir__}/xml/namespace_scope_config"
152
- autoload :Builder, "#{__dir__}/xml/builder"
153
- autoload :TypeNamespace, "#{__dir__}/xml/type_namespace"
154
- autoload :TransformationSupport, "#{__dir__}/xml/transformation_support"
155
- autoload :SharedDsl, "#{__dir__}/xml/shared_dsl"
156
-
157
- autoload :Oga, "#{__dir__}/xml/oga"
158
- autoload :Rexml, "#{__dir__}/xml/rexml"
159
- Lutaml::Model::RuntimeCompatibility.autoload_native(
160
- self,
161
- NokogiriElement: "#{__dir__}/xml/nokogiri/element",
162
- Nokogiri: "#{__dir__}/xml/nokogiri",
163
- Ox: "#{__dir__}/xml/ox",
164
- )
165
- end
166
- end
167
-
168
- # Register XML format with the model's format registry
169
- Lutaml::Model::FormatRegistry.register(
170
- :xml,
171
- mapping_class: Lutaml::Xml::Mapping,
172
- adapter_class: nil,
173
- transformer: Lutaml::Xml::Transform,
174
- adapter_loader: Lutaml::Xml::AdapterLoader,
175
- castable_type: Lutaml::Xml::XmlElement,
176
- key_value: false,
177
- error_types: %w[
178
- Moxml::ParseError
179
- Nokogiri::XML::SyntaxError
180
- Ox::ParseError
181
- REXML::ParseException
182
- ],
183
- adapter_options: if Lutaml::Model.opal?
184
- # Both Oga (vendored opal-oga fork, pure-Ruby lexer)
185
- # and REXML (bundled stdlib gem, pure Ruby) work
186
- # under Opal. Oga is the default because moxml's CI
187
- # verifies it most thoroughly; REXML is selectable
188
- # for callers that prefer a stdlib-only stack.
189
- {
190
- available: %i[oga rexml],
191
- default: :oga,
192
- }
193
- else
194
- {
195
- available: %i[nokogiri ox oga rexml],
196
- default: :nokogiri,
197
- }
198
- end,
199
- )
200
-
201
- # Register XML transformation builder
202
- Lutaml::Model::TransformationRegistry.register_builder(
203
- :xml, Lutaml::Xml::TransformationBuilder
204
- )
205
-
206
- # Extend Type::Value with XML configuration (namespace, xsd_type, xml block)
207
- Lutaml::Model::Type::Value.include(Lutaml::Xml::Type::Configurable)
208
-
209
- # Prepend XML-specific serialization hooks into Serialize::ClassMethods
210
- # Uses prepend so XML's hook overrides (pre_deserialize_hook, validate_document, etc.)
211
- # take priority over core's no-op defaults.
212
- Lutaml::Model::Serialize::ClassMethods.prepend(
213
- Lutaml::Xml::Serialization::FormatConversion,
214
- )
215
-
216
- # Prepend XML-specific ModelImport overrides (root?, ensure_format_mapping_imports!, etc.)
217
- Lutaml::Model::Serialize::ModelImport.prepend(
218
- Lutaml::Xml::Serialization::ModelImportExt,
219
- )
220
-
221
- # Prepend XML-specific instance methods (import_declaration_plan, validate_root_mapping!, etc.)
222
- Lutaml::Model::Serialize.prepend(
223
- Lutaml::Xml::Serialization::InstanceMethods,
224
- )
225
-
226
- # Opal does not propagate methods prepended into an already-included module
227
- # to classes that included it before the prepend. Serializable includes
228
- # Serialize during model boot, so make the XML instance API available on the
229
- # concrete base class as well.
230
- #
231
- # Anonymous model classes (Class.new { include Serialize }) extend
232
- # Serialize::ClassMethods rather than inheriting from Serializable, so the
233
- # ModelImportExt override (root?) must land on ClassMethods too. The other
234
- # two (FormatConversion, InstanceMethods) are already prepended unconditionally
235
- # above, so Opal's no-double-prepend rule means we don't repeat them here.
236
- if Lutaml::Model.opal?
237
- Lutaml::Model::Serializable.singleton_class.prepend(
238
- Lutaml::Xml::Serialization::ModelImportExt,
239
- )
240
-
241
- Lutaml::Model::Serializable.prepend(
242
- Lutaml::Xml::Serialization::InstanceMethods,
243
- )
244
-
245
- Lutaml::Model::Serialize::ClassMethods.prepend(
246
- Lutaml::Xml::Serialization::ModelImportExt,
247
- )
248
- end
249
-
250
- # Register XML-specific attribute override warning names
251
- Lutaml::Model::Attribute.format_specific_warn_names.push(:element_order,
252
- :schema_location, :encoding, :doctype, :ordered?, :mixed?)
253
-
254
- # Prepend XML-specific Collection overrides (unwrapped collection handling for XML)
255
- require_relative "xml/serialization/collection_ext"
256
- Lutaml::Model::Collection.singleton_class.prepend(
257
- Lutaml::Xml::Serialization::CollectionExt,
258
- )
259
-
260
- # Register XML type serializers
261
- require_relative "xml/type/serializers"
262
- Lutaml::Xml::Type::Serializers.register_all!
263
-
264
- # Register XML schema methods
265
- Lutaml::Model::Schema.register_method(:to_xsd) do |klass, options = {}|
266
- if Lutaml::Model.opal?
267
- raise NotImplementedError,
268
- "XSD schema generation is not available under Opal."
269
- end
270
-
271
- Lutaml::Xml::Schema::XsdSchema.generate(klass, options)
272
- end
273
-
274
- Lutaml::Model::Schema.register_method(:to_relaxng) do |klass, options = {}|
275
- if Lutaml::Model.opal?
276
- raise NotImplementedError,
277
- "RELAX NG schema generation requires Nokogiri, " \
278
- "which is not available under Opal."
279
- end
280
-
281
- Lutaml::Xml::Schema::RelaxngSchema.generate(klass, options)
282
- end
283
-
284
- Lutaml::Model::Schema.register_method(:from_xml) do |xml, options = {}|
285
- if Lutaml::Model.opal?
286
- raise NotImplementedError,
287
- "XML schema compilation is not available under Opal."
288
- end
289
-
290
- Lutaml::Model::Schema::XmlCompiler.to_models(xml, options)
291
- end
292
-
293
- Lutaml::Model::Schema.register_method(:from_relaxng) do |rng, options = {}|
294
- if Lutaml::Model::RuntimeCompatibility.opal?
295
- raise NotImplementedError,
296
- "RELAX NG schema compilation is not available under Opal."
297
- end
298
-
299
- Lutaml::Model::Schema::RngCompiler.to_models(rng, options)
300
- end
301
-
302
- Lutaml::Model::Schema.register_method(:from_rnc) do |rnc, options = {}|
303
- if Lutaml::Model::RuntimeCompatibility.opal?
304
- raise NotImplementedError,
305
- "RNC schema compilation is not available under Opal."
306
- end
307
-
308
- Lutaml::Model::Schema::RncCompiler.to_models(rnc, options)
309
- end
310
-
311
- # Register XML namespace registry with GlobalContext
312
- Lutaml::Model::GlobalContext.register_format_registry(
313
- :xml, Lutaml::Xml::NamespaceClassRegistry.new
314
- )
315
-
316
- # Eagerly load W3C namespace definitions (has registration side effects)
317
- require_relative "xml/w3c"
318
-
319
- # Auto-detection is now handled lazily by AdapterResolver on first use.
320
- # No eager adapter selection at require time — libraries can configure
321
- # their preferred adapter before any XML operations occur.
322
-
323
- # Namespace identifier validation:
324
- # - URIs (http://..., urn:...) are valid namespace names per W3C XML
325
- # Namespaces (RFC 3986). URNs are first-class identifiers (RFC 8141).
326
- # - Non-URI identifiers (e.g., FPIs like "-//OASIS//...") are also
327
- # accepted for compatibility, though not spec-compliant.
328
- # Use lenient mode to support FPIs and non-standard identifiers.
329
- Moxml.configure do |config|
330
- config.namespace_validation_mode = :lenient
331
- end
7
+ require_relative "xml/format"
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Internal YAML format implementation. Loaded from lib/lutaml/yaml.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
+ module Lutaml
9
+ module Yaml
10
+ class Error < StandardError; end
11
+
12
+ autoload :Adapter, "#{__dir__}/adapter"
13
+ autoload :Schema, "#{__dir__}/schema"
14
+ end
15
+ end
16
+
17
+ Lutaml::Model::FormatRegistry.register(
18
+ :yaml,
19
+ mapping_class: Lutaml::Yaml::Adapter::Mapping,
20
+ adapter_class: Lutaml::Yaml::Adapter::StandardAdapter,
21
+ transformer: Lutaml::Yaml::Adapter::Transform,
22
+ key_value: true,
23
+ adapter_options: {
24
+ available: %i[standard standard_yaml],
25
+ default: :standard,
26
+ },
27
+ )
28
+
29
+ require_relative "type/serializers"
30
+ Lutaml::Yaml::Type::Serializers.register_all!
data/lib/lutaml/yaml.rb CHANGED
@@ -1,30 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # YAML format module
4
- # Provides Lutaml::Yaml namespace for YAML serialization
5
-
6
- module Lutaml
7
- module Yaml
8
- class Error < StandardError; end
9
-
10
- autoload :Adapter, "#{__dir__}/yaml/adapter"
11
- autoload :Schema, "#{__dir__}/yaml/schema"
12
- end
13
- end
14
-
15
- # Register YAML format with the format registry
16
- Lutaml::Model::FormatRegistry.register(
17
- :yaml,
18
- mapping_class: Lutaml::Yaml::Adapter::Mapping,
19
- adapter_class: Lutaml::Yaml::Adapter::StandardAdapter,
20
- transformer: Lutaml::Yaml::Adapter::Transform,
21
- key_value: true,
22
- adapter_options: {
23
- available: %i[standard standard_yaml],
24
- default: :standard,
25
- },
26
- )
27
-
28
- # Register YAML type serializers
29
- require_relative "yaml/type/serializers"
30
- Lutaml::Yaml::Type::Serializers.register_all!
3
+ # Public YAML 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 "yaml/format"
@@ -10,9 +10,9 @@ module Lutaml
10
10
  super(:yaml)
11
11
  end
12
12
 
13
- def sequence(&)
13
+ def sequence(&block)
14
14
  @yamls_sequence = YamlsSequence.new
15
- @yamls_sequence.instance_eval(&)
15
+ @yamls_sequence.instance_eval(&block)
16
16
  end
17
17
 
18
18
  def dup_instance
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Internal YAMLS format implementation. Loaded from lib/lutaml/yamls.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
+ module Lutaml
9
+ module Yamls
10
+ class Error < StandardError; end
11
+
12
+ autoload :Adapter, "#{__dir__}/adapter"
13
+ end
14
+ end
15
+
16
+ Lutaml::Model::FormatRegistry.register(
17
+ :yamls,
18
+ mapping_class: Lutaml::Yamls::Adapter::Mapping,
19
+ adapter_class: Lutaml::Yamls::Adapter::StandardAdapter,
20
+ transformer: Lutaml::Yamls::Adapter::Transform,
21
+ key_value: true,
22
+ )
data/lib/lutaml/yamls.rb CHANGED
@@ -1,21 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # YAMLS format module
4
- # Provides Lutaml::Yamls namespace for YAMLS serialization
5
-
6
- module Lutaml
7
- module Yamls
8
- class Error < StandardError; end
9
-
10
- autoload :Adapter, "#{__dir__}/yamls/adapter"
11
- end
12
- end
13
-
14
- # Register YAMLS format with the format registry
15
- Lutaml::Model::FormatRegistry.register(
16
- :yamls,
17
- mapping_class: Lutaml::Yamls::Adapter::Mapping,
18
- adapter_class: Lutaml::Yamls::Adapter::StandardAdapter,
19
- transformer: Lutaml::Yamls::Adapter::Transform,
20
- key_value: true,
21
- )
3
+ # Public YAMLS 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 "yamls/format"
@@ -118,10 +118,10 @@ direction: nil)
118
118
  end
119
119
  end
120
120
 
121
- def time_runs(&)
121
+ def time_runs(&block)
122
122
  job = Benchmark::IPS::Job.new
123
123
  job.config(time: @run_time, warmup: 5)
124
- job.report("#{@label} #{@adapter} #{@direction}_#{@format}", &)
124
+ job.report("#{@label} #{@adapter} #{@direction}_#{@format}", &block)
125
125
  job.run
126
126
 
127
127
  entry = job.full_report.entries.first
data/lutaml-model.gemspec CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "canon"
39
39
  spec.add_dependency "concurrent-ruby"
40
40
  spec.add_dependency "liquid", ">= 4.0", "< 6.0"
41
- spec.add_dependency "moxml", "~> 0.1.25"
41
+ spec.add_dependency "moxml", "~> 0.5.0"
42
42
  spec.add_dependency "ostruct"
43
43
  spec.add_dependency "rubyzip", "~> 2.3"
44
44
  spec.add_dependency "thor"
@@ -18,7 +18,8 @@ RSpec.describe Lutaml::Model::Attribute, "#apply_value_map" do
18
18
 
19
19
  it "maps nil through value_map[:nil] => :empty to [] (collection)" do
20
20
  expect(
21
- attribute(:string, collection: true).apply_value_map(nil, { nil: :empty }),
21
+ attribute(:string, collection: true).apply_value_map(nil,
22
+ { nil: :empty }),
22
23
  ).to eq([])
23
24
  end
24
25
 
@@ -34,13 +35,15 @@ RSpec.describe Lutaml::Model::Attribute, "#apply_value_map" do
34
35
  custom_collection_class = Class.new(Lutaml::Model::Collection) do
35
36
  instances :items, member_class
36
37
  end
37
- attr = described_class.new("items", member_class, collection: custom_collection_class)
38
+ attr = described_class.new("items", member_class,
39
+ collection: custom_collection_class)
38
40
  result = attr.apply_value_map(nil, { nil: :empty })
39
41
  expect(result).to be_a(custom_collection_class)
40
42
  end
41
43
 
42
44
  it "maps nil through value_map[:nil] => :omitted to UninitializedClass" do
43
- expect(attribute(:string).apply_value_map(nil, { nil: :omitted })).to eq(uninit)
45
+ expect(attribute(:string).apply_value_map(nil,
46
+ { nil: :omitted })).to eq(uninit)
44
47
  end
45
48
  end
46
49
 
@@ -50,12 +53,14 @@ RSpec.describe Lutaml::Model::Attribute, "#apply_value_map" do
50
53
  end
51
54
 
52
55
  it "maps '' through value_map[:empty] => :empty preserving '' for scalar" do
53
- expect(attribute(:string).apply_value_map("", { empty: :empty })).to eq("")
56
+ expect(attribute(:string).apply_value_map("",
57
+ { empty: :empty })).to eq("")
54
58
  end
55
59
 
56
60
  it "maps [] through value_map[:empty] => :empty preserving [] for collection" do
57
61
  expect(
58
- attribute(:string, collection: true).apply_value_map([], { empty: :empty }),
62
+ attribute(:string, collection: true).apply_value_map([],
63
+ { empty: :empty }),
59
64
  ).to eq([])
60
65
  end
61
66
 
@@ -69,11 +74,13 @@ RSpec.describe Lutaml::Model::Attribute, "#apply_value_map" do
69
74
 
70
75
  describe "omitted handling" do
71
76
  it "maps UninitializedClass through value_map[:omitted] => :nil" do
72
- expect(attribute(:string).apply_value_map(uninit, { omitted: :nil })).to be_nil
77
+ expect(attribute(:string).apply_value_map(uninit,
78
+ { omitted: :nil })).to be_nil
73
79
  end
74
80
 
75
81
  it "maps UninitializedClass through value_map[:omitted] => :empty" do
76
- expect(attribute(:string).apply_value_map(uninit, { omitted: :empty })).to eq("")
82
+ expect(attribute(:string).apply_value_map(uninit,
83
+ { omitted: :empty })).to eq("")
77
84
  end
78
85
 
79
86
  it "does NOT route UninitializedClass through the :empty branch" do
@@ -81,18 +88,21 @@ RSpec.describe Lutaml::Model::Attribute, "#apply_value_map" do
81
88
  # utils.rb:132-137. Ensure dispatch order matches the originals: uninit
82
89
  # values go to :omitted, not :empty.
83
90
  expect(
84
- attribute(:string).apply_value_map(uninit, { empty: :nil, omitted: :empty }),
91
+ attribute(:string).apply_value_map(uninit,
92
+ { empty: :nil, omitted: :empty }),
85
93
  ).to eq("")
86
94
  end
87
95
  end
88
96
 
89
97
  describe "Boolean bare-form (Boolean attribute type required)" do
90
98
  it "returns false directly for Boolean attribute, value_map[:empty] = false" do
91
- expect(attribute(:boolean).apply_value_map("", { empty: false })).to be(false)
99
+ expect(attribute(:boolean).apply_value_map("",
100
+ { empty: false })).to be(false)
92
101
  end
93
102
 
94
103
  it "returns true directly for Boolean attribute, value_map[:omitted] = true" do
95
- expect(attribute(:boolean).apply_value_map(uninit, { omitted: true })).to be(true)
104
+ expect(attribute(:boolean).apply_value_map(uninit,
105
+ { omitted: true })).to be(true)
96
106
  end
97
107
 
98
108
  it "does NOT apply bare-boolean form to a non-Boolean attribute (documented tightening)" do
@@ -106,15 +116,18 @@ RSpec.describe Lutaml::Model::Attribute, "#apply_value_map" do
106
116
 
107
117
  describe "Boolean nested-form (value_map[:from][:empty] / [:omitted])" do
108
118
  it "returns false from value_map[:from][:empty] unconditionally for Boolean attribute" do
109
- expect(attribute(:boolean).apply_value_map("", { from: { empty: false } })).to be(false)
119
+ expect(attribute(:boolean).apply_value_map("",
120
+ { from: { empty: false } })).to be(false)
110
121
  end
111
122
 
112
123
  it "returns true from value_map[:from][:omitted] unconditionally for Boolean attribute" do
113
- expect(attribute(:boolean).apply_value_map(uninit, { from: { omitted: true } })).to be(true)
124
+ expect(attribute(:boolean).apply_value_map(uninit,
125
+ { from: { omitted: true } })).to be(true)
114
126
  end
115
127
 
116
128
  it "returns false from nested form even for non-Boolean attribute (no type gate)" do
117
- expect(attribute(:string).apply_value_map("", { from: { empty: false } })).to be(false)
129
+ expect(attribute(:string).apply_value_map("",
130
+ { from: { empty: false } })).to be(false)
118
131
  end
119
132
  end
120
133
 
@@ -358,7 +358,8 @@ RSpec.describe Lutaml::Model::Attribute do
358
358
  it "executes the proc in the instance context (reads instance attribute)" do
359
359
  instance = model_class.new(name: "Alice")
360
360
  attribute = model_class.attributes[:greeting]
361
- expect(attribute.default_value(register, instance)).to eq("Hello, Alice")
361
+ expect(attribute.default_value(register,
362
+ instance)).to eq("Hello, Alice")
362
363
  end
363
364
  end
364
365
  end
@@ -413,7 +414,9 @@ RSpec.describe Lutaml::Model::Attribute do
413
414
  end
414
415
  instance = model_class.new(prefix: "Dr")
415
416
  attr = model_class.attributes[:name]
416
- expect { attr.validate_value!(nil, register, instance_object: instance) }
417
+ expect do
418
+ attr.validate_value!(nil, register, instance_object: instance)
419
+ end
417
420
  .to raise_error(Lutaml::Model::InvalidValueError) do |error|
418
421
  # The error carries the value that was used for validation.
419
422
  # If instance_exec ran, value is "Dr_default"; if not, error
@@ -427,7 +430,9 @@ RSpec.describe Lutaml::Model::Attribute do
427
430
  it "short-circuits without raising" do
428
431
  uninit = Lutaml::Model::UninitializedClass.instance
429
432
  attr = described_class.new("name", :string)
430
- expect { attr.validate_value!(uninit, register, instance_object: model) }
433
+ expect do
434
+ attr.validate_value!(uninit, register, instance_object: model)
435
+ end
431
436
  .not_to raise_error
432
437
  end
433
438
  end
@@ -298,9 +298,9 @@ module CustomBibtexAdapterSpec
298
298
  add_mapping(name, to, field_type: :field, render_nil: render_nil)
299
299
  end
300
300
 
301
- def add_mapping(name, to, **)
301
+ def add_mapping(name, to, **options)
302
302
  # validate!(name, to, {})
303
- @mappings << BibtexMappingRule.new(name, to: to, **)
303
+ @mappings << BibtexMappingRule.new(name, to: to, **options)
304
304
  end
305
305
 
306
306
  def mapping_for_field(field)