lutaml-model 0.6.7 → 0.7.2
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/dependent-repos-todo.json +7 -0
- data/.github/workflows/dependent-repos.json +17 -9
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +51 -65
- data/Gemfile +4 -1
- data/README.adoc +5083 -2612
- data/RELEASE_NOTES.adoc +346 -0
- data/docs/custom_adapters.adoc +144 -0
- data/lib/lutaml/model/attribute.rb +101 -16
- data/lib/lutaml/model/choice.rb +7 -0
- data/lib/lutaml/model/comparable_model.rb +48 -9
- data/lib/lutaml/model/config.rb +48 -42
- data/lib/lutaml/model/error/collection_count_out_of_range_error.rb +1 -1
- data/lib/lutaml/model/error/polymorphic_error.rb +14 -0
- data/lib/lutaml/model/error.rb +1 -0
- data/lib/lutaml/model/format_registry.rb +41 -0
- data/lib/lutaml/model/hash/document.rb +11 -0
- data/lib/lutaml/model/hash/mapping.rb +19 -0
- data/lib/lutaml/model/hash/mapping_rule.rb +9 -0
- data/lib/lutaml/model/hash/standard_adapter.rb +17 -0
- data/lib/lutaml/model/hash/transform.rb +8 -0
- data/lib/lutaml/model/hash.rb +21 -0
- data/lib/lutaml/model/json/document.rb +11 -0
- data/lib/lutaml/model/json/mapping.rb +19 -0
- data/lib/lutaml/model/json/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
- data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
- data/lib/lutaml/model/json/transform.rb +8 -0
- data/lib/lutaml/model/json.rb +21 -0
- data/lib/lutaml/model/key_value_document.rb +27 -0
- data/lib/lutaml/model/{key_value_mapping.rb → mapping/key_value_mapping.rb} +64 -16
- data/lib/lutaml/model/{key_value_mapping_rule.rb → mapping/key_value_mapping_rule.rb} +18 -2
- data/lib/lutaml/model/mapping/mapping.rb +13 -0
- data/lib/lutaml/model/mapping/mapping_rule.rb +300 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +15 -15
- data/lib/lutaml/model/sequence.rb +2 -2
- data/lib/lutaml/model/serialization_adapter.rb +22 -0
- data/lib/lutaml/model/serialize.rb +219 -444
- data/lib/lutaml/model/services/logger.rb +54 -0
- data/lib/lutaml/model/services/transformer.rb +48 -0
- data/lib/lutaml/model/services.rb +2 -0
- data/lib/lutaml/model/toml/document.rb +11 -0
- data/lib/lutaml/model/toml/mapping.rb +27 -0
- data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
- data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
- data/lib/lutaml/model/toml/transform.rb +8 -0
- data/lib/lutaml/model/toml.rb +30 -0
- data/lib/lutaml/model/transform/key_value_transform.rb +291 -0
- data/lib/lutaml/model/transform/xml_transform.rb +239 -0
- data/lib/lutaml/model/transform.rb +78 -0
- data/lib/lutaml/model/type/date.rb +1 -1
- data/lib/lutaml/model/type/date_time.rb +2 -2
- data/lib/lutaml/model/type/hash.rb +1 -1
- data/lib/lutaml/model/type/time.rb +2 -2
- data/lib/lutaml/model/type/time_without_date.rb +2 -2
- data/lib/lutaml/model/type/value.rb +6 -9
- data/lib/lutaml/model/uninitialized_class.rb +64 -0
- data/lib/lutaml/model/utils.rb +44 -0
- data/lib/lutaml/model/validation.rb +1 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
- data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
- data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
- data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +41 -21
- data/lib/lutaml/model/xml/element.rb +32 -0
- data/lib/lutaml/model/xml/mapping.rb +410 -0
- data/lib/lutaml/model/xml/mapping_rule.rb +141 -0
- data/lib/lutaml/model/xml/nokogiri_adapter.rb +232 -0
- data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +3 -1
- data/lib/lutaml/model/xml/oga_adapter.rb +171 -0
- data/lib/lutaml/model/xml/ox_adapter.rb +215 -0
- data/lib/lutaml/model/xml/transform.rb +8 -0
- data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +23 -10
- data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
- data/lib/lutaml/model/xml.rb +31 -0
- data/lib/lutaml/model/xml_adapter/element.rb +11 -25
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
- data/lib/lutaml/model/yaml/document.rb +10 -0
- data/lib/lutaml/model/yaml/mapping.rb +19 -0
- data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
- data/lib/lutaml/model/yaml/transform.rb +8 -0
- data/lib/lutaml/model/yaml.rb +21 -0
- data/lib/lutaml/model.rb +40 -4
- data/lutaml-model.gemspec +0 -4
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +7 -7
- data/spec/fixtures/person.rb +5 -5
- data/spec/lutaml/model/attribute_spec.rb +37 -1
- data/spec/lutaml/model/cdata_spec.rb +9 -9
- data/spec/lutaml/model/collection_spec.rb +50 -2
- data/spec/lutaml/model/comparable_model_spec.rb +92 -27
- data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
- data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
- data/spec/lutaml/model/defaults_spec.rb +1 -1
- data/spec/lutaml/model/enum_spec.rb +1 -1
- data/spec/lutaml/model/group_spec.rb +333 -20
- data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
- data/spec/lutaml/model/json_adapter_spec.rb +6 -6
- data/spec/lutaml/model/key_value_mapping_spec.rb +65 -3
- data/spec/lutaml/model/mixed_content_spec.rb +24 -24
- data/spec/lutaml/model/multiple_mapping_spec.rb +5 -5
- data/spec/lutaml/model/ordered_content_spec.rb +6 -6
- data/spec/lutaml/model/polymorphic_spec.rb +526 -0
- data/spec/lutaml/model/render_empty_spec.rb +194 -0
- data/spec/lutaml/model/render_nil_spec.rb +206 -22
- data/spec/lutaml/model/root_mappings_spec.rb +3 -3
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +6 -6
- data/spec/lutaml/model/serializable_spec.rb +179 -103
- data/spec/lutaml/model/simple_model_spec.rb +9 -9
- data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
- data/spec/lutaml/model/toml_spec.rb +51 -0
- data/spec/lutaml/model/transformation_spec.rb +72 -15
- data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
- data/spec/lutaml/model/value_map_spec.rb +240 -0
- data/spec/lutaml/model/xml/namespace/nested_with_explicit_namespace_spec.rb +85 -0
- data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
- data/spec/lutaml/model/xml/xml_element_spec.rb +93 -0
- data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
- data/spec/lutaml/model/xml_adapter_spec.rb +6 -6
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +105 -5
- data/spec/lutaml/model/xml_mapping_spec.rb +70 -16
- data/spec/lutaml/model/xml_spec.rb +63 -0
- data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
- data/spec/sample_model_spec.rb +3 -3
- data/spec/spec_helper.rb +3 -3
- metadata +76 -59
- data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
- data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
- data/lib/lutaml/model/loggable.rb +0 -15
- data/lib/lutaml/model/mapping_rule.rb +0 -109
- data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
- data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
- data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
- data/lib/lutaml/model/toml_adapter.rb +0 -6
- data/lib/lutaml/model/xml_mapping.rb +0 -307
- data/lib/lutaml/model/xml_mapping_rule.rb +0 -122
- data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
- data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
- data/lib/lutaml/model/yaml_adapter.rb +0 -8
@@ -143,7 +143,7 @@ RSpec.describe DefaultsSpec::Glaze do
|
|
143
143
|
expect(default_model.temperature).to eq(1050)
|
144
144
|
expect(default_model.firing_time).to eq(60)
|
145
145
|
expect(default_model.balance).to eq(BigDecimal("0.0"))
|
146
|
-
expect(default_model.tags).to
|
146
|
+
expect(default_model.tags).to be_nil
|
147
147
|
expect(default_model.properties).to eq({ food_safe: true })
|
148
148
|
expect(default_model.status).to eq("active")
|
149
149
|
expect(default_model.batch_number).to eq(0)
|
@@ -5,7 +5,7 @@ module EnumSpec
|
|
5
5
|
class WithEnum < Lutaml::Model::Serializable
|
6
6
|
attribute :without_enum, :string
|
7
7
|
attribute :single_value, :string, values: %w[user admin super_admin]
|
8
|
-
attribute :multi_value, :string, values: %w[singular dual plural], collection: true
|
8
|
+
attribute :multi_value, :string, values: %w[singular dual plural], collection: true, initialize_empty: true
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -3,14 +3,19 @@ require "lutaml/model"
|
|
3
3
|
|
4
4
|
module GroupSpec
|
5
5
|
class Ceramic < Lutaml::Model::Serializable
|
6
|
-
attribute :type, :string
|
7
|
-
attribute :name, :string
|
6
|
+
attribute :type, :string, default: "Data"
|
7
|
+
attribute :name, :string, default: "Starc"
|
8
8
|
|
9
9
|
xml do
|
10
10
|
no_root
|
11
11
|
map_element :type, to: :type
|
12
12
|
map_element :name, to: :name
|
13
13
|
end
|
14
|
+
|
15
|
+
key_value do
|
16
|
+
map :type, to: :type
|
17
|
+
map :name, to: :name
|
18
|
+
end
|
14
19
|
end
|
15
20
|
|
16
21
|
class CeramicCollection < Lutaml::Model::Serializable
|
@@ -75,9 +80,139 @@ module GroupSpec
|
|
75
80
|
map_element :name, to: :name
|
76
81
|
end
|
77
82
|
end
|
83
|
+
|
84
|
+
class CommonAttributes < Lutaml::Model::Serializable
|
85
|
+
choice do
|
86
|
+
attribute :mstyle, :string
|
87
|
+
attribute :mcol, :string
|
88
|
+
attribute :mr, :string
|
89
|
+
end
|
90
|
+
|
91
|
+
xml do
|
92
|
+
no_root
|
93
|
+
sequence do
|
94
|
+
map_element :mstyle, to: :mstyle
|
95
|
+
map_element :mr, to: :mr
|
96
|
+
end
|
97
|
+
map_attribute :mcol, to: :mcol
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Mrow < Lutaml::Model::Serializable
|
102
|
+
attribute :mi, :string
|
103
|
+
import_model CommonAttributes
|
104
|
+
|
105
|
+
xml do
|
106
|
+
root "mrow"
|
107
|
+
map_element :mi, to: :mi
|
108
|
+
end
|
109
|
+
|
110
|
+
import_model GroupOfItems
|
111
|
+
|
112
|
+
key_value do
|
113
|
+
map :mcol, to: :mcol
|
114
|
+
end
|
115
|
+
|
116
|
+
import_model Ceramic
|
117
|
+
end
|
118
|
+
|
119
|
+
class Mfrac < Lutaml::Model::Serializable
|
120
|
+
attribute :num, :string
|
121
|
+
import_model CommonAttributes
|
122
|
+
|
123
|
+
xml do
|
124
|
+
root "mfrac"
|
125
|
+
map_element :num, to: :num
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class ContributionInfo < Lutaml::Model::Serializable
|
130
|
+
attribute :person, :string
|
131
|
+
attribute :organization, :string
|
132
|
+
|
133
|
+
xml do
|
134
|
+
no_root
|
135
|
+
map_element "person", to: :person
|
136
|
+
map_element "organization", to: :organization
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class Contributor < Lutaml::Model::Serializable
|
141
|
+
attribute :role, :string
|
142
|
+
import_model_attributes ContributionInfo
|
143
|
+
|
144
|
+
xml do
|
145
|
+
root "contributor"
|
146
|
+
map_element "role", to: :role
|
147
|
+
map_element "person", to: :person
|
148
|
+
map_element "organization", to: :organization
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class Identifier < Lutaml::Model::Serializable
|
153
|
+
attribute :id, :string
|
154
|
+
attribute :remarks, :string, collection: true
|
155
|
+
attribute :remark_items, :string, collection: true
|
156
|
+
|
157
|
+
key_value do
|
158
|
+
map "id", to: :id
|
159
|
+
map "remarks", to: :remarks
|
160
|
+
map "remark_items", to: :remark_items
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class ModelElement < Lutaml::Model::Serializable
|
165
|
+
import_model Identifier
|
166
|
+
end
|
78
167
|
end
|
79
168
|
|
80
169
|
RSpec.describe "Group" do
|
170
|
+
context "when serializing and deserializing import model having no_root" do
|
171
|
+
let(:xml) do
|
172
|
+
<<~XML
|
173
|
+
<mrow xmlns:ex1="http://www.example.com" xmlns:GML="http://www.sparxsystems.com/profiles/GML/1.0">
|
174
|
+
<mstyle>italic</mstyle>
|
175
|
+
<mr>y</mr>
|
176
|
+
<mi>x</mi>
|
177
|
+
<name>Smith</name>
|
178
|
+
<type>product</type>
|
179
|
+
<GML:description>Item</GML:description>
|
180
|
+
</mrow>
|
181
|
+
XML
|
182
|
+
end
|
183
|
+
|
184
|
+
let(:input_xml) do
|
185
|
+
<<~XML
|
186
|
+
<contributor>
|
187
|
+
<role>author</role>
|
188
|
+
<person>John Doe</person>
|
189
|
+
<organization>ACME</organization>
|
190
|
+
</contributor>
|
191
|
+
XML
|
192
|
+
end
|
193
|
+
|
194
|
+
it "parse the imported model correctly" do
|
195
|
+
parsed = GroupSpec::Mrow.from_xml(xml)
|
196
|
+
expect(parsed.mi).to eq("x")
|
197
|
+
expect(parsed.mstyle).to eq("italic")
|
198
|
+
expect(parsed.name).to eq("Smith")
|
199
|
+
expect(parsed.type).to eq("product")
|
200
|
+
expect(parsed.description).to eq("Item")
|
201
|
+
end
|
202
|
+
|
203
|
+
it "parse the imported model attributes correctly" do
|
204
|
+
parsed = GroupSpec::Contributor.from_xml(input_xml)
|
205
|
+
expect(parsed.person).to eq("John Doe")
|
206
|
+
expect(parsed.role).to eq("author")
|
207
|
+
expect(parsed.organization).to eq("ACME")
|
208
|
+
end
|
209
|
+
|
210
|
+
it "serialize the imported model correctly" do
|
211
|
+
instance = GroupSpec::Mrow.new(mi: "x", mstyle: "italic", mr: "y", name: "Smith", type: "product", description: "Item")
|
212
|
+
expect(instance.to_xml).to be_equivalent_to(xml)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
81
216
|
context "with no_root" do
|
82
217
|
let(:mapper) { GroupSpec::CeramicCollection }
|
83
218
|
|
@@ -93,13 +228,24 @@ RSpec.describe "Group" do
|
|
93
228
|
)
|
94
229
|
end
|
95
230
|
|
96
|
-
|
97
|
-
ceramic
|
231
|
+
context "deserializing XML" do
|
232
|
+
let(:ceramic) { GroupSpec::Ceramic.new(type: "Data", name: "Starc") }
|
98
233
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
234
|
+
it "raises error for root_less class" do
|
235
|
+
expect { ceramic.to_xml }.to raise_error(
|
236
|
+
Lutaml::Model::NoRootMappingError,
|
237
|
+
"GroupSpec::Ceramic has `no_root`, it allowed only for reusable models",
|
238
|
+
)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context "deserializing key-value formats" do
|
243
|
+
let(:ceramic) { GroupSpec::Ceramic.new(type: "Data", name: "Starc") }
|
244
|
+
|
245
|
+
it "does not raises error for root_less class" do
|
246
|
+
expect { ceramic.to_yaml }
|
247
|
+
.not_to raise_error(Lutaml::Model::NoRootMappingError)
|
248
|
+
end
|
103
249
|
end
|
104
250
|
|
105
251
|
it "correctly get the element of root-less class" do
|
@@ -116,26 +262,193 @@ RSpec.describe "Group" do
|
|
116
262
|
end
|
117
263
|
|
118
264
|
context "with model" do
|
119
|
-
|
120
|
-
|
265
|
+
shared_examples "imports attributes from" do |source_class, target_class|
|
266
|
+
it "#{source_class.name} correctly" do
|
267
|
+
source_attributes = source_class.attributes
|
268
|
+
target_attributes = target_class.attributes
|
269
|
+
|
270
|
+
source_attributes.each do |name, attr|
|
271
|
+
expect(target_attributes[name].name).to eq(attr.name)
|
272
|
+
expect(target_attributes[name].type).to eq(attr.type)
|
273
|
+
expect(target_attributes[name].options).to eq(attr.options)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
shared_examples "imports mappings from" do |source_class, target_class|
|
279
|
+
it "#{source_class.name} correctly" do
|
280
|
+
source_elements = source_class.mappings_for(:xml).elements
|
281
|
+
target_elements = target_class.mappings_for(:xml).elements
|
282
|
+
|
283
|
+
source_elements.each do |element|
|
284
|
+
matching_element = target_elements.find { |e| e.name == element.name }
|
285
|
+
expect(matching_element).not_to be_nil
|
286
|
+
expect(matching_element.to).to eq(element.to)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe GroupSpec::ComplexType do
|
292
|
+
it_behaves_like "imports attributes from", GroupSpec::GroupOfItems, described_class
|
293
|
+
it_behaves_like "imports mappings from", GroupSpec::GroupOfItems, described_class
|
294
|
+
end
|
295
|
+
|
296
|
+
describe GroupSpec::GenericType do
|
297
|
+
it_behaves_like "imports mappings from", GroupSpec::GroupOfItems, described_class
|
298
|
+
end
|
299
|
+
|
300
|
+
describe GroupSpec::SimpleType do
|
301
|
+
it_behaves_like "imports attributes from", GroupSpec::GroupOfItems, described_class
|
302
|
+
it_behaves_like "imports mappings from", GroupSpec::GroupOfItems, described_class
|
303
|
+
end
|
304
|
+
|
305
|
+
describe GroupSpec::ModelElement do
|
306
|
+
it_behaves_like "imports attributes from", GroupSpec::Identifier, described_class
|
307
|
+
it_behaves_like "imports mappings from", GroupSpec::Identifier, described_class
|
308
|
+
end
|
309
|
+
|
310
|
+
describe GroupSpec::Mrow do
|
311
|
+
it_behaves_like "imports attributes from", GroupSpec::CommonAttributes, described_class
|
312
|
+
it_behaves_like "imports attributes from", GroupSpec::Ceramic, described_class
|
313
|
+
it_behaves_like "imports mappings from", GroupSpec::CommonAttributes, described_class
|
314
|
+
it_behaves_like "imports mappings from", GroupSpec::Ceramic, described_class
|
315
|
+
end
|
316
|
+
|
317
|
+
describe GroupSpec::Contributor do
|
318
|
+
it_behaves_like "imports attributes from", GroupSpec::ContributionInfo, described_class
|
121
319
|
end
|
122
320
|
|
123
|
-
|
124
|
-
|
321
|
+
context "when importing multiple models with overlapping attributes" do
|
322
|
+
let(:mrow_instance) do
|
323
|
+
GroupSpec::Mrow.new
|
324
|
+
end
|
325
|
+
|
326
|
+
it "uses Ceramic's default values for overlapping attributes" do
|
327
|
+
expect(mrow_instance.type).to eq("Data")
|
328
|
+
expect(mrow_instance.name).to eq("Starc")
|
329
|
+
end
|
330
|
+
|
331
|
+
it "maintains the correct XML serialization order from last import" do
|
332
|
+
xml = mrow_instance.to_xml
|
333
|
+
expected_xml = "<mrow xmlns:ex1='http://www.example.com' xmlns:GML='http://www.sparxsystems.com/profiles/GML/1.0'/>"
|
334
|
+
|
335
|
+
expect(xml).to be_equivalent_to(expected_xml)
|
336
|
+
end
|
125
337
|
end
|
126
338
|
|
127
|
-
|
128
|
-
|
339
|
+
context "when update the imported attribute" do
|
340
|
+
it "updates the attribute `mstyle` only in `Mrow`" do
|
341
|
+
GroupSpec::Mrow.attributes[:mstyle].instance_variable_set(:@type, :integer)
|
342
|
+
expect(GroupSpec::Mrow.attributes[:mstyle].type).to eq(:integer)
|
343
|
+
end
|
344
|
+
|
345
|
+
it "maintains original type for the attribute `mstyle` in `Mfrac`" do
|
346
|
+
expect(GroupSpec::Mfrac.attributes[:mstyle].type).to eq(Lutaml::Model::Type::String)
|
347
|
+
end
|
348
|
+
|
349
|
+
it "maintains original type for the attribute `mstyle` in importable class `CommonAttributes`" do
|
350
|
+
expect(GroupSpec::CommonAttributes.attributes[:mstyle].type).to eq(Lutaml::Model::Type::String)
|
351
|
+
end
|
129
352
|
end
|
130
353
|
|
131
|
-
|
132
|
-
|
133
|
-
|
354
|
+
context "when updating imported choice" do
|
355
|
+
it "updates choice min/max only in Mrow" do
|
356
|
+
choice = GroupSpec::Mrow.choice_attributes.first
|
357
|
+
choice.instance_variable_set(:@min, 2)
|
358
|
+
choice.instance_variable_set(:@max, 3)
|
359
|
+
|
360
|
+
expect(choice.min).to eq(2)
|
361
|
+
expect(choice.max).to eq(3)
|
362
|
+
end
|
363
|
+
|
364
|
+
it "maintains original choice min/max in Mfrac" do
|
365
|
+
choice = GroupSpec::Mfrac.choice_attributes.first
|
366
|
+
expect(choice.min).to eq(1)
|
367
|
+
expect(choice.max).to eq(1)
|
368
|
+
end
|
369
|
+
|
370
|
+
it "maintains original choice min/max in CommonAttributes" do
|
371
|
+
choice = GroupSpec::CommonAttributes.choice_attributes.first
|
372
|
+
expect(choice.min).to eq(1)
|
373
|
+
expect(choice.max).to eq(1)
|
374
|
+
end
|
134
375
|
end
|
135
376
|
|
136
|
-
|
137
|
-
|
138
|
-
|
377
|
+
context "when updating imported mappings" do
|
378
|
+
let(:new_namespace) { "http://www.example.com/new" }
|
379
|
+
let(:new_prefix) { "test" }
|
380
|
+
|
381
|
+
context "with element mappings" do
|
382
|
+
it "updates the mapping namespace only in `Mrow`" do
|
383
|
+
mapping = GroupSpec::Mrow.mappings_for(:xml).elements.find { |e| e.name == :mstyle }
|
384
|
+
mapping.instance_variable_set(:@namespace, new_namespace)
|
385
|
+
mapping.instance_variable_set(:@prefix, new_prefix)
|
386
|
+
|
387
|
+
expect(mapping.namespace).to eq(new_namespace)
|
388
|
+
expect(mapping.prefix).to eq(new_prefix)
|
389
|
+
end
|
390
|
+
|
391
|
+
it "maintains original namespace for `mstyle` mapping in `Mfrac`" do
|
392
|
+
mapping = GroupSpec::Mfrac.mappings_for(:xml).elements.find { |e| e.name == :mstyle }
|
393
|
+
expect(mapping.namespace).to be_nil
|
394
|
+
expect(mapping.prefix).to be_nil
|
395
|
+
end
|
396
|
+
|
397
|
+
it "maintains original namespace for `mstyle` mapping in `CommonAttributes`" do
|
398
|
+
mapping = GroupSpec::CommonAttributes.mappings_for(:xml).elements.find { |e| e.name == :mstyle }
|
399
|
+
expect(mapping.namespace).to be_nil
|
400
|
+
expect(mapping.prefix).to be_nil
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
context "with attribute mappings" do
|
405
|
+
it "updates attribute mapping only in `Mrow`" do
|
406
|
+
mapping = GroupSpec::Mrow.mappings_for(:xml).attributes.find { |a| a.name == :mcol }
|
407
|
+
mapping.instance_variable_set(:@namespace, new_namespace)
|
408
|
+
|
409
|
+
expect(mapping.namespace).to eq(new_namespace)
|
410
|
+
end
|
411
|
+
|
412
|
+
it "maintains original attribute mapping in `Mfrac`" do
|
413
|
+
mapping = GroupSpec::Mfrac.mappings_for(:xml).attributes.find { |a| a.name == :mcol }
|
414
|
+
expect(mapping.namespace).to be_nil
|
415
|
+
end
|
416
|
+
|
417
|
+
it "maintains original attribute mapping in `CommonAttributes`" do
|
418
|
+
mapping = GroupSpec::CommonAttributes.mappings_for(:xml).attributes.find { |a| a.name == :mcol }
|
419
|
+
expect(mapping.namespace).to be_nil
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
context "with sequence elements" do
|
424
|
+
it "updates sequence elements only in `Mrow`" do
|
425
|
+
sequence = GroupSpec::Mrow.mappings_for(:xml).element_sequence.first
|
426
|
+
sequence.attributes << Lutaml::Model::Xml::MappingRule.new(
|
427
|
+
"new_element",
|
428
|
+
to: :new_element,
|
429
|
+
namespace: "http://example.com",
|
430
|
+
prefix: "test",
|
431
|
+
)
|
432
|
+
|
433
|
+
expect(sequence.attributes.map(&:name)).to include("new_element")
|
434
|
+
end
|
435
|
+
|
436
|
+
it "maintains original sequence elements in `Mfrac`" do
|
437
|
+
original_sequence = GroupSpec::Mfrac.mappings_for(:xml).element_sequence.first
|
438
|
+
expect(original_sequence.attributes.map(&:name)).not_to include("new_element")
|
439
|
+
end
|
440
|
+
|
441
|
+
it "maintains original sequence elements in `CommonAttributes`" do
|
442
|
+
original_sequence = GroupSpec::CommonAttributes.mappings_for(:xml).element_sequence.first
|
443
|
+
expect(original_sequence.attributes.map(&:name)).not_to include("new_element")
|
444
|
+
end
|
445
|
+
|
446
|
+
it "creates new sequence object with the new mapping object as model of sequence in `Mrow`" do
|
447
|
+
mrow_sequence_model = GroupSpec::Mrow.mappings_for(:xml).element_sequence[0].model
|
448
|
+
common_attributes_sequence_model = GroupSpec::CommonAttributes.mappings_for(:xml).element_sequence[0].model
|
449
|
+
expect(mrow_sequence_model).not_to be(common_attributes_sequence_model)
|
450
|
+
end
|
451
|
+
end
|
139
452
|
end
|
140
453
|
|
141
454
|
it "raises error if root is defined on imported class" do
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe "HashAdapter" do
|
4
|
+
before do
|
5
|
+
stub_const("HashMappingSpec", Module.new)
|
6
|
+
|
7
|
+
simple_class = Class.new(Lutaml::Model::Serializable) do
|
8
|
+
attribute :name, :string
|
9
|
+
attribute :age, :integer
|
10
|
+
attribute :active, :boolean
|
11
|
+
|
12
|
+
hsh do
|
13
|
+
map "name", to: :name
|
14
|
+
map "age", to: :age
|
15
|
+
map "is_active", to: :active
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
stub_const("HashMappingSpec::SimpleClass", simple_class)
|
20
|
+
|
21
|
+
nested_class = Class.new(Lutaml::Model::Serializable) do
|
22
|
+
attribute :title, :string
|
23
|
+
attribute :simple, HashMappingSpec::SimpleClass
|
24
|
+
attribute :items, :string, collection: true
|
25
|
+
|
26
|
+
hsh do
|
27
|
+
map "title", to: :title
|
28
|
+
map "simple", to: :simple
|
29
|
+
map "items", to: :items
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
stub_const("HashMappingSpec::NestedClass", nested_class)
|
34
|
+
|
35
|
+
collection_class = Class.new(Lutaml::Model::Serializable) do
|
36
|
+
attribute :name, :string
|
37
|
+
attribute :simples, HashMappingSpec::SimpleClass, collection: true
|
38
|
+
|
39
|
+
hsh do
|
40
|
+
map "name", to: :name
|
41
|
+
map "simples", to: :simples
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
stub_const("HashMappingSpec::CollectionClass", collection_class)
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "from_hash" do
|
49
|
+
context "with simple attributes" do
|
50
|
+
let(:parsed) do
|
51
|
+
HashMappingSpec::SimpleClass.from_hash(hash_data)
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:hash_data) do
|
55
|
+
{
|
56
|
+
"name" => "John Doe",
|
57
|
+
"age" => 30,
|
58
|
+
"is_active" => true,
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:expected) do
|
63
|
+
HashMappingSpec::SimpleClass.new(
|
64
|
+
name: "John Doe",
|
65
|
+
age: 30,
|
66
|
+
active: true,
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "deserializes hash to object" do
|
71
|
+
expect(parsed).to eq(expected)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "with nested objects" do
|
76
|
+
let(:parsed) do
|
77
|
+
HashMappingSpec::NestedClass.from_hash(hash_data)
|
78
|
+
end
|
79
|
+
|
80
|
+
let(:hash_data) do
|
81
|
+
{
|
82
|
+
"title" => "Test Title",
|
83
|
+
"simple" => {
|
84
|
+
"name" => "Jane Doe",
|
85
|
+
"age" => 25,
|
86
|
+
"is_active" => false,
|
87
|
+
},
|
88
|
+
"items" => ["item1", "item2", "item3"],
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
let(:expected) do
|
93
|
+
HashMappingSpec::NestedClass.new(
|
94
|
+
title: "Test Title",
|
95
|
+
simple: HashMappingSpec::SimpleClass.new(
|
96
|
+
name: "Jane Doe",
|
97
|
+
age: 25,
|
98
|
+
active: false,
|
99
|
+
),
|
100
|
+
items: ["item1", "item2", "item3"],
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "deserializes nested objects" do
|
105
|
+
expect(parsed).to eq(expected)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "with collections of objects" do
|
110
|
+
let(:parsed) do
|
111
|
+
HashMappingSpec::CollectionClass.from_hash(hash_data)
|
112
|
+
end
|
113
|
+
|
114
|
+
let(:hash_data) do
|
115
|
+
{
|
116
|
+
"name" => "Collection",
|
117
|
+
"simples" => [
|
118
|
+
{ "name" => "First", "age" => 10, "is_active" => true },
|
119
|
+
{ "name" => "Second", "age" => 20, "is_active" => false },
|
120
|
+
],
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
let(:expected) do
|
125
|
+
HashMappingSpec::CollectionClass.new(
|
126
|
+
name: "Collection",
|
127
|
+
simples: [
|
128
|
+
HashMappingSpec::SimpleClass.new(
|
129
|
+
name: "First",
|
130
|
+
age: 10,
|
131
|
+
active: true,
|
132
|
+
),
|
133
|
+
HashMappingSpec::SimpleClass.new(
|
134
|
+
name: "Second",
|
135
|
+
age: 20,
|
136
|
+
active: false,
|
137
|
+
),
|
138
|
+
],
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "deserializes collections of objects" do
|
143
|
+
expect(parsed).to eq(expected)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "to_hash" do
|
149
|
+
context "with simple attributes" do
|
150
|
+
let(:instance) do
|
151
|
+
HashMappingSpec::SimpleClass.new(
|
152
|
+
name: "John Doe",
|
153
|
+
age: 30,
|
154
|
+
active: true,
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
let(:expected_hash) do
|
159
|
+
{
|
160
|
+
"name" => "John Doe",
|
161
|
+
"age" => 30,
|
162
|
+
"is_active" => true,
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
it "serializes object to hash" do
|
167
|
+
expect(instance.to_hash).to eq(expected_hash)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context "with nested objects" do
|
172
|
+
let(:simple) do
|
173
|
+
HashMappingSpec::SimpleClass.new(
|
174
|
+
name: "Jane Doe",
|
175
|
+
age: 25,
|
176
|
+
active: false,
|
177
|
+
)
|
178
|
+
end
|
179
|
+
|
180
|
+
let(:instance) do
|
181
|
+
HashMappingSpec::NestedClass.new(
|
182
|
+
title: "Test Title",
|
183
|
+
simple: simple,
|
184
|
+
items: ["item1", "item2", "item3"],
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
let(:expected_hash) do
|
189
|
+
{
|
190
|
+
"title" => "Test Title",
|
191
|
+
"simple" => {
|
192
|
+
"name" => "Jane Doe",
|
193
|
+
"age" => 25,
|
194
|
+
"is_active" => false,
|
195
|
+
},
|
196
|
+
"items" => ["item1", "item2", "item3"],
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
it "serializes nested objects to hash" do
|
201
|
+
expect(instance.to_hash).to eq(expected_hash)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context "with collections of objects" do
|
206
|
+
let(:simples) do
|
207
|
+
[
|
208
|
+
HashMappingSpec::SimpleClass.new(name: "First", age: 10, active: true),
|
209
|
+
HashMappingSpec::SimpleClass.new(name: "Second", age: 20, active: false),
|
210
|
+
]
|
211
|
+
end
|
212
|
+
|
213
|
+
let(:instance) do
|
214
|
+
HashMappingSpec::CollectionClass.new(
|
215
|
+
name: "Collection",
|
216
|
+
simples: simples,
|
217
|
+
)
|
218
|
+
end
|
219
|
+
|
220
|
+
let(:expected_hash) do
|
221
|
+
{
|
222
|
+
"name" => "Collection",
|
223
|
+
"simples" => [
|
224
|
+
{ "name" => "First", "age" => 10, "is_active" => true },
|
225
|
+
{ "name" => "Second", "age" => 20, "is_active" => false },
|
226
|
+
],
|
227
|
+
}
|
228
|
+
end
|
229
|
+
|
230
|
+
it "serializes collections of objects to hash" do
|
231
|
+
expect(instance.to_hash).to eq(expected_hash)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe "round-trip serialization" do
|
237
|
+
let(:original_hash) do
|
238
|
+
{
|
239
|
+
"name" => "Collection",
|
240
|
+
"simples" => [
|
241
|
+
{ "name" => "First", "age" => 10, "is_active" => true },
|
242
|
+
{ "name" => "Second", "age" => 20, "is_active" => false },
|
243
|
+
],
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
let(:parsed) do
|
248
|
+
HashMappingSpec::CollectionClass.from_hash(original_hash)
|
249
|
+
end
|
250
|
+
|
251
|
+
it "maintains data integrity through serialization and deserialization" do
|
252
|
+
expect(parsed.to_hash).to eq(original_hash)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|