lutaml-model 0.8.22 → 0.8.24
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/performance.yml +1 -1
- data/Gemfile +6 -0
- data/README.adoc +16 -7
- data/docs/_guides/index.adoc +4 -0
- data/docs/_guides/native-engines.adoc +61 -0
- data/docs/_guides/xml-mapping.adoc +31 -0
- data/docs/_pages/configuration.adoc +11 -7
- data/docs/_pages/serialization_adapters.adoc +14 -5
- data/lib/compat/opal/lutaml_model_boot.rb +2 -0
- data/lib/lutaml/json/adapter/multi_json_adapter.rb +12 -1
- data/lib/lutaml/json/adapter/oj_adapter.rb +7 -0
- data/lib/lutaml/json/adapter/standard_adapter.rb +17 -5
- data/lib/lutaml/json/adapter/yeptris_adapter.rb +21 -0
- data/lib/lutaml/json/adapter.rb +1 -0
- data/lib/lutaml/json/format.rb +1 -1
- data/lib/lutaml/json/generator_options.rb +62 -0
- data/lib/lutaml/jsonl/adapter/standard_adapter.rb +1 -1
- data/lib/lutaml/jsonld/adapter.rb +12 -4
- data/lib/lutaml/key_value/adapter/json/multi_json_adapter.rb +16 -1
- data/lib/lutaml/key_value/adapter/json/oj_adapter.rb +7 -0
- data/lib/lutaml/key_value/adapter/json/standard_adapter.rb +17 -5
- data/lib/lutaml/key_value/adapter/json/yeptris_adapter.rb +15 -0
- data/lib/lutaml/key_value/adapter/jsonl/standard_adapter.rb +1 -1
- data/lib/lutaml/key_value/adapter/toml/teptris_adapter.rb +42 -0
- data/lib/lutaml/key_value/adapter/toml.rb +1 -0
- data/lib/lutaml/key_value/adapter/yaml/yeptris_adapter.rb +15 -0
- data/lib/lutaml/key_value/transform.rb +82 -0
- data/lib/lutaml/key_value/transformation/value_serializer.rb +25 -0
- data/lib/lutaml/key_value/transformation.rb +25 -1
- data/lib/lutaml/model/adapter_resolver.rb +23 -6
- data/lib/lutaml/model/attribute.rb +31 -6
- data/lib/lutaml/model/collection.rb +5 -0
- data/lib/lutaml/model/configuration.rb +5 -0
- data/lib/lutaml/model/error/unknown_adapter_type_error.rb +4 -4
- data/lib/lutaml/model/serialization_adapter.rb +1 -1
- data/lib/lutaml/model/serialize/format_conversion.rb +75 -7
- data/lib/lutaml/model/serialize.rb +18 -0
- data/lib/lutaml/model/toml.rb +10 -6
- data/lib/lutaml/model/transformation.rb +5 -0
- data/lib/lutaml/model/type/date_time.rb +2 -2
- data/lib/lutaml/model/type/value.rb +24 -2
- data/lib/lutaml/model/utils.rb +6 -5
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/toml/adapter/teptris_adapter.rb +14 -0
- data/lib/lutaml/toml/adapter.rb +1 -0
- data/lib/lutaml/toml/format.rb +1 -1
- data/lib/lutaml/xml/adapter/leptris_adapter.rb +15 -0
- data/lib/lutaml/xml/adapter_element.rb +111 -3
- data/lib/lutaml/xml/builder/base.rb +3 -1
- data/lib/lutaml/xml/builder/leptris.rb +13 -0
- data/lib/lutaml/xml/builder/nokogiri.rb +3 -0
- data/lib/lutaml/xml/builder.rb +1 -0
- data/lib/lutaml/xml/format.rb +4 -1
- data/lib/lutaml/xml/leptris/element.rb +34 -0
- data/lib/lutaml/xml/mapping.rb +24 -0
- data/lib/lutaml/xml/model_transform.rb +52 -1
- data/lib/lutaml/xml/plan_compiler.rb +139 -0
- data/lib/lutaml/xml/plan_hydrator.rb +80 -0
- data/lib/lutaml/xml/transformation/custom_method_wrapper.rb +8 -0
- data/lib/lutaml/xml/transformation.rb +29 -0
- data/lib/lutaml/xml/xml_element.rb +6 -2
- data/lib/lutaml/yaml/adapter/yeptris_adapter.rb +40 -0
- data/lib/lutaml/yaml/adapter.rb +5 -0
- data/lib/lutaml/yaml/format.rb +1 -1
- data/lib/tasks/performance.rake +5 -0
- data/lib/tasks/performance_comparator.rb +19 -3
- data/lib/tasks/performance_probe.rake +33 -0
- data/lib/tasks/performance_probe.rb +106 -0
- data/lib/tasks/performance_probe_xml.rb +54 -0
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/json/adapter/standard_adapter_spec.rb +159 -0
- data/spec/lutaml/json/yeptris_adapter_spec.rb +63 -0
- data/spec/lutaml/jsonld/adapter_spec.rb +19 -0
- data/spec/lutaml/key_value/adapter/toml/teptris_adapter_spec.rb +71 -0
- data/spec/lutaml/model/collection_spec.rb +1 -1
- data/spec/lutaml/model/collection_state_nesting_spec.rb +44 -0
- data/spec/lutaml/model/generator_defaults_spec.rb +101 -0
- data/spec/lutaml/model/json_adapter_spec.rb +6 -0
- data/spec/lutaml/model/json_state_nesting_spec.rb +82 -0
- data/spec/lutaml/model/toml_adapter_spec.rb +8 -0
- data/spec/lutaml/model/toml_spec.rb +36 -1
- data/spec/lutaml/xml/plan_fast_path_spec.rb +96 -0
- data/spec/lutaml/xml/shared_element_rules_spec.rb +64 -0
- data/spec/lutaml/xml/xml_spec.rb +13 -0
- data/spec/lutaml/yaml/yeptris_adapter_spec.rb +63 -0
- data/spec/spec_helper.rb +7 -1
- metadata +29 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8aca94b467fe681429ee9863355555d3aa003d9deda8543a74fd1424087bcc8
|
|
4
|
+
data.tar.gz: 02db8dceef19fc7ef7abd3a54f0936e00ef162a1ee6477191a8b1d3477ba7860
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4da87513ed839ba070c32b28de27411ffe12ccc92f879a0dbe433aad85ea3a32a76759b36112958a131b09a747b1618c0ff8a95e92238aca40f45b736dd46950
|
|
7
|
+
data.tar.gz: ff488be2b79ef92c3017d63ee0a9bf4ac3b183549be5b17a59efcb42ea31ae00cacb2b63995f9b69fbe3628af9bd65f30dfedb2fb5d2722fe15189c7e964a93b
|
data/Gemfile
CHANGED
|
@@ -19,6 +19,7 @@ gem "benchmark-ips"
|
|
|
19
19
|
gem "bigdecimal"
|
|
20
20
|
gem "canon" # , path: "../canon"
|
|
21
21
|
gem "json-ld"
|
|
22
|
+
gem "leptris"
|
|
22
23
|
gem "liquid", "~> 5"
|
|
23
24
|
gem "multi_json"
|
|
24
25
|
gem "nokogiri"
|
|
@@ -26,8 +27,11 @@ gem "oj"
|
|
|
26
27
|
gem "openssl", "~> 3.0"
|
|
27
28
|
gem "ox"
|
|
28
29
|
gem "rake"
|
|
30
|
+
# yeptris ships prebuilt platform gems for linux/darwin only; a source
|
|
31
|
+
# build is not wired for windows runners.
|
|
29
32
|
gem "rdf-turtle"
|
|
30
33
|
gem "rexml"
|
|
34
|
+
gem "yeptris" unless Gem.win_platform?
|
|
31
35
|
# TODO: revert rng branch to main when lutaml/rng#32 is merged
|
|
32
36
|
gem "rng", git: "https://github.com/lutaml/rng", branch: "main"
|
|
33
37
|
gem "rspec"
|
|
@@ -35,6 +39,8 @@ gem "rubocop"
|
|
|
35
39
|
gem "rubocop-performance", require: false
|
|
36
40
|
gem "rubocop-rake", require: false
|
|
37
41
|
gem "rubocop-rspec", require: false
|
|
42
|
+
# teptris ships prebuilt gems for every platform (incl. mingw-ucrt)
|
|
43
|
+
gem "teptris"
|
|
38
44
|
gem "tomlib"
|
|
39
45
|
gem "toml-rb"
|
|
40
46
|
|
data/README.adoc
CHANGED
|
@@ -16223,7 +16223,7 @@ Adapters are auto-detected on first use. The defaults when gems are available:
|
|
|
16223
16223
|
* YAML: `:standard` (alias: `:standard_yaml`)
|
|
16224
16224
|
* JSON: `:standard` (alias: `:standard_json`)
|
|
16225
16225
|
* Hash: `:standard` (alias: `:standard_hash`)
|
|
16226
|
-
* TOML: `:tomlib` on non-Windows, `:toml_rb` on Windows
|
|
16226
|
+
* TOML: `:teptris` if available, else `:tomlib` on non-Windows, `:toml_rb` on Windows
|
|
16227
16227
|
|
|
16228
16228
|
|
|
16229
16229
|
==== Configure adapters through symbol choices
|
|
@@ -16246,7 +16246,7 @@ Lutaml::Model::Config.configure do |config|
|
|
|
16246
16246
|
config.hash_adapter = :standard
|
|
16247
16247
|
config.yaml_adapter = :standard
|
|
16248
16248
|
config.json_adapter = :standard # can be one of [:standard, :multi_json, :oj]
|
|
16249
|
-
config.toml_adapter = :toml_rb # can be one of [:toml_rb, :tomlib]
|
|
16249
|
+
config.toml_adapter = :toml_rb # can be one of [:teptris, :toml_rb, :tomlib]
|
|
16250
16250
|
end
|
|
16251
16251
|
----
|
|
16252
16252
|
|
|
@@ -16388,22 +16388,29 @@ end
|
|
|
16388
16388
|
|
|
16389
16389
|
Lutaml::Model supports the following TOML adapters:
|
|
16390
16390
|
|
|
16391
|
+
Teptris::
|
|
16392
|
+
(default when the `teptris` gem is in the bundle — every platform)
|
|
16393
|
+
The native TOML engine (libteptris via FFI). Value semantics mirror
|
|
16394
|
+
tomlib: offset and local datetimes become `Time`, dates become `Date`,
|
|
16395
|
+
local times stay `String`.
|
|
16396
|
+
Requires the `teptris` gem.
|
|
16397
|
+
|
|
16391
16398
|
Toml-rb::
|
|
16392
|
-
(default on Windows)
|
|
16399
|
+
(default on Windows without teptris)
|
|
16393
16400
|
A TOML parser and serializer for Ruby that is compatible with the TOML v1.0.0
|
|
16394
16401
|
specification.
|
|
16395
16402
|
Requires the `toml-rb` gem.
|
|
16396
16403
|
|
|
16397
16404
|
Tomlib::
|
|
16398
|
-
(default on non-Windows platforms)
|
|
16405
|
+
(default on non-Windows platforms without teptris)
|
|
16399
16406
|
Toml-rb fork that is compatible with the TOML v1.0.0 specification, but with
|
|
16400
16407
|
additional features.
|
|
16401
16408
|
Requires the `tomlib` gem.
|
|
16402
16409
|
|
|
16403
16410
|
IMPORTANT: The Tomlib adapter is not available on Windows due to segmentation
|
|
16404
|
-
fault issues. On Windows, the `
|
|
16405
|
-
default. If you explicitly
|
|
16406
|
-
`ConfigurationError` will be raised.
|
|
16411
|
+
fault issues. On Windows, the `teptris` adapter (when bundled) or the
|
|
16412
|
+
`toml-rb` adapter is used automatically as the default. If you explicitly
|
|
16413
|
+
configure `tomlib` on Windows, a `ConfigurationError` will be raised.
|
|
16407
16414
|
|
|
16408
16415
|
|
|
16409
16416
|
.Using a TOML adapter
|
|
@@ -16412,6 +16419,8 @@ default. If you explicitly configure `tomlib` on Windows, a
|
|
|
16412
16419
|
require 'lutaml/model'
|
|
16413
16420
|
|
|
16414
16421
|
Lutaml::Model::Config.configure do |config|
|
|
16422
|
+
config.toml_adapter = :teptris
|
|
16423
|
+
# or
|
|
16415
16424
|
config.toml_adapter = :toml_rb
|
|
16416
16425
|
# or (not available on Windows)
|
|
16417
16426
|
config.toml_adapter = :tomlib
|
data/docs/_guides/index.adoc
CHANGED
|
@@ -22,6 +22,10 @@ Task-oriented guides for accomplishing specific goals with Lutaml::Model.
|
|
|
22
22
|
* link:../keyvalue-serialization[Key-Value Serialization] - JSON/YAML/TOML/Hash
|
|
23
23
|
* link:../collection-serialization[Collection Serialization] - JSONL and YAML Stream
|
|
24
24
|
|
|
25
|
+
== Performance
|
|
26
|
+
|
|
27
|
+
* link:../native-engines[Native engines (leptris / yeptris / teptris)] - Drop-in XML/YAML/JSON/TOML engine upgrades via bundle opt-in
|
|
28
|
+
|
|
25
29
|
== Linked Data serialization
|
|
26
30
|
|
|
27
31
|
* link:../rdf-serialization[Unified RDF Serialization] - One `rdf` block for both JSON-LD and Turtle
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
= Native engines (leptris / yeptris / teptris)
|
|
2
|
+
|
|
3
|
+
By default, `lutaml-model` serializes through the same engines every Ruby
|
|
4
|
+
install already has: Nokogiri (via moxml) for XML, Psych for YAML, the
|
|
5
|
+
`json` gem for JSON.
|
|
6
|
+
|
|
7
|
+
The leptris family of native engines is a drop-in performance upgrade:
|
|
8
|
+
|
|
9
|
+
* **leptris** — the XML engine moxml drives. When the `leptris` gem is in
|
|
10
|
+
your bundle, moxml prefers it over Nokogiri for parsing and
|
|
11
|
+
serialization.
|
|
12
|
+
* **yeptris** — the YAML/JSON engine. When the `yeptris` gem is in your
|
|
13
|
+
bundle, lutaml-model's YAML and JSON adapters resolve to the yeptris
|
|
14
|
+
engine.
|
|
15
|
+
* **teptris** — the TOML engine. When the `teptris` gem is in your
|
|
16
|
+
bundle it becomes the TOML default on every platform (0.2.12+ parses
|
|
17
|
+
~3x and dumps ~8x faster than tomlib, and the Windows mingw-ucrt
|
|
18
|
+
prebuilts are back), giving Windows native TOML and retiring the
|
|
19
|
+
tomlib-segfault workaround (previously pure-Ruby toml-rb only).
|
|
20
|
+
|
|
21
|
+
Opting in is purely a bundle decision — no code changes, no gemspec
|
|
22
|
+
requirements:
|
|
23
|
+
|
|
24
|
+
[source,sh]
|
|
25
|
+
----
|
|
26
|
+
bundle add leptris yeptris teptris
|
|
27
|
+
----
|
|
28
|
+
|
|
29
|
+
leptris and yeptris ship prebuilt platform gems for Linux and macOS
|
|
30
|
+
(yeptris has no Windows prebuilts). teptris ships prebuilt gems for
|
|
31
|
+
every platform, including Windows. When a gem is absent, lutaml-model
|
|
32
|
+
automatically falls back to the standard engine for that format.
|
|
33
|
+
|
|
34
|
+
== Selecting adapters explicitly
|
|
35
|
+
|
|
36
|
+
The engines register as ordinary adapter types, so explicit selection and
|
|
37
|
+
the availability error messages work as usual:
|
|
38
|
+
|
|
39
|
+
[source,ruby]
|
|
40
|
+
----
|
|
41
|
+
Lutaml::Model::Config.xml_adapter_type = :leptris
|
|
42
|
+
Lutaml::Model::Config.yaml_adapter_type = :yeptris
|
|
43
|
+
Lutaml::Model::Config.json_adapter_type = :yeptris
|
|
44
|
+
Lutaml::Model::Config.toml_adapter_type = :teptris
|
|
45
|
+
----
|
|
46
|
+
|
|
47
|
+
== Semantics notes
|
|
48
|
+
|
|
49
|
+
* **YAML**: the yeptris adapter uses the engine's `compat_11` schema, so
|
|
50
|
+
implicit typing matches Psych (parity-verified across the scalar
|
|
51
|
+
battery: dates, timestamps, octal, sexagesimal, `yes`/`no`, `.inf`).
|
|
52
|
+
Two deliberate differences: anchors and aliases always resolve, and a
|
|
53
|
+
tagged value without a core type materializes as plain data instead of
|
|
54
|
+
raising `Psych::DisallowedClass`.
|
|
55
|
+
* **JSON**: `yeptris` parsing targets exact `JSON.parse` semantics;
|
|
56
|
+
generation stays on the `json` gem.
|
|
57
|
+
* **TOML**: the teptris adapter mirrors tomlib value semantics — offset
|
|
58
|
+
and local datetimes become `Time`, dates become `Date`, local times
|
|
59
|
+
stay `String`. Parse failures raise `Teptris::ParseError` with line
|
|
60
|
+
and column, wrapped as `InvalidFormatError` like the other TOML
|
|
61
|
+
adapters.
|
|
@@ -717,6 +717,37 @@ end
|
|
|
717
717
|
----
|
|
718
718
|
====
|
|
719
719
|
|
|
720
|
+
===== Mapping several element names to one attribute
|
|
721
|
+
|
|
722
|
+
Several `map_element` rules may target the same collection attribute to
|
|
723
|
+
accept multiple spellings of one element (e.g. a legacy and a modern
|
|
724
|
+
name). Parsing merges the content of every spelling **in document
|
|
725
|
+
order**; serialization emits the value once, under the **first declared**
|
|
726
|
+
spelling.
|
|
727
|
+
|
|
728
|
+
[source,ruby]
|
|
729
|
+
----
|
|
730
|
+
class Editorial < Lutaml::Model::Serializable
|
|
731
|
+
attribute :groups, Group, collection: true
|
|
732
|
+
|
|
733
|
+
xml do
|
|
734
|
+
element "metadata"
|
|
735
|
+
map_element "editorial-group", to: :groups
|
|
736
|
+
map_element "editorialgroup", to: :groups # legacy spelling
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
Editorial.from_xml(
|
|
741
|
+
"<metadata><editorialgroup>…</editorialgroup><editorial-group>…</editorial-group></metadata>"
|
|
742
|
+
).groups
|
|
743
|
+
# => [group(legacy), group(modern)] — document order
|
|
744
|
+
----
|
|
745
|
+
|
|
746
|
+
NOTE: The merge applies to plain element rules on collection attributes.
|
|
747
|
+
Rules with custom deserialization methods, `raw`, `content`, or `cdata`
|
|
748
|
+
are not merged — each still assigns separately, last rule winning as
|
|
749
|
+
before.
|
|
750
|
+
|
|
720
751
|
===== Using elements in different namespaces
|
|
721
752
|
|
|
722
753
|
====== General
|
|
@@ -23,7 +23,7 @@ The auto-detection order for each format:
|
|
|
23
23
|
|===
|
|
24
24
|
| Format | Detection order
|
|
25
25
|
| XML | `:nokogiri` → `:ox` → `:oga` → `:rexml`
|
|
26
|
-
| TOML | `:tomlib` → `:toml_rb` (Windows: `:toml_rb`
|
|
26
|
+
| TOML | `:teptris` → `:tomlib` → `:toml_rb` (Windows: `:teptris` → `:toml_rb`)
|
|
27
27
|
| JSON | `:standard` (always available)
|
|
28
28
|
| YAML | `:standard` (always available)
|
|
29
29
|
| Hash | `:standard` (always available)
|
|
@@ -109,14 +109,17 @@ When an adapter is needed for a format, `AdapterResolver` follows this chain:
|
|
|
109
109
|
|===
|
|
110
110
|
| Adapter | Description
|
|
111
111
|
|
|
112
|
+
| `:teptris` (default when bundled)
|
|
113
|
+
| Native engine (libteptris via FFI), all platforms including Windows. tomlib-compatible value semantics.
|
|
114
|
+
|
|
112
115
|
| `:tomlib` (default on non-Windows)
|
|
113
116
|
| Enhanced fork. Better performance on non-Windows platforms.
|
|
114
117
|
|
|
115
|
-
| `:toml_rb` (default on Windows)
|
|
116
|
-
| Pure Ruby. TOML v1.0.0 compatible.
|
|
118
|
+
| `:toml_rb` (default on Windows without teptris)
|
|
119
|
+
| Pure Ruby. TOML v1.0.0 compatible.
|
|
117
120
|
|===
|
|
118
121
|
|
|
119
|
-
NOTE: The `:tomlib` adapter is not available on Windows due to segmentation fault issues. On Windows, `:toml_rb` is
|
|
122
|
+
NOTE: The `:tomlib` adapter is not available on Windows due to segmentation fault issues. On Windows, `:teptris` (when bundled) or `:toml_rb` is used as the default.
|
|
120
123
|
|
|
121
124
|
== Per-operation adapter override
|
|
122
125
|
|
|
@@ -226,7 +229,7 @@ If not explicitly configured, Lutaml::Model auto-detects on first use:
|
|
|
226
229
|
* JSON: `:standard` (alias: `:standard_json`)
|
|
227
230
|
* YAML: `:standard` (alias: `:standard_yaml`)
|
|
228
231
|
* Hash: `:standard` (alias: `:standard_hash`)
|
|
229
|
-
* TOML: `:tomlib` on non-Windows, `:toml_rb` on Windows
|
|
232
|
+
* TOML: `:teptris` if available, else `:tomlib` on non-Windows, `:toml_rb` on Windows
|
|
230
233
|
|
|
231
234
|
== When to configure
|
|
232
235
|
|
|
@@ -272,8 +275,9 @@ end
|
|
|
272
275
|
|
|
273
276
|
=== Choose TOML adapter based on
|
|
274
277
|
|
|
275
|
-
* **
|
|
276
|
-
* **
|
|
278
|
+
* **Teptris**: Bundled native engine — ~3x faster parse, ~8x faster dump than tomlib, every platform
|
|
279
|
+
* **Tomlib**: Non-Windows projects without teptris (better performance)
|
|
280
|
+
* **TomlRb**: Windows platforms without teptris (required due to tomlib incompatibility)
|
|
277
281
|
|
|
278
282
|
== Opal runtime compatibility
|
|
279
283
|
|
|
@@ -48,7 +48,7 @@ more information.
|
|
|
48
48
|
Adapters are resolved lazily on first use. If no adapter is explicitly configured, `AdapterResolver` probes for available gems in a preferred order:
|
|
49
49
|
|
|
50
50
|
* **XML**: `:nokogiri` → `:ox` → `:oga` → `:rexml`
|
|
51
|
-
* **TOML**: `:tomlib` → `:toml_rb` (Windows: `:toml_rb`
|
|
51
|
+
* **TOML**: `:teptris` → `:tomlib` → `:toml_rb` (Windows: `:teptris` → `:toml_rb`)
|
|
52
52
|
* **JSON/YAML/Hash**: `:standard` (always available)
|
|
53
53
|
|
|
54
54
|
The detection result is cached after the first probe, so it only runs once per format.
|
|
@@ -84,7 +84,7 @@ There is a default configuration for adapters for commonly used formats:
|
|
|
84
84
|
* YAML: `:standard` (alias: `:standard_yaml`)
|
|
85
85
|
* JSON: `:standard` (alias: `:standard_json`)
|
|
86
86
|
* Hash: `:standard` (alias: `:standard_hash`)
|
|
87
|
-
* TOML: `:tomlib` on non-Windows, `:toml_rb` on Windows
|
|
87
|
+
* TOML: `:teptris` if available, else `:tomlib` on non-Windows, `:toml_rb` on Windows
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
==== Configure adapters through symbol choices
|
|
@@ -107,7 +107,7 @@ Lutaml::Model::Config.configure do |config|
|
|
|
107
107
|
config.hash_adapter = :standard
|
|
108
108
|
config.yaml_adapter = :standard
|
|
109
109
|
config.json_adapter = :standard # can be one of [:standard, :multi_json, :oj]
|
|
110
|
-
config.toml_adapter = :toml_rb # can be one of [:toml_rb, :tomlib] (tomlib not available on Windows)
|
|
110
|
+
config.toml_adapter = :toml_rb # can be one of [:teptris, :toml_rb, :tomlib] (tomlib not available on Windows)
|
|
111
111
|
end
|
|
112
112
|
----
|
|
113
113
|
|
|
@@ -285,8 +285,16 @@ end
|
|
|
285
285
|
|
|
286
286
|
Lutaml::Model supports the following TOML adapters:
|
|
287
287
|
|
|
288
|
+
Teptris::
|
|
289
|
+
(default when the `teptris` gem is in the bundle — every platform)
|
|
290
|
+
The native TOML engine (libteptris via FFI). Value semantics mirror
|
|
291
|
+
tomlib: offset and local datetimes become `Time`, dates become `Date`,
|
|
292
|
+
local times stay `String`. Parse failures raise `Teptris::ParseError`
|
|
293
|
+
with line and column.
|
|
294
|
+
Requires the `teptris` gem.
|
|
295
|
+
|
|
288
296
|
Tomlib::
|
|
289
|
-
(default on non-Windows)
|
|
297
|
+
(default on non-Windows when teptris is absent)
|
|
290
298
|
Toml-rb fork that is compatible with the TOML v1.0.0 specification, with
|
|
291
299
|
additional features and better performance.
|
|
292
300
|
Requires the `tomlib` gem.
|
|
@@ -348,4 +356,5 @@ raised to indicate that the input format is malformed and cannot be parsed.
|
|
|
348
356
|
|
|
349
357
|
NOTE: The `:tomlib` TOML adapter is disabled on Windows due to segmentation
|
|
350
358
|
fault issues. Attempting to configure `:tomlib` on Windows will raise an
|
|
351
|
-
`ArgumentError`. Use `:toml_rb` on
|
|
359
|
+
`ArgumentError`. Use `:teptris` (native, all platforms) or `:toml_rb` on
|
|
360
|
+
Windows instead.
|
|
@@ -167,6 +167,8 @@ require "lutaml/xml/oga"
|
|
|
167
167
|
require "lutaml/xml/parse_session"
|
|
168
168
|
require "lutaml/xml/parsed_namespace_declaration"
|
|
169
169
|
require "lutaml/xml/parsed_namespace_set"
|
|
170
|
+
require "lutaml/xml/plan_compiler"
|
|
171
|
+
require "lutaml/xml/plan_hydrator"
|
|
170
172
|
require "lutaml/xml/polymorphic_value_handler"
|
|
171
173
|
require "lutaml/xml/qualified_inheritance_strategy"
|
|
172
174
|
require "lutaml/xml/rexml"
|
|
@@ -16,6 +16,8 @@ module Lutaml
|
|
|
16
16
|
"multi_json gem is not available. Please add 'multi_json' to your Gemfile."
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
INTERNAL_LUTAML_KEYS = %i[register adapter _adapter_override].freeze
|
|
20
|
+
|
|
19
21
|
# rubocop:disable Style/ArgumentsForwarding -- anonymous * requires Ruby 3.2+
|
|
20
22
|
def to_json(*args)
|
|
21
23
|
require "multi_json"
|
|
@@ -28,7 +30,16 @@ module Lutaml
|
|
|
28
30
|
@attributes
|
|
29
31
|
end
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
# LutaML's own threading keys must not reach the backend; the
|
|
34
|
+
# rest (e.g. :pretty) are meaningful to multi_json. The json
|
|
35
|
+
# gem 3.x and Oj raise on unknown generator options, unlike
|
|
36
|
+
# json 2.x which ignored them.
|
|
37
|
+
dump_args = args.map do |arg|
|
|
38
|
+
next arg unless arg.is_a?(::Hash)
|
|
39
|
+
|
|
40
|
+
arg.except(*INTERNAL_LUTAML_KEYS)
|
|
41
|
+
end
|
|
42
|
+
MultiJson.dump(attributes_to_serialize, *dump_args)
|
|
32
43
|
# rubocop:enable Style/ArgumentsForwarding
|
|
33
44
|
rescue LoadError
|
|
34
45
|
raise LoadError,
|
|
@@ -17,6 +17,13 @@ module Lutaml
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# rubocop:disable Style/ArgumentsForwarding -- anonymous * requires Ruby 3.2+
|
|
20
|
+
# Oj has its own option namespace and its own escape_mode. Handing it
|
|
21
|
+
# the stdlib generator's options overrides that configuration, so it
|
|
22
|
+
# takes none of them -- which is what it received before.
|
|
23
|
+
def ignores_generator_options?
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
20
27
|
def to_json(*args)
|
|
21
28
|
require "oj"
|
|
22
29
|
# Handle KeyValueElement input (new symmetric architecture)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
+
require_relative "../generator_options"
|
|
4
5
|
|
|
5
6
|
module Lutaml
|
|
6
7
|
module Json
|
|
@@ -9,12 +10,16 @@ module Lutaml
|
|
|
9
10
|
FORMAT_SYMBOL = :json
|
|
10
11
|
|
|
11
12
|
def self.parse(json, _options = {})
|
|
12
|
-
JSON.parse(json
|
|
13
|
+
JSON.parse(json)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
# This adapter hands its payload to the stdlib generator, so a
|
|
17
|
+
# JSON::State carrying the outer formatting is meaningful here.
|
|
18
|
+
def accepts_generator_state?
|
|
19
|
+
true
|
|
20
|
+
end
|
|
17
21
|
|
|
22
|
+
def to_json(*args)
|
|
18
23
|
# Handle KeyValueElement input (new symmetric architecture)
|
|
19
24
|
attributes_to_serialize = if @attributes.is_a?(Lutaml::KeyValue::DataModel::Element)
|
|
20
25
|
# Unwrap __root__ wrapper to get actual content
|
|
@@ -24,10 +29,17 @@ module Lutaml
|
|
|
24
29
|
@attributes
|
|
25
30
|
end
|
|
26
31
|
|
|
32
|
+
unless GeneratorOptions.lutaml_options?(args.first)
|
|
33
|
+
return JSON.generate(attributes_to_serialize, args.first)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
options = args.first || {}
|
|
37
|
+
generator_options = GeneratorOptions.filter(options)
|
|
38
|
+
|
|
27
39
|
if options[:pretty]
|
|
28
|
-
JSON.pretty_generate(attributes_to_serialize,
|
|
40
|
+
JSON.pretty_generate(attributes_to_serialize, generator_options)
|
|
29
41
|
else
|
|
30
|
-
JSON.generate(attributes_to_serialize,
|
|
42
|
+
JSON.generate(attributes_to_serialize, generator_options)
|
|
31
43
|
end
|
|
32
44
|
end
|
|
33
45
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yeptris"
|
|
4
|
+
require_relative "standard_adapter"
|
|
5
|
+
|
|
6
|
+
module Lutaml
|
|
7
|
+
module Json
|
|
8
|
+
module Adapter
|
|
9
|
+
# JSON parsing over the yeptris engine: Yeptris::JSON.load targets
|
|
10
|
+
# exact JSON.parse semantics (spec-pinned upstream), with a fused
|
|
11
|
+
# native materializer when the loaded libyeptris build carries it.
|
|
12
|
+
# Generation stays on the json gem (the yeptris JSON surface is
|
|
13
|
+
# load-only), so everything else inherits from the standard adapter.
|
|
14
|
+
class YeptrisAdapter < StandardAdapter
|
|
15
|
+
def self.parse(json, _options = {})
|
|
16
|
+
::Yeptris::JSON.load(json)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/lutaml/json/adapter.rb
CHANGED
data/lib/lutaml/json/format.rb
CHANGED
|
@@ -52,7 +52,7 @@ Lutaml::Model::FormatRegistry.register(
|
|
|
52
52
|
transformer: Lutaml::Json::Adapter::Transform,
|
|
53
53
|
key_value: true,
|
|
54
54
|
adapter_options: {
|
|
55
|
-
available: %i[standard standard_json multi_json oj],
|
|
55
|
+
available: %i[standard standard_json multi_json oj yeptris],
|
|
56
56
|
default: :standard,
|
|
57
57
|
},
|
|
58
58
|
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Lutaml
|
|
6
|
+
module Json
|
|
7
|
+
# Filters the options LutaML threads through serialization down to the
|
|
8
|
+
# subset the JSON generator accepts.
|
|
9
|
+
#
|
|
10
|
+
# json 2.x silently ignored unknown generator options; json 3.0 raises
|
|
11
|
+
# ArgumentError on them, so LutaML's own options (:register, :pretty and
|
|
12
|
+
# any caller-supplied extras) have to be stripped before they reach
|
|
13
|
+
# JSON.generate.
|
|
14
|
+
module GeneratorOptions
|
|
15
|
+
# Options the generator accepts on both json 2.x and 3.x. Measured
|
|
16
|
+
# against 2.9.1, 2.15.2, 2.19.9, 2.20.0, 2.21.1, 2.21.2 and 3.0.0.
|
|
17
|
+
# :escape_slash is NOT here because json 3.0 removed it; on 2.x it still
|
|
18
|
+
# arrives via DERIVED_PERMITTED. :allow_duplicate_key IS here because the
|
|
19
|
+
# generator accepts it on every version while JSON::State never exposed
|
|
20
|
+
# it as an accessor, so the derived half alone would miss it.
|
|
21
|
+
BASE_PERMITTED = %i[
|
|
22
|
+
allow_duplicate_key allow_nan array_nl as_json ascii_only
|
|
23
|
+
buffer_initial_length depth indent max_nesting object_nl script_safe
|
|
24
|
+
sort_keys space space_before strict
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
# Additive, so a json release that adds an option needs no change here.
|
|
28
|
+
# Opal's JSON shim has no State class, hence the guard.
|
|
29
|
+
DERIVED_PERMITTED =
|
|
30
|
+
if defined?(::JSON::State)
|
|
31
|
+
accessors = ::JSON::State.instance_methods(false).grep(/\A[a-z_][a-z0-9_]*=\z/)
|
|
32
|
+
accessors.map { |name| name.to_s.chomp("=").to_sym }
|
|
33
|
+
else
|
|
34
|
+
[]
|
|
35
|
+
end.freeze
|
|
36
|
+
|
|
37
|
+
PERMITTED = (BASE_PERMITTED | DERIVED_PERMITTED).freeze
|
|
38
|
+
|
|
39
|
+
# json 3.0 removed :escape_slash, which was only ever an alias of
|
|
40
|
+
# :script_safe. Dropping it would silently stop escaping slashes, so it
|
|
41
|
+
# is translated instead of discarded.
|
|
42
|
+
RENAMED = { escape_slash: :script_safe }.freeze
|
|
43
|
+
|
|
44
|
+
def self.filter(options)
|
|
45
|
+
return {} unless options.is_a?(::Hash)
|
|
46
|
+
|
|
47
|
+
options.each_with_object({}) do |(key, value), kept|
|
|
48
|
+
key = RENAMED.fetch(key, key) unless PERMITTED.include?(key)
|
|
49
|
+
kept[key] = value if PERMITTED.include?(key)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Ruby's JSON generator calls #to_json with a JSON::State whenever a
|
|
54
|
+
# document is nested inside another JSON.generate call. That is the
|
|
55
|
+
# generator's own state, not a LutaML options hash, and json 3.0 removed
|
|
56
|
+
# JSON::State#[], so it must never be read like one.
|
|
57
|
+
def self.lutaml_options?(argument)
|
|
58
|
+
argument.nil? || argument.is_a?(::Hash)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
+
require_relative "../json/generator_options"
|
|
4
5
|
|
|
5
6
|
module Lutaml
|
|
6
7
|
module JsonLd
|
|
7
8
|
class Adapter < Lutaml::KeyValue::Document
|
|
8
9
|
def self.parse(jsonld_string, _options = {})
|
|
9
|
-
JSON.parse(jsonld_string
|
|
10
|
+
JSON.parse(jsonld_string)
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def to_jsonld(*args)
|
|
13
|
-
options = args.first || {}
|
|
14
14
|
data = @attributes
|
|
15
|
+
|
|
16
|
+
unless Lutaml::Json::GeneratorOptions.lutaml_options?(args.first)
|
|
17
|
+
return JSON.generate(data, args.first)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
options = args.first || {}
|
|
21
|
+
generator_options = Lutaml::Json::GeneratorOptions.filter(options)
|
|
22
|
+
|
|
15
23
|
if options[:pretty]
|
|
16
|
-
JSON.pretty_generate(data,
|
|
24
|
+
JSON.pretty_generate(data, generator_options)
|
|
17
25
|
else
|
|
18
|
-
JSON.generate(data,
|
|
26
|
+
JSON.generate(data, generator_options)
|
|
19
27
|
end
|
|
20
28
|
end
|
|
21
29
|
end
|
|
@@ -20,6 +20,8 @@ module Lutaml
|
|
|
20
20
|
"multi_json gem is not available. Please add 'multi_json' to your Gemfile."
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
INTERNAL_LUTAML_KEYS = %i[register adapter _adapter_override].freeze
|
|
24
|
+
|
|
23
25
|
# rubocop:disable Style/ArgumentsForwarding -- anonymous * requires Ruby 3.2+
|
|
24
26
|
def to_json(*args)
|
|
25
27
|
require "multi_json"
|
|
@@ -32,7 +34,20 @@ module Lutaml
|
|
|
32
34
|
@attributes
|
|
33
35
|
end
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
# LutaML's own options must not reach the backend: the json
|
|
38
|
+
# gem 3.x and Oj both raise on unknown generator options
|
|
39
|
+
# (multi_json's backend choice shifts with the resolved json
|
|
40
|
+
# version), unlike json 2.x which ignored them.
|
|
41
|
+
# LutaML's own threading keys must not reach the backend; the
|
|
42
|
+
# rest (e.g. :pretty) are meaningful to multi_json. The json
|
|
43
|
+
# gem 3.x and Oj raise on unknown generator options, unlike
|
|
44
|
+
# json 2.x which ignored them.
|
|
45
|
+
dump_args = args.map do |arg|
|
|
46
|
+
next arg unless arg.is_a?(::Hash)
|
|
47
|
+
|
|
48
|
+
arg.except(*INTERNAL_LUTAML_KEYS)
|
|
49
|
+
end
|
|
50
|
+
MultiJson.dump(attributes_to_serialize, *dump_args)
|
|
36
51
|
# rubocop:enable Style/ArgumentsForwarding
|
|
37
52
|
rescue LoadError
|
|
38
53
|
raise LoadError,
|
|
@@ -21,6 +21,13 @@ module Lutaml
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# rubocop:disable Style/ArgumentsForwarding -- anonymous * requires Ruby 3.2+
|
|
24
|
+
# Oj has its own option namespace and its own escape_mode. Handing it
|
|
25
|
+
# the stdlib generator's options overrides that configuration, so it
|
|
26
|
+
# takes none of them -- which is what it received before.
|
|
27
|
+
def ignores_generator_options?
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
|
|
24
31
|
def to_json(*args)
|
|
25
32
|
require "oj"
|
|
26
33
|
# Handle KeyValueElement input (new symmetric architecture)
|