lutaml-model 0.8.143 → 0.8.145

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27c27a6e43a54c69e89e08cdeb8f749992155bc6ea072d5ccb1ba9867de9fc92
4
- data.tar.gz: aab282e0b42b4a811529b58ab6b2dd5e8ab40287498802683c5283cfc6b55bdc
3
+ metadata.gz: 1d4a991baea8734712513f2d1ffb0025d0a5867f174a40b32bee58b1be5017c5
4
+ data.tar.gz: f1713d5b0ef5024da66ee23f8251604da86fd3f8d7035e7f64118bf080ea9128
5
5
  SHA512:
6
- metadata.gz: 5b6b43e0792d190536c648d6eebf21d7021e1a4f5c84f687f02394ee98b02dc255fd628c9cf07c130dab442a6f19eada2afaab7f1f7172806cb03cbff38ff836
7
- data.tar.gz: 492f737e7ebddc49e140e454a78a01b4c240ef463de21cd0a0040b4b9048657125642eb7a9f2211a5842fe9311a9dfd627506209acbd9b17656fc6f27c4d4ad5
6
+ metadata.gz: db827973c88ea9fe1779a0105d18ef1d0f93868fa4a59147a029c504005ec5f5e50378a7f90c490097e24d20c62fb33531a8fe277fb7620ed3cd17debdd2adaf
7
+ data.tar.gz: 90045bda2ce6c2605ca3708527c44ad7811cacb6cb8d4f72d83f3a875a3a043a84503dc48f24f716d94f5e92e367d6a4bfb2fa556982d14db65125623d885252
@@ -184,12 +184,16 @@ module Lutaml
184
184
  end
185
185
  end
186
186
 
187
- private
188
-
189
187
  # Replace a shared structure with a class-owned copy. The copy
190
188
  # becomes the snapshot for every existing subclass, which is what
191
189
  # keeps post-fork mutations invisible to them (the eager-copy
192
190
  # semantics).
191
+ #
192
+ # Public on purpose: code that mutates `mappings`, `attributes`,
193
+ # `choices` or `registers` outside the DSL — sibling classes pruning
194
+ # inherited elements (relaton's Item-header prunes) or importing
195
+ # models — must fork the structure first, or the mutation lands on
196
+ # the object shared with the superclass and every sibling class.
193
197
  def lutaml_cow_fork!(structure)
194
198
  return if lutaml_cow_status(structure) == 1
195
199
 
@@ -220,8 +224,6 @@ module Lutaml
220
224
  lutaml_cow_set_status!(structure, 1)
221
225
  end
222
226
 
223
- public
224
-
225
227
  # Return the mapping object a format DSL may mutate. Forking
226
228
  # the mappings structure copies every entry, so the object
227
229
  # returned here is this class's own.
@@ -67,6 +67,21 @@ module Lutaml
67
67
  klass = Object.const_get(klass_name)
68
68
 
69
69
  propagated = options.slice(*Lutaml::Model::Attribute::CHILD_PROPAGATION_KEYS)
70
+
71
+ # A map entry that resolves to the deserializing class itself
72
+ # (a subclass inheriting its base's map, or an apply_mappings
73
+ # override delegating back to super for its own concrete class)
74
+ # must fall through to this class's real deserialization:
75
+ # re-dispatching apply_mappings would resolve the same entry
76
+ # again and never make progress.
77
+ if klass == model
78
+ transformer = Lutaml::Model::Config.transformer_for(format)
79
+ return transformer.data_to_model(
80
+ self, doc, format,
81
+ propagated.merge(register: instance.lutaml_register)
82
+ )
83
+ end
84
+
70
85
  klass.apply_mappings(doc, format,
71
86
  propagated.merge(register: instance.lutaml_register))
72
87
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.8.143"
5
+ VERSION = "0.8.145"
6
6
  end
7
7
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Copy-on-write inheritance shares a subclass's structures with its
6
+ # superclass until the class's own DSL forks them. External mutators — a
7
+ # library pruning an inherited attribute on one subclass (relaton's
8
+ # Item-header prunes) — need a supported way to claim ownership first, or
9
+ # the mutation erases the attribute for every sibling class.
10
+ RSpec.describe "Lutaml::Model::Serializable#lutaml_cow_fork!" do
11
+ let(:parent_class) do
12
+ Class.new(Lutaml::Model::Serializable) do
13
+ attribute :id, :string
14
+ attribute :shared, :string
15
+
16
+ xml do
17
+ root "parent"
18
+ map_attribute "id", to: :id
19
+ map_element "shared", to: :shared
20
+ end
21
+ end
22
+ end
23
+
24
+ let(:child_class) do
25
+ Class.new(parent_class)
26
+ end
27
+
28
+ it "is callable from outside the DSL" do
29
+ expect(child_class).to respond_to(:lutaml_cow_fork!)
30
+ end
31
+
32
+ it "gives the class a private copy without touching the superclass" do
33
+ parent_attributes = parent_class.class_attributes
34
+ child_class.lutaml_cow_fork!(:attributes)
35
+
36
+ expect(child_class.class_attributes).not_to equal(parent_attributes)
37
+ expect(parent_class.class_attributes).to equal(parent_attributes)
38
+ end
39
+
40
+ it "keeps post-fork mutations private to the forking class" do
41
+ child_class.lutaml_cow_fork!(:attributes)
42
+ child_class.lutaml_cow_fork!(:mappings)
43
+ child_class.class_attributes.delete(:id)
44
+ xml_attrs = child_class.mappings[:xml].instance_variable_get(:@attributes)
45
+ xml_attrs.delete("id")
46
+
47
+ expect(parent_class.class_attributes).to be_key(:id)
48
+ expect(parent_class.mappings[:xml]
49
+ .instance_variable_get(:@attributes)).to be_key("id")
50
+ expect(child_class.class_attributes).not_to be_key(:id)
51
+ end
52
+
53
+ it "does not fork an already-owned structure into a new object" do
54
+ child_class.lutaml_cow_fork!(:attributes)
55
+ owned = child_class.class_attributes
56
+ child_class.lutaml_cow_fork!(:attributes)
57
+
58
+ expect(child_class.class_attributes).to equal(owned)
59
+ end
60
+ end
@@ -125,6 +125,21 @@ module PolymorphicSpec
125
125
  }
126
126
  end
127
127
  end
128
+
129
+ # A polymorphic map whose entry resolves to the class carrying it
130
+ # (e.g. a subclass inheriting its base's map, or an apply_mappings
131
+ # override delegating back to super for its own concrete class).
132
+ class SelfResolving < Lutaml::Model::Serializable
133
+ attribute :_type, :string
134
+ attribute :name, :string
135
+
136
+ key_value do
137
+ map "_type", to: :_type, polymorphic_map: {
138
+ "self_resolving" => "PolymorphicSpec::Base::SelfResolving",
139
+ }
140
+ map "name", to: :name
141
+ end
142
+ end
128
143
  end
129
144
 
130
145
  module Child
@@ -546,4 +561,17 @@ RSpec.describe "Polymorphic" do
546
561
  end
547
562
  end
548
563
  end
564
+
565
+ # A polymorphic map that resolves a document to the very class that is
566
+ # deserializing it must fall through to the model's own
567
+ # deserialization, not re-dispatch apply_mappings forever.
568
+ context "when the polymorphic map resolves to the deserializing class" do
569
+ it "deserializes without recursing" do
570
+ model = PolymorphicSpec::Base::SelfResolving.apply_mappings(
571
+ { "_type" => "self_resolving", "name" => "x" }, :hash
572
+ )
573
+ expect(model).to be_a(PolymorphicSpec::Base::SelfResolving)
574
+ expect(model.name).to eq("x")
575
+ end
576
+ end
549
577
  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.143
4
+ version: 0.8.145
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -1753,6 +1753,7 @@ files:
1753
1753
  - spec/lutaml/model/consolidation_spec.rb
1754
1754
  - spec/lutaml/model/context_registry_spec.rb
1755
1755
  - spec/lutaml/model/conversion_caching_spec.rb
1756
+ - spec/lutaml/model/cow_fork_public_spec.rb
1756
1757
  - spec/lutaml/model/custom_bibtex_adapter_spec.rb
1757
1758
  - spec/lutaml/model/custom_collection_spec.rb
1758
1759
  - spec/lutaml/model/custom_from_empty_values_spec.rb