lutaml-model 0.8.23 → 0.8.28
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/_pages/configuration.adoc +11 -7
- data/docs/_pages/serialization_adapters.adoc +14 -5
- data/lib/compat/opal/lutaml_model_boot.rb +5 -0
- data/lib/lutaml/json/adapter/multi_json_adapter.rb +12 -1
- 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/key_value/adapter/json/multi_json_adapter.rb +16 -1
- data/lib/lutaml/key_value/adapter/json/yeptris_adapter.rb +15 -0
- 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/compiled_rule.rb +16 -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/serialize/format_conversion.rb +65 -0
- data/lib/lutaml/model/toml.rb +10 -6
- 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/declaration_planner.rb +8 -2
- data/lib/lutaml/xml/format.rb +7 -1
- data/lib/lutaml/xml/leptris/element.rb +34 -0
- data/lib/lutaml/xml/namespace_collector.rb +4 -3
- data/lib/lutaml/xml/plan_compiler.rb +253 -0
- data/lib/lutaml/xml/plan_hydrator.rb +341 -0
- data/lib/lutaml/xml/plan_order.rb +50 -0
- data/lib/lutaml/xml/plan_serializer.rb +241 -0
- data/lib/lutaml/xml/plan_walk.rb +30 -0
- data/lib/lutaml/xml/transformation/custom_method_wrapper.rb +8 -0
- data/lib/lutaml/xml/xml_element.rb +6 -2
- data/lib/lutaml/yaml/adapter/yeptris_adapter.rb +46 -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_plan_walk.rb +58 -0
- data/lib/tasks/performance_probe_xml.rb +54 -0
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/json/yeptris_adapter_spec.rb +63 -0
- data/spec/lutaml/key_value/adapter/toml/teptris_adapter_spec.rb +71 -0
- data/spec/lutaml/model/json_adapter_spec.rb +6 -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 +547 -0
- data/spec/lutaml/xml/plan_walk_spec.rb +102 -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 +28 -4
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.5.
|
|
41
|
+
spec.add_dependency "moxml", "~> 0.5.56"
|
|
42
42
|
spec.add_dependency "ostruct"
|
|
43
43
|
spec.add_dependency "rubyzip", "~> 2.3"
|
|
44
44
|
spec.add_dependency "thor"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
begin
|
|
5
|
+
require "lutaml/json/adapter/yeptris_adapter"
|
|
6
|
+
rescue LoadError
|
|
7
|
+
# yeptris ships no platform gem here (e.g. Windows): every example skips.
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
YEPTRIS_AVAILABLE = defined?(Yeptris)
|
|
11
|
+
|
|
12
|
+
RSpec.describe(YEPTRIS_AVAILABLE ? Lutaml::Json::Adapter::YeptrisAdapter : Object) do
|
|
13
|
+
before { skip "yeptris is not available on this platform" unless YEPTRIS_AVAILABLE }
|
|
14
|
+
|
|
15
|
+
let(:attributes) { { "name" => "John", "age" => 30, "roles" => %w[admin dev] } }
|
|
16
|
+
|
|
17
|
+
describe ".parse" do
|
|
18
|
+
it "parses a JSON document into a hash" do
|
|
19
|
+
expect(described_class.parse('{"name":"John","age":30,"roles":["admin","dev"]}'))
|
|
20
|
+
.to eq(attributes)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "parses scalars and nulls like JSON.parse" do
|
|
24
|
+
expect(described_class.parse('{"a":1.5,"b":null,"c":true,"d":"1e3"}'))
|
|
25
|
+
.to eq("a" => 1.5, "b" => nil, "c" => true, "d" => "1e3")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "raises on invalid JSON" do
|
|
29
|
+
expect { described_class.parse("{nope") }
|
|
30
|
+
.to raise_error(Yeptris::JSON::ParseError)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "#to_json" do
|
|
35
|
+
subject(:document) { described_class.new(attributes) }
|
|
36
|
+
|
|
37
|
+
it "serializes via the inherited json generator" do
|
|
38
|
+
expect(document.to_json)
|
|
39
|
+
.to eq('{"name":"John","age":30,"roles":["admin","dev"]}')
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "round trip through a model" do
|
|
44
|
+
let(:model_class) do
|
|
45
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
46
|
+
attribute :name, :string
|
|
47
|
+
attribute :roles, :string, collection: true
|
|
48
|
+
|
|
49
|
+
key_value do
|
|
50
|
+
map "name", to: :name
|
|
51
|
+
map "roles", to: :roles
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "deserializes via the yeptris engine and serializes back" do
|
|
57
|
+
model = model_class.from_json('{"name":"John","roles":["admin","dev"]}')
|
|
58
|
+
expect(model.name).to eq("John")
|
|
59
|
+
expect(model.roles).to eq(%w[admin dev])
|
|
60
|
+
expect(model_class.from_json(model.to_json).roles).to eq(%w[admin dev])
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "lutaml/key_value/adapter/toml/teptris_adapter"
|
|
5
|
+
begin
|
|
6
|
+
require "teptris"
|
|
7
|
+
rescue LoadError
|
|
8
|
+
# teptris is not resolvable here (absent or broken native library):
|
|
9
|
+
# every example skips.
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
TEPTRIS_AVAILABLE = defined?(Teptris)
|
|
13
|
+
|
|
14
|
+
RSpec.describe(TEPTRIS_AVAILABLE ? Lutaml::KeyValue::Adapter::Toml::TeptrisAdapter : Object) do
|
|
15
|
+
before { skip "teptris is not available" unless TEPTRIS_AVAILABLE }
|
|
16
|
+
|
|
17
|
+
describe ".parse" do
|
|
18
|
+
it "parses a TOML document into a hash" do
|
|
19
|
+
expect(described_class.parse("name = \"John\"\nage = 30\n"))
|
|
20
|
+
.to eq("name" => "John", "age" => 30)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "parses nested tables and arrays" do
|
|
24
|
+
toml = <<~TOML
|
|
25
|
+
title = "cfg"
|
|
26
|
+
[owner]
|
|
27
|
+
name = "t"
|
|
28
|
+
tags = ["a", "b"]
|
|
29
|
+
TOML
|
|
30
|
+
expect(described_class.parse(toml))
|
|
31
|
+
.to eq("title" => "cfg", "owner" => { "name" => "t", "tags" => %w[a b] })
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "materializes dates and datetimes like tomlib" do
|
|
35
|
+
require "date"
|
|
36
|
+
doc = described_class.parse("d = 1979-05-27\ndt = 1979-05-27T07:32:00Z\n")
|
|
37
|
+
expect(doc["d"]).to be_a(Date)
|
|
38
|
+
expect(doc["dt"]).to be_a(Time)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "reports parse errors with line and column" do
|
|
42
|
+
expect { described_class.parse("a = [1,") }
|
|
43
|
+
.to raise_error(Teptris::ParseError) do |e|
|
|
44
|
+
expect(e.line).to eq(1)
|
|
45
|
+
expect(e.column).to eq(8)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe "#to_toml" do
|
|
51
|
+
it "dumps a hash to TOML" do
|
|
52
|
+
attributes = { "name" => "x", "nested" => { "a" => [1, 2] } }
|
|
53
|
+
expect(described_class.new(attributes).to_toml)
|
|
54
|
+
.to include("name", "[nested]", "a = [1, 2]")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "unwraps the __root__ KeyValueElement wrapper" do
|
|
58
|
+
element = Lutaml::KeyValue::DataModel::Element.new("__root__")
|
|
59
|
+
name_child = Lutaml::KeyValue::DataModel::Element.new("name")
|
|
60
|
+
name_child.value = "x"
|
|
61
|
+
element.add_child(name_child)
|
|
62
|
+
expect(described_class.new(element).to_toml).to include("name")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "round-trips through the model layer" do
|
|
67
|
+
attributes = { "name" => "John", "age" => 30 }
|
|
68
|
+
adapter_toml = described_class.new(attributes).to_toml
|
|
69
|
+
expect(described_class.parse(adapter_toml)).to eq(attributes)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
|
+
require "multi_json"
|
|
2
3
|
require "lutaml/key_value/adapter/json/standard_adapter"
|
|
3
4
|
require "lutaml/key_value/adapter/json/multi_json_adapter"
|
|
4
5
|
require "lutaml/key_value/adapter/json/oj_adapter"
|
|
@@ -37,6 +38,11 @@ RSpec.describe "JsonAdapter" do
|
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
describe Lutaml::KeyValue::Adapter::Json::MultiJsonAdapter do
|
|
41
|
+
# multi_json 1.21's json_gem adapter passes options positionally to
|
|
42
|
+
# JSON.parse, which json >= 3 removed (intridea/multi_json#227).
|
|
43
|
+
if Gem::Version.new(JSON::VERSION) >= Gem::Version.new("3")
|
|
44
|
+
before { skip "multi_json is incompatible with json #{JSON::VERSION} (intridea/multi_json#227)" }
|
|
45
|
+
end
|
|
40
46
|
it_behaves_like "a JSON adapter", described_class
|
|
41
47
|
end
|
|
42
48
|
|
|
@@ -17,6 +17,8 @@ RSpec.describe "TomlAdapter" do
|
|
|
17
17
|
TomlRB.dump(attributes)
|
|
18
18
|
elsif adapter_class == Lutaml::Model::Toml::TomlibAdapter
|
|
19
19
|
Tomlib.dump(attributes)
|
|
20
|
+
elsif adapter_class == Lutaml::Model::Toml::TeptrisAdapter && defined?(Teptris)
|
|
21
|
+
Teptris::TOML.dump(attributes)
|
|
20
22
|
end
|
|
21
23
|
end
|
|
22
24
|
|
|
@@ -45,4 +47,10 @@ RSpec.describe "TomlAdapter" do
|
|
|
45
47
|
it_behaves_like "a TOML adapter", described_class
|
|
46
48
|
end
|
|
47
49
|
end
|
|
50
|
+
|
|
51
|
+
if defined?(Teptris)
|
|
52
|
+
describe Lutaml::Model::Toml::TeptrisAdapter do
|
|
53
|
+
it_behaves_like "a TOML adapter", described_class
|
|
54
|
+
end
|
|
55
|
+
end
|
|
48
56
|
end
|
|
@@ -10,7 +10,7 @@ RSpec.describe Lutaml::Model::Toml do
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
it "does not autoload native TOML adapters on Opal" do
|
|
13
|
-
%i[TomlRbAdapter TomlibAdapter].each do |constant_name|
|
|
13
|
+
%i[TomlRbAdapter TomlibAdapter TeptrisAdapter].each do |constant_name|
|
|
14
14
|
hide_const("Lutaml::Toml::Adapter::#{constant_name}")
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -19,12 +19,14 @@ RSpec.describe Lutaml::Model::Toml do
|
|
|
19
19
|
|
|
20
20
|
expect(Lutaml::Toml::Adapter.autoload?(:TomlRbAdapter)).to be_nil
|
|
21
21
|
expect(Lutaml::Toml::Adapter.autoload?(:TomlibAdapter)).to be_nil
|
|
22
|
+
expect(Lutaml::Toml::Adapter.autoload?(:TeptrisAdapter)).to be_nil
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
describe ".detect_toml_adapter" do
|
|
26
27
|
before do
|
|
27
28
|
# Hide any existing constants first
|
|
29
|
+
hide_const("Teptris") if Object.const_defined?(:Teptris)
|
|
28
30
|
hide_const("Tomlib") if Object.const_defined?(:Tomlib)
|
|
29
31
|
hide_const("TomlRb") if Object.const_defined?(:TomlRb)
|
|
30
32
|
|
|
@@ -32,6 +34,39 @@ RSpec.describe Lutaml::Model::Toml do
|
|
|
32
34
|
allow(Lutaml::Model::Utils).to receive(:require).and_return(true)
|
|
33
35
|
end
|
|
34
36
|
|
|
37
|
+
context "when teptris is available" do
|
|
38
|
+
before do
|
|
39
|
+
stub_const("Teptris", Module.new)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "returns :teptris on non-Windows platforms (tomlib absent)" do
|
|
43
|
+
allow(Gem).to receive(:win_platform?).and_return(false)
|
|
44
|
+
expect(described_class.detect_toml_adapter).to eq(:teptris)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "returns :teptris on Windows (native TOML without tomlib segfaults)" do
|
|
48
|
+
allow(Gem).to receive(:win_platform?).and_return(true)
|
|
49
|
+
expect(described_class.detect_toml_adapter).to eq(:teptris)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
context "when teptris and tomlib are both available" do
|
|
54
|
+
before do
|
|
55
|
+
stub_const("Teptris", Module.new)
|
|
56
|
+
stub_const("Tomlib", Module.new)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "prefers teptris on non-Windows (0.2.12+ beats tomlib both directions)" do
|
|
60
|
+
allow(Gem).to receive(:win_platform?).and_return(false)
|
|
61
|
+
expect(described_class.detect_toml_adapter).to eq(:teptris)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "prefers teptris on Windows" do
|
|
65
|
+
allow(Gem).to receive(:win_platform?).and_return(true)
|
|
66
|
+
expect(described_class.detect_toml_adapter).to eq(:teptris)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
35
70
|
context "when Tomlib is available" do
|
|
36
71
|
before do
|
|
37
72
|
stub_const("Tomlib", Module.new)
|