lutaml-model 0.8.19 → 0.8.23

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 (139) 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/docs/_guides/xml-mapping.adoc +31 -0
  12. data/lib/compat/opal/generate_boot.rb +3 -1
  13. data/lib/compat/opal/lutaml_model_boot.rb +10 -7
  14. data/lib/compat/opal/moxml_boot.rb +6 -1
  15. data/lib/lutaml/hash_format/format.rb +29 -0
  16. data/lib/lutaml/hash_format.rb +5 -27
  17. data/lib/lutaml/json/adapter/oj_adapter.rb +7 -0
  18. data/lib/lutaml/json/adapter/standard_adapter.rb +17 -5
  19. data/lib/lutaml/json/format.rb +61 -0
  20. data/lib/lutaml/json/generator_options.rb +62 -0
  21. data/lib/lutaml/json.rb +4 -59
  22. data/lib/lutaml/jsonl/adapter/standard_adapter.rb +1 -1
  23. data/lib/lutaml/jsonl/format.rb +22 -0
  24. data/lib/lutaml/jsonl.rb +5 -19
  25. data/lib/lutaml/jsonld/adapter.rb +12 -4
  26. data/lib/lutaml/key_value/adapter/json/oj_adapter.rb +7 -0
  27. data/lib/lutaml/key_value/adapter/json/standard_adapter.rb +17 -5
  28. data/lib/lutaml/key_value/adapter/jsonl/standard_adapter.rb +1 -1
  29. data/lib/lutaml/key_value/format.rb +26 -0
  30. data/lib/lutaml/key_value/transform.rb +4 -1
  31. data/lib/lutaml/key_value/transformation/value_serializer.rb +0 -2
  32. data/lib/lutaml/key_value.rb +4 -21
  33. data/lib/lutaml/model/attribute.rb +38 -4
  34. data/lib/lutaml/model/collection.rb +7 -2
  35. data/lib/lutaml/model/compiled_rule.rb +1 -1
  36. data/lib/lutaml/model/config.rb +2 -2
  37. data/lib/lutaml/model/consolidation_map.rb +2 -2
  38. data/lib/lutaml/model/consolidation_rule/dispatch_block.rb +2 -2
  39. data/lib/lutaml/model/errors.rb +2 -2
  40. data/lib/lutaml/model/global_context.rb +4 -2
  41. data/lib/lutaml/model/mapping/mapping_rule.rb +4 -1
  42. data/lib/lutaml/model/mapping/model_mapping.rb +2 -2
  43. data/lib/lutaml/model/runtime_compatibility.rb +2 -2
  44. data/lib/lutaml/model/schema/class_loader.rb +2 -1
  45. data/lib/lutaml/model/schema/decorators/definition_collection.rb +4 -4
  46. data/lib/lutaml/model/schema/definitions/namespace.rb +2 -1
  47. data/lib/lutaml/model/schema/file_writer.rb +5 -2
  48. data/lib/lutaml/model/schema/renderers/base.rb +4 -3
  49. data/lib/lutaml/model/schema/renderers/mappings.rb +9 -3
  50. data/lib/lutaml/model/schema/renderers/member_decls.rb +9 -3
  51. data/lib/lutaml/model/schema/renderers/model.rb +18 -6
  52. data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +2 -1
  53. data/lib/lutaml/model/schema/renderers.rb +2 -1
  54. data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +6 -2
  55. data/lib/lutaml/model/schema/rnc_compiler.rb +3 -1
  56. data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +35 -15
  57. data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +4 -2
  58. data/lib/lutaml/model/schema/rng_compiler.rb +10 -5
  59. data/lib/lutaml/model/schema/xml_compiler/element_order.rb +2 -1
  60. data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +4 -2
  61. data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +39 -13
  62. data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +29 -10
  63. data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +2 -2
  64. data/lib/lutaml/model/schema/xml_compiler.rb +10 -5
  65. data/lib/lutaml/model/sequence.rb +4 -4
  66. data/lib/lutaml/model/serialization_adapter.rb +1 -1
  67. data/lib/lutaml/model/serialize/attribute_definition.rb +1 -0
  68. data/lib/lutaml/model/serialize/format_conversion.rb +40 -9
  69. data/lib/lutaml/model/serialize/initialization.rb +9 -1
  70. data/lib/lutaml/model/serialize/model_import.rb +1 -0
  71. data/lib/lutaml/model/serialize.rb +22 -3
  72. data/lib/lutaml/model/transformation.rb +5 -0
  73. data/lib/lutaml/model/type/uninitialized_class_guard.rb +1 -1
  74. data/lib/lutaml/model/type.rb +9 -0
  75. data/lib/lutaml/model/type_registry.rb +2 -2
  76. data/lib/lutaml/model/uninitialized_class.rb +1 -1
  77. data/lib/lutaml/model/union.rb +5 -2
  78. data/lib/lutaml/model/utils.rb +2 -2
  79. data/lib/lutaml/model/value_policy.rb +27 -0
  80. data/lib/lutaml/model/version.rb +1 -1
  81. data/lib/lutaml/model.rb +17 -16
  82. data/lib/lutaml/rdf/mapping.rb +2 -2
  83. data/lib/lutaml/rdf/namespace_set.rb +2 -2
  84. data/lib/lutaml/rdf/transform.rb +2 -2
  85. data/lib/lutaml/toml/format.rb +48 -0
  86. data/lib/lutaml/toml.rb +5 -46
  87. data/lib/lutaml/xml/adapter/base_adapter.rb +2 -2
  88. data/lib/lutaml/xml/builder/base.rb +2 -2
  89. data/lib/lutaml/xml/format.rb +331 -0
  90. data/lib/lutaml/xml/mapping.rb +26 -2
  91. data/lib/lutaml/xml/mapping_rule.rb +62 -3
  92. data/lib/lutaml/xml/model_transform.rb +183 -55
  93. data/lib/lutaml/xml/namespace_collector.rb +3 -3
  94. data/lib/lutaml/xml/parse_session.rb +59 -0
  95. data/lib/lutaml/xml/parsed_namespace_set.rb +2 -2
  96. data/lib/lutaml/xml/schema/builder.rb +5 -5
  97. data/lib/lutaml/xml/schema/xsd/schema_location_mapping.rb +2 -2
  98. data/lib/lutaml/xml/schema/xsd.rb +0 -2
  99. data/lib/lutaml/xml/schema/xsd_schema.rb +2 -1
  100. data/lib/lutaml/xml/serialization/format_conversion.rb +3 -3
  101. data/lib/lutaml/xml/transformation/element_builder.rb +2 -1
  102. data/lib/lutaml/xml/transformation/ordered_applier.rb +2 -1
  103. data/lib/lutaml/xml/transformation.rb +29 -0
  104. data/lib/lutaml/xml/xml_element.rb +2 -0
  105. data/lib/lutaml/xml.rb +4 -328
  106. data/lib/lutaml/yaml/format.rb +30 -0
  107. data/lib/lutaml/yaml.rb +5 -28
  108. data/lib/lutaml/yamls/adapter/mapping.rb +2 -2
  109. data/lib/lutaml/yamls/format.rb +22 -0
  110. data/lib/lutaml/yamls.rb +5 -19
  111. data/lib/tasks/benchmark_runner.rb +2 -2
  112. data/lutaml-model.gemspec +1 -1
  113. data/spec/lutaml/json/adapter/standard_adapter_spec.rb +159 -0
  114. data/spec/lutaml/jsonld/adapter_spec.rb +19 -0
  115. data/spec/lutaml/model/attribute_apply_value_map_spec.rb +26 -13
  116. data/spec/lutaml/model/attribute_spec.rb +8 -3
  117. data/spec/lutaml/model/collection_spec.rb +1 -1
  118. data/spec/lutaml/model/collection_state_nesting_spec.rb +44 -0
  119. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +2 -2
  120. data/spec/lutaml/model/custom_from_empty_values_spec.rb +74 -0
  121. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +8 -8
  122. data/spec/lutaml/model/defaults_spec.rb +45 -0
  123. data/spec/lutaml/model/generator_defaults_spec.rb +101 -0
  124. data/spec/lutaml/model/json_spec.rb +1 -2
  125. data/spec/lutaml/model/json_state_nesting_spec.rb +82 -0
  126. data/spec/lutaml/model/serialize/builder_spec.rb +6 -3
  127. data/spec/lutaml/model/store_spec.rb +3 -1
  128. data/spec/lutaml/model/transform_apply_value_map_spec.rb +6 -3
  129. data/spec/lutaml/model/union_attribute_spec.rb +4 -2
  130. data/spec/lutaml/model/value_policy_spec.rb +82 -0
  131. data/spec/lutaml/require_load_order_spec.rb +25 -0
  132. data/spec/lutaml/xml/attribute_element_namespace_disjoint_spec.rb +88 -0
  133. data/spec/lutaml/xml/attribute_namespace_disjointness_spec.rb +125 -0
  134. data/spec/lutaml/xml/out_of_namespace_parsing_spec.rb +123 -0
  135. data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +17 -6
  136. data/spec/lutaml/xml/schema/rng_compiler_spec.rb +15 -6
  137. data/spec/lutaml/xml/shared_element_rules_spec.rb +64 -0
  138. data/spec/lutaml/xml/w3c_types_spec.rb +3 -6
  139. metadata +26 -4
@@ -264,6 +264,8 @@ module Lutaml
264
264
  root, model_instance, options,
265
265
  compiled_rules, model_class, register_id
266
266
  ) do |action, rule, value, set_xsi_nil|
267
+ next if action == :apply_rule && duplicate_element_rules[rule]
268
+
267
269
  rule_options = options.merge(current_model: model_instance)
268
270
  case action
269
271
  when :apply_rule
@@ -291,6 +293,32 @@ module Lutaml
291
293
  # @param root [XmlElement] Root element
292
294
  # @param model_instance [Object] The model instance
293
295
  # @param options [Hash] Options
296
+ # Reader for the precomputed skip set (#765).
297
+ def duplicate_element_rules
298
+ @duplicate_element_rules
299
+ end
300
+
301
+ # lutaml-model#765: several element rules may target one attribute
302
+ # (spelling tolerance, e.g. editorial-group vs editorialgroup). The
303
+ # extra spellings exist for parse tolerance only — serialization
304
+ # emits the value once, under the first declared spelling. Derived
305
+ # once at compile time (transformations are frozen and shared).
306
+ def after_compile
307
+ seen = {}
308
+ @duplicate_element_rules = {}
309
+ compiled_rules.each do |rule|
310
+ next unless rule.option(:mapping_type) == :element
311
+
312
+ key = rule.attribute_name.to_s
313
+ if seen.key?(key)
314
+ @duplicate_element_rules[rule] = true
315
+ else
316
+ seen[key] = true
317
+ end
318
+ end
319
+ @duplicate_element_rules.freeze
320
+ end
321
+
294
322
  def apply_standard_rules(root, model_instance, options)
295
323
  attr_order = model_instance.is_a?(Lutaml::Model::Serialize) &&
296
324
  model_instance.attribute_order
@@ -303,6 +331,7 @@ module Lutaml
303
331
 
304
332
  rules.each do |rule|
305
333
  next unless valid_mapping?(rule, options)
334
+ next if duplicate_element_rules[rule]
306
335
 
307
336
  rule_options = options.merge(current_model: model_instance)
308
337
  apply_rule(root, rule, model_instance, rule_options, model_class,
@@ -360,6 +360,8 @@ module Lutaml
360
360
  end
361
361
 
362
362
  def find_attribute_value(attribute_name)
363
+ return if attributes.empty?
364
+
363
365
  # Performance: Use hash index for O(1) lookup instead of linear scan
364
366
  ensure_attribute_index
365
367
 
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"