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.
- checksums.yaml +4 -4
- data/.github/workflows/dependent-tests.yml +1 -1
- data/.github/workflows/rake.yml +1 -0
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +133 -72
- data/bench/bench_common.rb +8 -5
- data/bench/bench_niso.rb +1 -2
- data/bench/gate_config.rb +4 -1
- data/benchmark/serialization_benchmark.rb +4 -4
- data/docs/_guides/value-transformations.adoc +27 -0
- data/lib/compat/opal/generate_boot.rb +3 -1
- data/lib/compat/opal/lutaml_model_boot.rb +10 -7
- data/lib/compat/opal/moxml_boot.rb +6 -1
- data/lib/lutaml/hash_format/format.rb +29 -0
- data/lib/lutaml/hash_format.rb +5 -27
- data/lib/lutaml/json/format.rb +61 -0
- data/lib/lutaml/json.rb +4 -59
- data/lib/lutaml/jsonl/format.rb +22 -0
- data/lib/lutaml/jsonl.rb +5 -19
- data/lib/lutaml/key_value/format.rb +26 -0
- data/lib/lutaml/key_value/transform.rb +4 -1
- data/lib/lutaml/key_value/transformation/value_serializer.rb +0 -2
- data/lib/lutaml/key_value.rb +4 -21
- data/lib/lutaml/model/attribute.rb +38 -4
- data/lib/lutaml/model/collection.rb +2 -2
- data/lib/lutaml/model/compiled_rule.rb +1 -1
- data/lib/lutaml/model/config.rb +2 -2
- data/lib/lutaml/model/consolidation_map.rb +2 -2
- data/lib/lutaml/model/consolidation_rule/dispatch_block.rb +2 -2
- data/lib/lutaml/model/errors.rb +2 -2
- data/lib/lutaml/model/global_context.rb +4 -2
- data/lib/lutaml/model/mapping/mapping_rule.rb +4 -1
- data/lib/lutaml/model/mapping/model_mapping.rb +2 -2
- data/lib/lutaml/model/runtime_compatibility.rb +2 -2
- data/lib/lutaml/model/schema/class_loader.rb +2 -1
- data/lib/lutaml/model/schema/decorators/definition_collection.rb +4 -4
- data/lib/lutaml/model/schema/definitions/namespace.rb +2 -1
- data/lib/lutaml/model/schema/file_writer.rb +5 -2
- data/lib/lutaml/model/schema/renderers/base.rb +4 -3
- data/lib/lutaml/model/schema/renderers/mappings.rb +9 -3
- data/lib/lutaml/model/schema/renderers/member_decls.rb +9 -3
- data/lib/lutaml/model/schema/renderers/model.rb +18 -6
- data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +2 -1
- data/lib/lutaml/model/schema/renderers.rb +2 -1
- data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +6 -2
- data/lib/lutaml/model/schema/rnc_compiler.rb +3 -1
- data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +35 -15
- data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +4 -2
- data/lib/lutaml/model/schema/rng_compiler.rb +10 -5
- data/lib/lutaml/model/schema/xml_compiler/element_order.rb +2 -1
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +4 -2
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +39 -13
- data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +29 -10
- data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +2 -2
- data/lib/lutaml/model/schema/xml_compiler.rb +10 -5
- data/lib/lutaml/model/sequence.rb +4 -4
- data/lib/lutaml/model/serialize/attribute_definition.rb +1 -0
- data/lib/lutaml/model/serialize/format_conversion.rb +2 -2
- data/lib/lutaml/model/serialize/initialization.rb +9 -1
- data/lib/lutaml/model/serialize/model_import.rb +1 -0
- data/lib/lutaml/model/serialize.rb +4 -3
- data/lib/lutaml/model/type/uninitialized_class_guard.rb +1 -1
- data/lib/lutaml/model/type.rb +9 -0
- data/lib/lutaml/model/type_registry.rb +2 -2
- data/lib/lutaml/model/uninitialized_class.rb +1 -1
- data/lib/lutaml/model/union.rb +5 -2
- data/lib/lutaml/model/utils.rb +2 -2
- data/lib/lutaml/model/value_policy.rb +27 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +17 -16
- data/lib/lutaml/rdf/mapping.rb +2 -2
- data/lib/lutaml/rdf/namespace_set.rb +2 -2
- data/lib/lutaml/rdf/transform.rb +2 -2
- data/lib/lutaml/toml/format.rb +48 -0
- data/lib/lutaml/toml.rb +5 -46
- data/lib/lutaml/xml/adapter/base_adapter.rb +2 -2
- data/lib/lutaml/xml/builder/base.rb +2 -2
- data/lib/lutaml/xml/format.rb +331 -0
- data/lib/lutaml/xml/mapping.rb +2 -2
- data/lib/lutaml/xml/mapping_rule.rb +62 -3
- data/lib/lutaml/xml/model_transform.rb +132 -55
- data/lib/lutaml/xml/namespace_collector.rb +3 -3
- data/lib/lutaml/xml/parse_session.rb +59 -0
- data/lib/lutaml/xml/parsed_namespace_set.rb +2 -2
- data/lib/lutaml/xml/schema/builder.rb +5 -5
- data/lib/lutaml/xml/schema/xsd/schema_location_mapping.rb +2 -2
- data/lib/lutaml/xml/schema/xsd.rb +0 -2
- data/lib/lutaml/xml/schema/xsd_schema.rb +2 -1
- data/lib/lutaml/xml/serialization/format_conversion.rb +3 -3
- data/lib/lutaml/xml/transformation/element_builder.rb +2 -1
- data/lib/lutaml/xml/transformation/ordered_applier.rb +2 -1
- data/lib/lutaml/xml/xml_element.rb +2 -0
- data/lib/lutaml/xml.rb +4 -328
- data/lib/lutaml/yaml/format.rb +30 -0
- data/lib/lutaml/yaml.rb +5 -28
- data/lib/lutaml/yamls/adapter/mapping.rb +2 -2
- data/lib/lutaml/yamls/format.rb +22 -0
- data/lib/lutaml/yamls.rb +5 -19
- data/lib/tasks/benchmark_runner.rb +2 -2
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/model/attribute_apply_value_map_spec.rb +26 -13
- data/spec/lutaml/model/attribute_spec.rb +8 -3
- data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +2 -2
- data/spec/lutaml/model/custom_from_empty_values_spec.rb +74 -0
- data/spec/lutaml/model/custom_vobject_adapter_spec.rb +8 -8
- data/spec/lutaml/model/defaults_spec.rb +45 -0
- data/spec/lutaml/model/json_spec.rb +1 -2
- data/spec/lutaml/model/serialize/builder_spec.rb +6 -3
- data/spec/lutaml/model/store_spec.rb +3 -1
- data/spec/lutaml/model/transform_apply_value_map_spec.rb +6 -3
- data/spec/lutaml/model/union_attribute_spec.rb +4 -2
- data/spec/lutaml/model/value_policy_spec.rb +82 -0
- data/spec/lutaml/require_load_order_spec.rb +25 -0
- data/spec/lutaml/xml/attribute_element_namespace_disjoint_spec.rb +88 -0
- data/spec/lutaml/xml/attribute_namespace_disjointness_spec.rb +125 -0
- data/spec/lutaml/xml/out_of_namespace_parsing_spec.rb +123 -0
- data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +17 -6
- data/spec/lutaml/xml/schema/rng_compiler_spec.rb +15 -6
- data/spec/lutaml/xml/w3c_types_spec.rb +3 -6
- metadata +20 -4
data/lib/lutaml/xml.rb
CHANGED
|
@@ -1,331 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
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
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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"
|
|
@@ -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
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
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,
|
|
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,
|
|
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,
|
|
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("",
|
|
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([],
|
|
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,
|
|
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,
|
|
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,
|
|
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("",
|
|
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,
|
|
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("",
|
|
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,
|
|
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("",
|
|
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,
|
|
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
|
|
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
|
|
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)
|