lutaml-model 0.8.0 → 0.8.2

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-repos.json +9 -0
  3. data/.github/workflows/downstream-performance.yml +0 -3
  4. data/.rubocop_todo.yml +18 -186
  5. data/README.adoc +212 -15
  6. data/bench/bench_xmi.rb +6 -6
  7. data/bench/gate_config.rb +2 -9
  8. data/docs/_pages/configuration.adoc +155 -41
  9. data/docs/_pages/serialization_adapters.adoc +65 -14
  10. data/docs/index.adoc +3 -1
  11. data/docs/yamls_sequence.adoc +335 -0
  12. data/lib/lutaml/hash_format.rb +4 -0
  13. data/lib/lutaml/json/adapter/multi_json_adapter.rb +4 -2
  14. data/lib/lutaml/json/adapter/oj_adapter.rb +4 -2
  15. data/lib/lutaml/json.rb +4 -0
  16. data/lib/lutaml/key_value/adapter/json/multi_json_adapter.rb +4 -2
  17. data/lib/lutaml/key_value/adapter/json/oj_adapter.rb +4 -2
  18. data/lib/lutaml/model/adapter_resolver.rb +410 -0
  19. data/lib/lutaml/model/adapter_scope.rb +64 -0
  20. data/lib/lutaml/model/config.rb +84 -21
  21. data/lib/lutaml/model/configuration.rb +17 -249
  22. data/lib/lutaml/model/format_registry.rb +44 -117
  23. data/lib/lutaml/model/mapping/listener.rb +4 -2
  24. data/lib/lutaml/model/serialize/format_conversion.rb +42 -3
  25. data/lib/lutaml/model/serialize.rb +4 -2
  26. data/lib/lutaml/model/services/base.rb +4 -2
  27. data/lib/lutaml/model/version.rb +1 -1
  28. data/lib/lutaml/model.rb +2 -0
  29. data/lib/lutaml/toml.rb +10 -3
  30. data/lib/lutaml/xml/serialization/instance_methods.rb +6 -0
  31. data/lib/lutaml/xml.rb +3 -4
  32. data/lib/lutaml/yaml.rb +4 -0
  33. data/lib/lutaml/yamls/adapter/mapping.rb +7 -0
  34. data/lib/lutaml/yamls/adapter/standard_adapter.rb +23 -2
  35. data/lib/lutaml/yamls/adapter/transform.rb +105 -7
  36. data/lib/lutaml/yamls/adapter/yamls_sequence.rb +20 -0
  37. data/lib/lutaml/yamls/adapter/yamls_sequence_rule.rb +48 -0
  38. data/lib/lutaml/yamls/adapter.rb +2 -0
  39. data/spec/fixtures/geolexica_v2_concept.rb +136 -0
  40. data/spec/fixtures/geolexica_v2_sample.yaml +36 -0
  41. data/spec/fixtures/geolexica_v2_sample2.yaml +38 -0
  42. data/spec/fixtures/yamls_range_concept.rb +139 -0
  43. data/spec/lutaml/model/xml_decoupling_spec.rb +5 -4
  44. data/spec/lutaml/model/yamls_range_spec.rb +393 -0
  45. data/spec/lutaml/model/yamls_sequence_spec.rb +245 -0
  46. data/spec/spec_helper.rb +5 -0
  47. metadata +13 -3
  48. data/bench/bench_uniword.rb +0 -69
data/bench/gate_config.rb CHANGED
@@ -20,7 +20,7 @@ module GateConfig
20
20
  xmi: {
21
21
  "ea251" => {
22
22
  file: "ea-xmi-2.5.1.xmi",
23
- alloc_ratio: 1.05,
23
+ alloc_ratio: 1.10,
24
24
  time_ratio: 1.15,
25
25
  absolute_max: 0.50,
26
26
  },
@@ -91,14 +91,7 @@ module GateConfig
91
91
  "pnas-152KB" => {
92
92
  alloc_ratio: 1.05,
93
93
  time_ratio: 1.15,
94
- absolute_max: 1.0,
95
- },
96
- },
97
- uniword: {
98
- "iso690" => {
99
- alloc_ratio: 1.05,
100
- time_ratio: 1.15,
101
- absolute_max: 60.0,
94
+ absolute_max: 1.5,
102
95
  },
103
96
  },
104
97
  }.freeze
@@ -7,7 +7,27 @@ nav_order: 10
7
7
 
8
8
  == Overview
9
9
 
10
- Lutaml::Model uses an adapter pattern to support multiple serialization libraries. You must configure which adapters to use for each format.
10
+ Lutaml::Model uses an adapter pattern to support multiple serialization libraries. Adapters are resolved automatically via lazy auto-detection, but can be explicitly configured when needed.
11
+
12
+ == Auto-detection
13
+
14
+ Lutaml::Model automatically detects available adapter gems at runtime on first use. You only need to configure adapters if:
15
+
16
+ * You want to override the default choice
17
+ * You are testing and need deterministic adapter selection
18
+ * Your library requires a specific adapter
19
+
20
+ The auto-detection order for each format:
21
+
22
+ [cols="1,2",options="header"]
23
+ |===
24
+ | Format | Detection order
25
+ | XML | `:nokogiri` → `:ox` → `:oga` → `:rexml`
26
+ | TOML | `:tomlib` → `:toml_rb` (Windows: `:toml_rb` only)
27
+ | JSON | `:standard` (always available)
28
+ | YAML | `:standard` (always available)
29
+ | Hash | `:standard` (always available)
30
+ |===
11
31
 
12
32
  == Basic configuration
13
33
 
@@ -18,14 +38,24 @@ Use `Lutaml::Model::Config.configure` to set adapters:
18
38
  require 'lutaml/model'
19
39
 
20
40
  Lutaml::Model::Config.configure do |config|
21
- config.xml_adapter_type = :nokogiri
22
- config.json_adapter_type = :standard_json
23
- config.yaml_adapter_type = :standard_yaml
24
- config.toml_adapter_type = :toml_rb
25
- config.hash_adapter_type = :standard_hash
41
+ config.xml_adapter = :nokogiri
42
+ config.json_adapter = :standard
43
+ config.yaml_adapter = :standard
44
+ config.toml_adapter = :toml_rb
45
+ config.hash_adapter = :standard
26
46
  end
27
47
  ----
28
48
 
49
+ == Resolution chain
50
+
51
+ When an adapter is needed for a format, `AdapterResolver` follows this chain:
52
+
53
+ . **Thread-local scope override** — from `Config.with_adapter` block
54
+ . **Explicitly configured type** — from `Config.xml_adapter_type = :nokogiri`
55
+ . **Lazy auto-detected type** — cached after first probe
56
+ . **Format default** — built-in default for the format
57
+ . **Error** — `FormatAdapterNotSpecifiedError`
58
+
29
59
  == Available adapters
30
60
 
31
61
  === XML adapters
@@ -42,6 +72,9 @@ end
42
72
 
43
73
  | `:oga`
44
74
  | Pure Ruby. No compilation needed. Opal-compatible.
75
+
76
+ | `:rexml`
77
+ | Pure Ruby. Bundled with Ruby (default gem).
45
78
  |===
46
79
 
47
80
  === JSON adapters
@@ -50,7 +83,7 @@ end
50
83
  |===
51
84
  | Adapter | Description
52
85
 
53
- | `:standard_json` (default)
86
+ | `:standard` (default, aliases: `:standard_json`)
54
87
  | Ruby standard library. No extra gems needed.
55
88
 
56
89
  | `:multi_json`
@@ -66,7 +99,7 @@ end
66
99
  |===
67
100
  | Adapter | Description
68
101
 
69
- | `:standard_yaml` (default)
102
+ | `:standard` (default, aliases: `:standard_yaml`)
70
103
  | Psych (Ruby standard library). No extra gems needed.
71
104
  |===
72
105
 
@@ -85,30 +118,85 @@ end
85
118
 
86
119
  NOTE: The `:tomlib` adapter is not available on Windows due to segmentation fault issues. On Windows, `:toml_rb` is automatically used as the default.
87
120
 
88
- == Opal runtime compatibility
121
+ == Per-operation adapter override
89
122
 
90
- Lutaml::Model supports running under https://opalrb.com[Opal] (Ruby compiled to JavaScript) with some limitations. The library detects the runtime automatically via `Lutaml::Model::RuntimeCompatibility` and adapts its behavior accordingly.
123
+ Override the adapter for a single serialization/deserialization call using the `adapter:` option:
91
124
 
92
- === Adapter defaults on Opal
125
+ [source,ruby]
126
+ ----
127
+ # Parse with Ox for this call only
128
+ model = MyClass.from_xml(xml_string, adapter: :ox)
93
129
 
94
- [cols="1,2",options="header"]
95
- |===
96
- | Format | Behavior
97
- | XML | Only `:oga` is available (auto-selected). Nokogiri, Ox, and REXML require native extensions.
98
- | JSON | Only `:standard` is available. Oj and MultiJson require native extensions.
99
- | YAML | `:standard` (Psych ships with Opal's stdlib).
100
- | TOML | **Not available.** Both tomlib and toml-rb depend on native extensions.
101
- | Hash | `:standard` / `:standard_hash` (pure Ruby).
102
- |===
130
+ # Serialize with REXML for this call only
131
+ output = model.to_xml(adapter: :rexml)
132
+ ----
103
133
 
104
- === Features unavailable on Opal
134
+ This is useful for:
105
135
 
106
- The following features raise `NotImplementedError` when called under Opal:
136
+ * Testing with a specific adapter without changing global configuration
137
+ * Performance-sensitive operations using a faster adapter
138
+ * Cross-adapter workflows (parse with one, serialize with another)
139
+
140
+ == Scoped adapter context
141
+
142
+ Use `Config.with_adapter` for thread-safe, block-scoped adapter overrides:
143
+
144
+ [source,ruby]
145
+ ----
146
+ # All XML operations in this block use Ox
147
+ Lutaml::Model::Config.with_adapter(xml: :ox) do
148
+ model = MyClass.from_xml(data)
149
+ model.to_xml # also uses Ox
150
+ end
151
+ # Outside the block, reverts to the configured default
152
+
153
+ # Multiple formats at once
154
+ Lutaml::Model::Config.with_adapter(xml: :nokogiri, toml: :tomlib) do
155
+ model = MyClass.from_xml(data)
156
+ toml = model.to_toml
157
+ end
158
+ ----
159
+
160
+ TIP: `with_adapter` is thread-safe — each thread has its own scope stack. Use it in tests instead of save/restore patterns:
161
+
162
+ [source,ruby]
163
+ ----
164
+ # Instead of this:
165
+ around do |example|
166
+ old = Config.xml_adapter
167
+ Config.xml_adapter_type = :oga
168
+ example.run
169
+ ensure
170
+ Config.xml_adapter = old
171
+ end
172
+
173
+ # Prefer this:
174
+ around do |example|
175
+ Config.with_adapter(xml: :oga) { example.run }
176
+ end
177
+ ----
107
178
 
108
- * `Lutaml::Model::Schema.to_xsd` — XSD schema generation requires Nokogiri
109
- * `Lutaml::Model::Schema.to_relaxng` — RELAX NG generation requires Nokogiri
110
- * `Lutaml::Model::Schema.from_xml` XML schema compilation is not supported
111
- * `Lutaml::Xml::Schema::Xsd::Base#to_formatted_xml` — XSD formatted output requires the Canon gem
179
+ == Library stacking
180
+
181
+ When multiple libraries depend on lutaml-model, each can set its preferred adapter within a scoped context:
182
+
183
+ [source,ruby]
184
+ ----
185
+ # In gem "my_xml_library"
186
+ module MyXmlLibrary
187
+ def self.parse(data)
188
+ # This gem requires Nokogiri internally
189
+ Config.with_adapter(xml: :nokogiri) do
190
+ MyModel.from_xml(data)
191
+ end
192
+ end
193
+ end
194
+
195
+ # The end-user's adapter choice is not affected
196
+ Config.xml_adapter_type = :ox # user prefers Ox
197
+ result = MyXmlLibrary.parse(data) # uses Nokogiri inside
198
+ my_model.to_xml # uses Ox (user's choice)
199
+ ----
112
200
 
113
201
  == Configuration with class references
114
202
 
@@ -118,8 +206,8 @@ For advanced use, specify adapter classes directly:
118
206
  ----
119
207
  require 'lutaml/model'
120
208
  require 'lutaml/xml/adapter/nokogiri_adapter'
121
- require 'lutaml/json/adapter/standard_adapter'
122
- require 'lutaml/yaml/adapter/standard_adapter'
209
+ require 'lutaml/key_value/adapter/json/standard_adapter'
210
+ require 'lutaml/key_value/adapter/yaml/standard_adapter'
123
211
  require 'lutaml/toml/adapter/toml_rb_adapter'
124
212
 
125
213
  Lutaml::Model::Config.configure do |config|
@@ -132,23 +220,23 @@ end
132
220
 
133
221
  == Default configuration
134
222
 
135
- If not configured, Lutaml::Model uses these defaults:
223
+ If not explicitly configured, Lutaml::Model auto-detects on first use:
136
224
 
137
- * XML: `:nokogiri`
138
- * JSON: `:standard` (or `:standard_json` as an alias)
139
- * YAML: `:standard` (or `:standard_yaml` as an alias)
140
- * Hash: `:standard` (or `:standard_hash` as an alias)
225
+ * XML: `:nokogiri` if available, else `:ox` → `:oga` → `:rexml`
226
+ * JSON: `:standard` (alias: `:standard_json`)
227
+ * YAML: `:standard` (alias: `:standard_yaml`)
228
+ * Hash: `:standard` (alias: `:standard_hash`)
141
229
  * TOML: `:tomlib` on non-Windows, `:toml_rb` on Windows
142
230
 
143
231
  == When to configure
144
232
 
145
233
  Configure adapters in one of these locations:
146
234
 
147
- For applications:: In an initializer or early-loading file
235
+ For applications:: In an initializer or early-loading file (optional -- auto-detection usually sufficient)
148
236
 
149
- For gems:: Let end users configure adapters (document the requirement)
237
+ For gems:: Do not configure globally. Use `Config.with_adapter` for internal adapter requirements.
150
238
 
151
- For testing:: In `spec_helper.rb` or test setup
239
+ For testing:: In `spec_helper.rb` or test setup, or use `Config.with_adapter` per test.
152
240
 
153
241
  .RSpec configuration example
154
242
  [example]
@@ -159,10 +247,10 @@ For testing:: In `spec_helper.rb` or test setup
159
247
  require 'lutaml/model'
160
248
 
161
249
  Lutaml::Model::Config.configure do |config|
162
- config.xml_adapter_type = :nokogiri
163
- config.json_adapter_type = :standard_json
164
- config.yaml_adapter_type = :standard_yaml
165
- config.toml_adapter_type = :toml_rb
250
+ config.xml_adapter = :nokogiri
251
+ config.json_adapter = :standard
252
+ config.yaml_adapter = :standard
253
+ config.toml_adapter = :toml_rb
166
254
  end
167
255
  ----
168
256
  ====
@@ -174,6 +262,7 @@ end
174
262
  * **Nokogiri**: Most projects (default, best compatibility)
175
263
  * **Ox**: Performance-critical applications
176
264
  * **Oga**: Pure Ruby environments, Opal/JavaScript compilation
265
+ * **REXML**: No extra gems, pure Ruby (bundled with Ruby)
177
266
 
178
267
  === Choose JSON adapter based on
179
268
 
@@ -186,7 +275,32 @@ end
186
275
  * **Tomlib**: Most projects on non-Windows (better performance)
187
276
  * **TomlRb**: Windows platforms (required due to tomlib incompatibility)
188
277
 
278
+ == Opal runtime compatibility
279
+
280
+ Lutaml::Model supports running under https://opalrb.com[Opal] (Ruby compiled to JavaScript) with some limitations. The library detects the runtime automatically via `Lutaml::Model::RuntimeCompatibility` and adapts its behavior accordingly.
281
+
282
+ === Adapter defaults on Opal
283
+
284
+ [cols="1,2",options="header"]
285
+ |===
286
+ | Format | Behavior
287
+ | XML | Only `:oga` is available (auto-selected). Nokogiri, Ox, and REXML require native extensions.
288
+ | JSON | Only `:standard` is available. Oj and MultiJson require native extensions.
289
+ | YAML | `:standard` (Psych ships with Opal's stdlib).
290
+ | TOML | **Not available.** Both tomlib and toml-rb depend on native extensions.
291
+ | Hash | `:standard` (pure Ruby).
292
+ |===
293
+
294
+ === Features unavailable on Opal
295
+
296
+ The following features raise `NotImplementedError` when called under Opal:
297
+
298
+ * `Lutaml::Model::Schema.to_xsd` -- XSD schema generation requires Nokogiri
299
+ * `Lutaml::Model::Schema.to_relaxng` -- RELAX NG generation requires Nokogiri
300
+ * `Lutaml::Model::Schema.from_xml` -- XML schema compilation is not supported
301
+ * `Lutaml::Xml::Schema::Xsd::Base#to_formatted_xml` -- XSD formatted output requires the Canon gem
302
+
189
303
  == See also
190
304
 
191
305
  * link:../serialization_adapters[Serialization Adapters Reference]
192
- * link:../references/custom_adapters[Creating Custom Adapters]
306
+ * link:../references/custom_adapters[Creating Custom Adapters]
@@ -41,6 +41,17 @@ different serialization libraries. Please refer to their specific sections for
41
41
  more information.
42
42
 
43
43
 
44
+ === Auto-detection
45
+
46
+ Adapters are resolved lazily on first use. If no adapter is explicitly configured, `AdapterResolver` probes for available gems in a preferred order:
47
+
48
+ * **XML**: `:nokogiri` → `:ox` → `:oga` → `:rexml`
49
+ * **TOML**: `:tomlib` → `:toml_rb` (Windows: `:toml_rb` only)
50
+ * **JSON/YAML/Hash**: `:standard` (always available)
51
+
52
+ The detection result is cached after the first probe, so it only runs once per format.
53
+
54
+
44
55
  === Configuration
45
56
 
46
57
  ==== General
@@ -67,10 +78,10 @@ There are two ways to specify a configuration:
67
78
 
68
79
  There is a default configuration for adapters for commonly used formats:
69
80
 
70
- * XML: `:nokogiri`
71
- * YAML: `:standard` (or `:standard_yaml` as an alias)
72
- * JSON: `:standard` (or `:standard_json` as an alias)
73
- * Hash: `:standard` (or `:standard_hash` as an alias)
81
+ * XML: `:nokogiri` (auto-detected)
82
+ * YAML: `:standard` (alias: `:standard_yaml`)
83
+ * JSON: `:standard` (alias: `:standard_json`)
84
+ * Hash: `:standard` (alias: `:standard_hash`)
74
85
  * TOML: `:tomlib` on non-Windows, `:toml_rb` on Windows
75
86
 
76
87
 
@@ -90,11 +101,11 @@ Syntax:
90
101
  require 'lutaml/model'
91
102
 
92
103
  Lutaml::Model::Config.configure do |config|
93
- config.xml_adapter_type = :nokogiri # can be one of [:nokogiri, :ox, :oga, :rexml]
94
- config.hash_adapter_type = :standard
95
- config.yaml_adapter_type = :standard
96
- config.json_adapter_type = :standard # can be one of [:standard, :multi_json, :oj]
97
- config.toml_adapter_type = :toml_rb # can be one of [:toml_rb, :tomlib] (tomlib not available on Windows)
104
+ config.xml_adapter = :nokogiri # can be one of [:nokogiri, :ox, :oga, :rexml]
105
+ config.hash_adapter = :standard
106
+ config.yaml_adapter = :standard
107
+ config.json_adapter = :standard # can be one of [:standard, :multi_json, :oj]
108
+ config.toml_adapter = :toml_rb # can be one of [:toml_rb, :tomlib] (tomlib not available on Windows)
98
109
  end
99
110
  ----
100
111
 
@@ -130,6 +141,41 @@ end
130
141
  ====
131
142
 
132
143
 
144
+ ==== Per-operation adapter override
145
+
146
+ Override the adapter for a single call using the `adapter:` option:
147
+
148
+ [source,ruby]
149
+ ----
150
+ # Parse with Ox for this call only
151
+ model = MyClass.from_xml(xml_string, adapter: :ox)
152
+
153
+ # Serialize with REXML for this call only
154
+ output = model.to_xml(adapter: :rexml)
155
+ ----
156
+
157
+
158
+ ==== Scoped adapter context
159
+
160
+ Use `Config.with_adapter` for thread-safe, block-scoped overrides:
161
+
162
+ [source,ruby]
163
+ ----
164
+ Lutaml::Model::Config.with_adapter(xml: :ox) do
165
+ model = MyClass.from_xml(data)
166
+ model.to_xml # also uses Ox
167
+ end
168
+ # Outside the block, reverts to the configured default
169
+
170
+ # Multiple formats at once
171
+ Lutaml::Model::Config.with_adapter(xml: :nokogiri, toml: :tomlib) do
172
+ model = MyClass.from_xml(data)
173
+ toml = model.to_toml
174
+ end
175
+ ----
176
+
177
+ TIP: `with_adapter` is thread-safe -- use it in tests instead of save/restore patterns.
178
+
133
179
 
134
180
  === XML
135
181
 
@@ -154,6 +200,12 @@ Fast XML parser and object serializer for Ruby, implemented partially in C.
154
200
  Requires native extensions (i.e. compiled C code).
155
201
  Requires the `ox` gem.
156
202
 
203
+ REXML::
204
+ (optional)
205
+ Pure Ruby XML parser, bundled as a default gem with Ruby.
206
+ Moved from standard library to a default gem in Ruby 3.0.
207
+ Requires the `rexml` gem (bundled with Ruby by default).
208
+
157
209
 
158
210
  .Using an XML adapter
159
211
  [source,ruby]
@@ -166,6 +218,8 @@ Lutaml::Model::Config.configure do |config|
166
218
  config.xml_adapter = :oga
167
219
  # or
168
220
  config.xml_adapter = :ox
221
+ # or
222
+ config.xml_adapter = :rexml
169
223
  end
170
224
  ----
171
225
 
@@ -185,7 +239,7 @@ Included in the Ruby standard library.
185
239
  require 'lutaml/model'
186
240
 
187
241
  Lutaml::Model::Config.configure do |config|
188
- config.yaml_adapter = :standard_yaml
242
+ config.yaml_adapter = :standard
189
243
  end
190
244
  ----
191
245
 
@@ -215,7 +269,7 @@ Requires the `oj` gem.
215
269
  require 'lutaml/model'
216
270
 
217
271
  Lutaml::Model::Config.configure do |config|
218
- config.json_adapter = :standard_json
272
+ config.json_adapter = :standard
219
273
  # or
220
274
  config.json_adapter = :multi_json
221
275
  # or
@@ -263,6 +317,3 @@ raised to indicate that the input format is malformed and cannot be parsed.
263
317
  NOTE: The `:tomlib` TOML adapter is disabled on Windows due to segmentation
264
318
  fault issues. Attempting to configure `:tomlib` on Windows will raise an
265
319
  `ArgumentError`. Use `:toml_rb` on Windows instead.
266
-
267
-
268
-
data/docs/index.adoc CHANGED
@@ -16,7 +16,7 @@ Lutaml::Model is a Ruby library for creating information models with attributes
16
16
  == Key features
17
17
 
18
18
  * **Model-driven design** - Define models with attributes and types
19
- * **Multi-format serialization** - XML, JSON, YAML, TOML, Hash
19
+ * **Multi-format serialization** - XML, JSON, YAML, TOML, Hash, YAML Stream
20
20
  * **XML namespace support** - Full W3C namespace implementation
21
21
  * **Validation** - Built-in validation with custom rules
22
22
  * **Schema generation** - Generate XSD, JSON Schema, YAML Schema
@@ -81,6 +81,8 @@ Person.from_json(json_string)
81
81
 
82
82
  Fundamental concepts, configuration, and essential features.
83
83
 
84
+ * link:yamls_sequence.adoc[YAMLS Sequence -- Heterogeneous YAML Stream Support]
85
+
84
86
  === link:tutorials/index[Tutorials]
85
87
 
86
88
  Progressive, step-by-step learning from basics to advanced topics.