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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/bench/bench_sts.rb +9 -3
  3. data/bench/gate_config.rb +5 -4
  4. data/lib/compat/opal/lutaml_model_boot.rb +1 -0
  5. data/lib/lutaml/key_value/transform.rb +52 -5
  6. data/lib/lutaml/key_value/transformation.rb +10 -2
  7. data/lib/lutaml/model/adapter_resolver.rb +14 -2
  8. data/lib/lutaml/model/attribute.rb +15 -9
  9. data/lib/lutaml/model/choice.rb +6 -5
  10. data/lib/lutaml/model/format_registry.rb +3 -1
  11. data/lib/lutaml/model/liquefiable.rb +23 -2
  12. data/lib/lutaml/model/mapping/mapping_rule.rb +46 -11
  13. data/lib/lutaml/model/sequence.rb +47 -2
  14. data/lib/lutaml/model/serialize/attribute_definition.rb +176 -47
  15. data/lib/lutaml/model/serialize/deserialization_context.rb +1 -0
  16. data/lib/lutaml/model/serialize.rb +54 -10
  17. data/lib/lutaml/model/services/transformer.rb +30 -11
  18. data/lib/lutaml/model/store.rb +70 -32
  19. data/lib/lutaml/model/version.rb +1 -1
  20. data/lib/lutaml/xml/format.rb +1 -0
  21. data/lib/lutaml/xml/html_entities.rb +290 -0
  22. data/lib/lutaml/xml/mapping.rb +72 -0
  23. data/lib/lutaml/xml/model_transform.rb +210 -72
  24. data/lib/lutaml/xml/parse_session.rb +38 -0
  25. data/lib/lutaml/yaml/format.rb +1 -0
  26. data/spec/lutaml/model/adapter_resolver_configured_spec.rb +38 -0
  27. data/spec/lutaml/model/custom_method_context_spec.rb +62 -0
  28. data/spec/lutaml/model/liquefiable_lazy_spec.rb +36 -0
  29. data/spec/lutaml/model/mapping_type_validation_spec.rb +61 -0
  30. data/spec/lutaml/model/sequence_choice_compositor_spec.rb +66 -0
  31. data/spec/lutaml/model/store_spec.rb +13 -11
  32. data/spec/lutaml/model/yaml_stream_spec.rb +70 -0
  33. data/spec/lutaml/xml/html_entities_spec.rb +45 -0
  34. 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 = instance.refs_for(model_class.to_s)
206
- alive_count = live.count do |ref|
207
- ref.weakref_alive?
208
- rescue WeakRef::RefError
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 "does not compact when exactly at threshold" do
229
- threshold = Lutaml::Model::Store::COMPACTION_THRESHOLD
226
+ it "compacts only on interval multiples" do
227
+ interval = Lutaml::Model::Store::COMPACTION_INTERVAL
230
228
  instance = described_class.instance
231
229
 
232
- _objects = Array.new(threshold) { |i| model_class.new(id: "edge-#{i}") }
233
-
234
- # refs.size == threshold, which does not satisfy size > threshold
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 &amp; b &#8212; c &copy; 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 &amp; b &copy; d</t></doc>")
42
+ expect(parsed.t).to eq("a & b &copy; 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.36
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