lutaml-model 0.8.36 → 0.8.38
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/bench/bench_sts.rb +9 -3
- data/bench/gate_config.rb +5 -4
- data/lib/compat/opal/lutaml_model_boot.rb +1 -0
- data/lib/lutaml/key_value/transform.rb +52 -5
- data/lib/lutaml/key_value/transformation.rb +10 -2
- data/lib/lutaml/model/adapter_resolver.rb +14 -2
- data/lib/lutaml/model/attribute.rb +15 -9
- data/lib/lutaml/model/choice.rb +6 -5
- data/lib/lutaml/model/format_registry.rb +3 -1
- data/lib/lutaml/model/liquefiable.rb +23 -2
- data/lib/lutaml/model/mapping/mapping_rule.rb +46 -11
- data/lib/lutaml/model/sequence.rb +47 -2
- data/lib/lutaml/model/serialize/attribute_definition.rb +176 -47
- data/lib/lutaml/model/serialize/deserialization_context.rb +1 -0
- data/lib/lutaml/model/serialize.rb +54 -10
- data/lib/lutaml/model/services/transformer.rb +30 -11
- data/lib/lutaml/model/store.rb +70 -32
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/xml/format.rb +1 -0
- data/lib/lutaml/xml/html_entities.rb +290 -0
- data/lib/lutaml/xml/mapping.rb +72 -0
- data/lib/lutaml/xml/model_transform.rb +210 -72
- data/lib/lutaml/xml/parse_session.rb +38 -0
- data/lib/lutaml/yaml/format.rb +1 -0
- data/spec/lutaml/model/adapter_resolver_configured_spec.rb +38 -0
- data/spec/lutaml/model/custom_method_context_spec.rb +62 -0
- data/spec/lutaml/model/liquefiable_lazy_spec.rb +36 -0
- data/spec/lutaml/model/mapping_type_validation_spec.rb +61 -0
- data/spec/lutaml/model/sequence_choice_compositor_spec.rb +66 -0
- data/spec/lutaml/model/store_spec.rb +13 -11
- data/spec/lutaml/model/yaml_stream_spec.rb +70 -0
- data/spec/lutaml/xml/html_entities_spec.rb +45 -0
- metadata +9 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# lutaml-model#687: an xs:sequence remembers a nested xs:choice
|
|
6
|
+
# compositor. Element rules inside `choice` take their position in the
|
|
7
|
+
# sequence and carry the choice on their model attributes, so validation
|
|
8
|
+
# applies order and exclusivity.
|
|
9
|
+
RSpec.describe "Sequence with nested choice" do
|
|
10
|
+
let(:model) do
|
|
11
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
12
|
+
attribute :title, :string
|
|
13
|
+
attribute :hardcover, :string
|
|
14
|
+
attribute :paperback, :string
|
|
15
|
+
attribute :author, :string, collection: true
|
|
16
|
+
|
|
17
|
+
xml do
|
|
18
|
+
element "book"
|
|
19
|
+
sequence do
|
|
20
|
+
map_element "title", to: :title
|
|
21
|
+
choice do
|
|
22
|
+
map_element "hardcover", to: :hardcover
|
|
23
|
+
map_element "papercover", to: :paperback
|
|
24
|
+
map_element "paperback", to: :paperback
|
|
25
|
+
end
|
|
26
|
+
map_element "author", to: :author
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "records the choice as a compositor alongside the flat order" do
|
|
33
|
+
sequence = model.mappings_for(:xml).element_sequence.first
|
|
34
|
+
expect(sequence.compositors).to contain_exactly(
|
|
35
|
+
an_instance_of(Lutaml::Model::Choice),
|
|
36
|
+
)
|
|
37
|
+
choice = sequence.compositors.first
|
|
38
|
+
# After finalize the choice carries the model attributes its rules
|
|
39
|
+
# target — the shape validation machinery expects.
|
|
40
|
+
expect(choice.attributes.map(&:name)).to eq(%i[hardcover paperback])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "keeps the model attributes tagged with the nested choice" do
|
|
44
|
+
choice = model.mappings_for(:xml).element_sequence.first.compositors.first
|
|
45
|
+
expect(model.attributes[:hardcover].options[:choice]).to equal(choice)
|
|
46
|
+
expect(model.attributes[:paperback].options[:choice]).to equal(choice)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "validates order and exclusivity" do
|
|
50
|
+
valid = model.from_xml(
|
|
51
|
+
"<book><title>T</title><paperback>PB</paperback><author>A</author></book>",
|
|
52
|
+
)
|
|
53
|
+
expect(valid.validate).to be_empty
|
|
54
|
+
|
|
55
|
+
out_of_order = model.from_xml(
|
|
56
|
+
"<book><author>A</author><title>T</title><hardcover>HB</hardcover></book>",
|
|
57
|
+
)
|
|
58
|
+
expect(out_of_order.validate).not_to be_empty
|
|
59
|
+
|
|
60
|
+
both_branches = model.from_xml(
|
|
61
|
+
"<book><title>T</title><hardcover>HB</hardcover>" \
|
|
62
|
+
"<paperback>PB</paperback><author>A</author></book>",
|
|
63
|
+
)
|
|
64
|
+
expect(both_branches.validate).not_to be_empty
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -202,12 +202,10 @@ RSpec.describe Lutaml::Model::Store do
|
|
|
202
202
|
# After compaction, the refs array should be smaller than before
|
|
203
203
|
# (some dead refs removed). Exact count depends on GC timing,
|
|
204
204
|
# but the live refs must still be present.
|
|
205
|
-
live
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
false
|
|
210
|
-
end
|
|
205
|
+
# refs_for yields live objects directly under the WeakMap buckets
|
|
206
|
+
# (dead keys never surface), so the live count is bounded by the
|
|
207
|
+
# post-GC population regardless of compaction timing.
|
|
208
|
+
alive_count = instance.refs_for(model_class.to_s).count
|
|
211
209
|
expect(alive_count).to be < (threshold + 1 + interval)
|
|
212
210
|
end
|
|
213
211
|
|
|
@@ -225,14 +223,18 @@ RSpec.describe Lutaml::Model::Store do
|
|
|
225
223
|
expect(counters[other_class.to_s]).to eq(3)
|
|
226
224
|
end
|
|
227
225
|
|
|
228
|
-
it "
|
|
229
|
-
|
|
226
|
+
it "compacts only on interval multiples" do
|
|
227
|
+
interval = Lutaml::Model::Store::COMPACTION_INTERVAL
|
|
230
228
|
instance = described_class.instance
|
|
231
229
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
#
|
|
230
|
+
# WeakMap buckets shed dead keys without help, so compaction is
|
|
231
|
+
# purely index maintenance on a fixed cadence: the counter fires
|
|
232
|
+
# exactly at each interval boundary, never between.
|
|
233
|
+
(interval - 1).times { |i| model_class.new(id: "edge-#{i}") }
|
|
235
234
|
expect(instance.compaction_count).to eq(0)
|
|
235
|
+
|
|
236
|
+
model_class.new(id: "edge-trigger")
|
|
237
|
+
expect(instance.compaction_count).to eq(1)
|
|
236
238
|
end
|
|
237
239
|
end
|
|
238
240
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# lutaml-model#436: multi-document YAML streams with per-document model
|
|
6
|
+
# dispatch (the glossarist-ruby#132 / ISO 5843-6 shape).
|
|
7
|
+
RSpec.describe "YAML streams" do
|
|
8
|
+
let(:concept_class) do
|
|
9
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
10
|
+
attribute :id, :string
|
|
11
|
+
attribute :language_code, :string
|
|
12
|
+
|
|
13
|
+
key_value do
|
|
14
|
+
map "id", to: :id
|
|
15
|
+
map "language_code", to: :language_code
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
let(:localized_class) do
|
|
21
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
22
|
+
attribute :id, :string
|
|
23
|
+
attribute :status, :string
|
|
24
|
+
|
|
25
|
+
key_value do
|
|
26
|
+
map "id", to: :id
|
|
27
|
+
map "status", to: :status
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
let(:stream) do
|
|
33
|
+
<<~YAML
|
|
34
|
+
---
|
|
35
|
+
id: 1-EN
|
|
36
|
+
language_code: eng
|
|
37
|
+
---
|
|
38
|
+
id: 3a75
|
|
39
|
+
status: accepted
|
|
40
|
+
---
|
|
41
|
+
id: 1-FR
|
|
42
|
+
language_code: fra
|
|
43
|
+
YAML
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "parses every document in the stream" do
|
|
47
|
+
models = concept_class.from_yaml_stream(stream)
|
|
48
|
+
expect(models.map(&:id)).to eq(%w[1-EN 3a75 1-FR])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "dispatches documents polymorphically via discriminator" do
|
|
52
|
+
models = concept_class.from_yaml_stream(
|
|
53
|
+
stream,
|
|
54
|
+
discriminator: lambda { |doc|
|
|
55
|
+
doc.key?("language_code") ? concept_class : localized_class
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
expect(models.map(&:class)).to eq([concept_class, localized_class,
|
|
59
|
+
concept_class])
|
|
60
|
+
expect(models[1].status).to eq("accepted")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "round-trips through to_yaml_stream" do
|
|
64
|
+
models = concept_class.from_yaml_stream(stream)
|
|
65
|
+
out = concept_class.to_yaml_stream(models)
|
|
66
|
+
reparsed = concept_class.from_yaml_stream(out)
|
|
67
|
+
expect(reparsed.map(&:id)).to eq(models.map(&:id))
|
|
68
|
+
expect(out).to include("---")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# lutaml-model#154: HTML named/numeric entities decode only for
|
|
6
|
+
# mappings that opt in via `html_entities` — XML otherwise keeps
|
|
7
|
+
# undefined entities literal.
|
|
8
|
+
RSpec.describe "Opt-in HTML entity decoding" do
|
|
9
|
+
context "when the mapping opts in" do
|
|
10
|
+
let(:model_class) do
|
|
11
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
12
|
+
attribute :t, :string
|
|
13
|
+
|
|
14
|
+
xml do
|
|
15
|
+
element "doc"
|
|
16
|
+
map_element "t", to: :t
|
|
17
|
+
html_entities
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "decodes named and numeric entities in element text" do
|
|
23
|
+
parsed = model_class.from_xml("<doc><t>a & b — c © d</t></doc>")
|
|
24
|
+
expect(parsed.t).to eq("a & b — c © d")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context "when the mapping does not opt in" do
|
|
29
|
+
let(:model_class) do
|
|
30
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
31
|
+
attribute :t, :string
|
|
32
|
+
|
|
33
|
+
xml do
|
|
34
|
+
element "doc"
|
|
35
|
+
map_element "t", to: :t
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "keeps undefined entities literal" do
|
|
41
|
+
parsed = model_class.from_xml("<doc><t>a & b © d</t></doc>")
|
|
42
|
+
expect(parsed.t).to eq("a & b © d")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lutaml-model
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.38
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -776,6 +776,7 @@ files:
|
|
|
776
776
|
- lib/lutaml/xml/format.rb
|
|
777
777
|
- lib/lutaml/xml/format_chooser.rb
|
|
778
778
|
- lib/lutaml/xml/hoisting_algorithm.rb
|
|
779
|
+
- lib/lutaml/xml/html_entities.rb
|
|
779
780
|
- lib/lutaml/xml/leptris/element.rb
|
|
780
781
|
- lib/lutaml/xml/listener.rb
|
|
781
782
|
- lib/lutaml/xml/mapping.rb
|
|
@@ -1686,6 +1687,7 @@ files:
|
|
|
1686
1687
|
- spec/lutaml/key_value/transformation/value_serializer_spec.rb
|
|
1687
1688
|
- spec/lutaml/key_value/transformation_spec.rb
|
|
1688
1689
|
- spec/lutaml/model/absent_value_contracts_spec.rb
|
|
1690
|
+
- spec/lutaml/model/adapter_resolver_configured_spec.rb
|
|
1689
1691
|
- spec/lutaml/model/attribute_apply_value_map_spec.rb
|
|
1690
1692
|
- spec/lutaml/model/attribute_collection_spec.rb
|
|
1691
1693
|
- spec/lutaml/model/attribute_default_cache_spec.rb
|
|
@@ -1715,6 +1717,7 @@ files:
|
|
|
1715
1717
|
- spec/lutaml/model/custom_bibtex_adapter_spec.rb
|
|
1716
1718
|
- spec/lutaml/model/custom_collection_spec.rb
|
|
1717
1719
|
- spec/lutaml/model/custom_from_empty_values_spec.rb
|
|
1720
|
+
- spec/lutaml/model/custom_method_context_spec.rb
|
|
1718
1721
|
- spec/lutaml/model/custom_model_spec.rb
|
|
1719
1722
|
- spec/lutaml/model/custom_serialization_spec.rb
|
|
1720
1723
|
- spec/lutaml/model/custom_vobject_adapter_spec.rb
|
|
@@ -1743,6 +1746,7 @@ files:
|
|
|
1743
1746
|
- spec/lutaml/model/key_value_data_model/key_value_element_spec.rb
|
|
1744
1747
|
- spec/lutaml/model/key_value_mapping_spec.rb
|
|
1745
1748
|
- spec/lutaml/model/lazy_collection_spec.rb
|
|
1749
|
+
- spec/lutaml/model/liquefiable_lazy_spec.rb
|
|
1746
1750
|
- spec/lutaml/model/liquefiable_spec.rb
|
|
1747
1751
|
- spec/lutaml/model/liquid/indexed_access_spec.rb
|
|
1748
1752
|
- spec/lutaml/model/liquid_compatibility_spec.rb
|
|
@@ -1750,6 +1754,7 @@ files:
|
|
|
1750
1754
|
- spec/lutaml/model/map_all_spec.rb
|
|
1751
1755
|
- spec/lutaml/model/map_content_spec.rb
|
|
1752
1756
|
- spec/lutaml/model/mapping_rule_perf_guard_spec.rb
|
|
1757
|
+
- spec/lutaml/model/mapping_type_validation_spec.rb
|
|
1753
1758
|
- spec/lutaml/model/mixed_content_spec.rb
|
|
1754
1759
|
- spec/lutaml/model/model_transformer_spec.rb
|
|
1755
1760
|
- spec/lutaml/model/multiple_mapping_spec.rb
|
|
@@ -1783,6 +1788,7 @@ files:
|
|
|
1783
1788
|
- spec/lutaml/model/schema/renderers/union_spec.rb
|
|
1784
1789
|
- spec/lutaml/model/schema/xsd_schema_spec.rb
|
|
1785
1790
|
- spec/lutaml/model/schema/yaml_schema_spec.rb
|
|
1791
|
+
- spec/lutaml/model/sequence_choice_compositor_spec.rb
|
|
1786
1792
|
- spec/lutaml/model/sequence_spec.rb
|
|
1787
1793
|
- spec/lutaml/model/serializable_spec.rb
|
|
1788
1794
|
- spec/lutaml/model/serializable_validation_spec.rb
|
|
@@ -1855,6 +1861,7 @@ files:
|
|
|
1855
1861
|
- spec/lutaml/model/xsd_type_spec.rb
|
|
1856
1862
|
- spec/lutaml/model/xsd_type_validation_spec.rb
|
|
1857
1863
|
- spec/lutaml/model/yaml_adapter_spec.rb
|
|
1864
|
+
- spec/lutaml/model/yaml_stream_spec.rb
|
|
1858
1865
|
- spec/lutaml/model/yamls/standard_adapter_spec.rb
|
|
1859
1866
|
- spec/lutaml/model/yamls_range_spec.rb
|
|
1860
1867
|
- spec/lutaml/model/yamls_sequence_spec.rb
|
|
@@ -1909,6 +1916,7 @@ files:
|
|
|
1909
1916
|
- spec/lutaml/xml/format_chooser_spec.rb
|
|
1910
1917
|
- spec/lutaml/xml/fpi_namespace_spec.rb
|
|
1911
1918
|
- spec/lutaml/xml/general_namespace_spec.rb
|
|
1919
|
+
- spec/lutaml/xml/html_entities_spec.rb
|
|
1912
1920
|
- spec/lutaml/xml/indent_spec.rb
|
|
1913
1921
|
- spec/lutaml/xml/line_ending_spec.rb
|
|
1914
1922
|
- spec/lutaml/xml/mapping_finalization_guard_spec.rb
|