lutaml-model 0.5.0 → 0.5.1
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/.rubocop_todo.yml +4 -4
- data/README.adoc +837 -184
- data/lib/lutaml/model/attribute.rb +6 -2
- data/lib/lutaml/model/error/collection_true_missing_error.rb +16 -0
- data/lib/lutaml/model/error/multiple_mappings_error.rb +6 -0
- data/lib/lutaml/model/error.rb +2 -0
- data/lib/lutaml/model/key_value_mapping.rb +26 -4
- data/lib/lutaml/model/key_value_mapping_rule.rb +15 -4
- data/lib/lutaml/model/loggable.rb +15 -0
- data/lib/lutaml/model/mapping_rule.rb +2 -2
- data/lib/lutaml/model/serialize.rb +50 -15
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/xml_document.rb +3 -3
- data/lib/lutaml/model/xml_mapping.rb +4 -0
- data/lib/lutaml/model/xml_mapping_rule.rb +2 -5
- data/lib/lutaml/model.rb +1 -0
- data/spec/lutaml/model/root_mappings_spec.rb +297 -0
- data/spec/lutaml/model/serializable_spec.rb +41 -6
- data/spec/lutaml/model/with_child_mapping_spec.rb +182 -0
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +66 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +8 -0
- metadata +6 -2
@@ -0,0 +1,297 @@
|
|
1
|
+
module RootMappingSpec
|
2
|
+
class CeramicDetails < Lutaml::Model::Serializable
|
3
|
+
attribute :name, :string
|
4
|
+
attribute :insignia, :string
|
5
|
+
|
6
|
+
key_value do
|
7
|
+
map "name", to: :name
|
8
|
+
map "insignia", to: :insignia
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class CeramicWithDetails < Lutaml::Model::Serializable
|
13
|
+
attribute :ceramic_id, :string
|
14
|
+
attribute :ceramic_type, :string
|
15
|
+
attribute :ceramic_details, CeramicDetails
|
16
|
+
attribute :ceramic_urn, :string
|
17
|
+
|
18
|
+
key_value do
|
19
|
+
map "id", to: :ceramic_id
|
20
|
+
map "type", to: :ceramic_type
|
21
|
+
map "details", to: :ceramic_details
|
22
|
+
map "urn", to: :ceramic_urn
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class CeramicCollectionWithKeyAndValue < Lutaml::Model::Serializable
|
27
|
+
attribute :ceramics, CeramicWithDetails, collection: true
|
28
|
+
|
29
|
+
key_value do
|
30
|
+
map to: :ceramics,
|
31
|
+
root_mappings: {
|
32
|
+
ceramic_id: :key,
|
33
|
+
ceramic_details: :value,
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class CeramicCollectionWithKeyAndComplexValue < Lutaml::Model::Serializable
|
39
|
+
attribute :ceramics, CeramicWithDetails, collection: true
|
40
|
+
|
41
|
+
key_value do
|
42
|
+
map to: :ceramics,
|
43
|
+
root_mappings: {
|
44
|
+
ceramic_id: :key,
|
45
|
+
ceramic_type: :type,
|
46
|
+
ceramic_details: "details",
|
47
|
+
ceramic_urn: ["urn", "primary"],
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Ceramic < Lutaml::Model::Serializable
|
53
|
+
attribute :ceramic_id, :string
|
54
|
+
attribute :ceramic_name, :string
|
55
|
+
|
56
|
+
key_value do
|
57
|
+
map "id", to: :ceramic_id
|
58
|
+
map "name", to: :ceramic_name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class CeramicCollectionWithKeyOnly < Lutaml::Model::Serializable
|
63
|
+
attribute :ceramics, Ceramic, collection: true
|
64
|
+
|
65
|
+
key_value do
|
66
|
+
map to: :ceramics, root_mappings: { ceramic_id: :key }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class CeramicCollectionWithoutCollectionTrue < Lutaml::Model::Serializable
|
71
|
+
attribute :ceramics, Ceramic
|
72
|
+
|
73
|
+
key_value do
|
74
|
+
map to: :ceramics, root_mappings: { ceramic_id: :key }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
RSpec.describe "RootMapping" do
|
80
|
+
shared_examples "having root mappings" do |format|
|
81
|
+
let(:adapter) do
|
82
|
+
Lutaml::Model::Config.public_send(:"#{format}_adapter")
|
83
|
+
end
|
84
|
+
|
85
|
+
let(:input) do
|
86
|
+
adapter.new(input_hash).public_send(:"to_#{format}")
|
87
|
+
end
|
88
|
+
|
89
|
+
# 1. Only map to `:key`. Then only override key, the rest of the mappings stay.
|
90
|
+
context "when only `key` is mapped" do
|
91
|
+
let(:parsed) do
|
92
|
+
RootMappingSpec::CeramicCollectionWithKeyOnly.public_send(:"from_#{format}", input)
|
93
|
+
end
|
94
|
+
|
95
|
+
let(:input_hash) do
|
96
|
+
{
|
97
|
+
"vase1" => { "name" => "Imperial Vase" },
|
98
|
+
"bowl2" => { "name" => "18th Century Bowl" },
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
let(:ceramic_vase) do
|
103
|
+
RootMappingSpec::Ceramic.new(
|
104
|
+
ceramic_id: "vase1",
|
105
|
+
ceramic_name: "Imperial Vase",
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
let(:ceramic_bowl) do
|
110
|
+
RootMappingSpec::Ceramic.new(
|
111
|
+
ceramic_id: "bowl2",
|
112
|
+
ceramic_name: "18th Century Bowl",
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "parses" do
|
117
|
+
expect(parsed.ceramics.count).to eq(2)
|
118
|
+
# Because Tomlib reverses the order of the hash, so can not check based on position
|
119
|
+
expect(parsed.ceramics).to include(ceramic_vase)
|
120
|
+
expect(parsed.ceramics).to include(ceramic_bowl)
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "serialize" do
|
124
|
+
let(:collection) do
|
125
|
+
RootMappingSpec::CeramicCollectionWithKeyOnly.new(ceramics: [
|
126
|
+
ceramic_vase,
|
127
|
+
ceramic_bowl,
|
128
|
+
])
|
129
|
+
end
|
130
|
+
|
131
|
+
it "serializes correctly" do
|
132
|
+
expect(collection.public_send(:"to_#{format}")).to eq(input)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# 2. Maps `:key` and another attribute, then we override all the other mappings (clean slate)
|
138
|
+
context "when `key` and `value` are mapped" do
|
139
|
+
let(:parsed) do
|
140
|
+
RootMappingSpec::CeramicCollectionWithKeyAndValue.public_send(:"from_#{format}", input)
|
141
|
+
end
|
142
|
+
|
143
|
+
let(:input_hash) do
|
144
|
+
{
|
145
|
+
"vase1" => { "name" => "Imperial Vase", "insignia" => "Tang Tianbao" },
|
146
|
+
"bowl2" => { "name" => "18th Century Bowl", "insignia" => "Ming Wanli" },
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
let(:vase_with_details) do
|
151
|
+
RootMappingSpec::CeramicWithDetails.new(
|
152
|
+
ceramic_id: "vase1",
|
153
|
+
ceramic_details: RootMappingSpec::CeramicDetails.new(
|
154
|
+
name: "Imperial Vase",
|
155
|
+
insignia: "Tang Tianbao",
|
156
|
+
),
|
157
|
+
)
|
158
|
+
end
|
159
|
+
|
160
|
+
let(:bowl_with_details) do
|
161
|
+
RootMappingSpec::CeramicWithDetails.new(
|
162
|
+
ceramic_id: "bowl2",
|
163
|
+
ceramic_details: RootMappingSpec::CeramicDetails.new(
|
164
|
+
name: "18th Century Bowl",
|
165
|
+
insignia: "Ming Wanli",
|
166
|
+
),
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "parses" do
|
171
|
+
expect(parsed.ceramics.count).to eq(2)
|
172
|
+
# Because Tomlib reverses the order of the hash, so can not check based on position
|
173
|
+
expect(parsed.ceramics).to include(vase_with_details)
|
174
|
+
expect(parsed.ceramics).to include(bowl_with_details)
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "serialize" do
|
178
|
+
let(:collection) do
|
179
|
+
RootMappingSpec::CeramicCollectionWithKeyAndValue.new(ceramics: [
|
180
|
+
vase_with_details,
|
181
|
+
bowl_with_details,
|
182
|
+
])
|
183
|
+
end
|
184
|
+
|
185
|
+
it "serializes correctly" do
|
186
|
+
expect(collection.public_send(:"to_#{format}")).to eq(input)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# 3. Maps `:key` and `:value`, then we map the key and the value body to the new mappings.
|
192
|
+
context "when `key` and complex value structure is mapped" do
|
193
|
+
let(:parsed) do
|
194
|
+
RootMappingSpec::CeramicCollectionWithKeyAndComplexValue.public_send(:"from_#{format}", input)
|
195
|
+
end
|
196
|
+
|
197
|
+
let(:input_hash) do
|
198
|
+
{
|
199
|
+
"vase1" => {
|
200
|
+
"type" => "vase",
|
201
|
+
"details" => {
|
202
|
+
"name" => "Imperial Vase",
|
203
|
+
"insignia" => "Tang Tianbao",
|
204
|
+
},
|
205
|
+
"urn" => {
|
206
|
+
"primary" => "urn:ceramic:vase:vase1",
|
207
|
+
},
|
208
|
+
},
|
209
|
+
"bowl2" => {
|
210
|
+
"type" => "bowl",
|
211
|
+
"details" => {
|
212
|
+
"name" => "18th Century Bowl",
|
213
|
+
"insignia" => "Ming Wanli",
|
214
|
+
},
|
215
|
+
"urn" => {
|
216
|
+
"primary" => "urn:ceramic:bowl:bowl2",
|
217
|
+
},
|
218
|
+
},
|
219
|
+
}
|
220
|
+
end
|
221
|
+
|
222
|
+
let(:vase_with_details) do
|
223
|
+
RootMappingSpec::CeramicWithDetails.new(
|
224
|
+
ceramic_id: "vase1",
|
225
|
+
ceramic_type: "vase",
|
226
|
+
ceramic_urn: "urn:ceramic:vase:vase1",
|
227
|
+
ceramic_details: RootMappingSpec::CeramicDetails.new(
|
228
|
+
name: "Imperial Vase",
|
229
|
+
insignia: "Tang Tianbao",
|
230
|
+
),
|
231
|
+
)
|
232
|
+
end
|
233
|
+
|
234
|
+
let(:bowl_with_details) do
|
235
|
+
RootMappingSpec::CeramicWithDetails.new(
|
236
|
+
ceramic_id: "bowl2",
|
237
|
+
ceramic_type: "bowl",
|
238
|
+
ceramic_urn: "urn:ceramic:bowl:bowl2",
|
239
|
+
ceramic_details: RootMappingSpec::CeramicDetails.new(
|
240
|
+
name: "18th Century Bowl",
|
241
|
+
insignia: "Ming Wanli",
|
242
|
+
),
|
243
|
+
)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "parses" do
|
247
|
+
expect(parsed.ceramics.count).to eq(2)
|
248
|
+
# Because Tomlib reverses the order of the hash, so can not check based on position
|
249
|
+
expect(parsed.ceramics).to include(vase_with_details)
|
250
|
+
expect(parsed.ceramics).to include(bowl_with_details)
|
251
|
+
end
|
252
|
+
|
253
|
+
describe "serialize from object" do
|
254
|
+
let(:collection) do
|
255
|
+
RootMappingSpec::CeramicCollectionWithKeyAndComplexValue.new(ceramics: [
|
256
|
+
vase_with_details,
|
257
|
+
bowl_with_details,
|
258
|
+
])
|
259
|
+
end
|
260
|
+
|
261
|
+
it "serializes correctly" do
|
262
|
+
expect(collection.public_send(:"to_#{format}")).to eq(input)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context "when `collection: true` is missing" do
|
268
|
+
let(:input_hash) do
|
269
|
+
{
|
270
|
+
"vase1" => { "name" => "Imperial Vase" },
|
271
|
+
"bowl2" => { "name" => "18th Century Bowl" },
|
272
|
+
}
|
273
|
+
end
|
274
|
+
|
275
|
+
it "raises error" do
|
276
|
+
expect do
|
277
|
+
RootMappingSpec::CeramicCollectionWithoutCollectionTrue.public_send(:"from_#{format}", input)
|
278
|
+
end.to raise_error(
|
279
|
+
Lutaml::Model::CollectionTrueMissingError,
|
280
|
+
"May be `collection: true` is missing for `ceramics` in RootMappingSpec::CeramicCollectionWithoutCollectionTrue",
|
281
|
+
)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe Lutaml::Model::YamlAdapter::StandardYamlAdapter do
|
287
|
+
it_behaves_like "having root mappings", :yaml
|
288
|
+
end
|
289
|
+
|
290
|
+
describe Lutaml::Model::JsonAdapter::StandardJsonAdapter do
|
291
|
+
it_behaves_like "having root mappings", :json
|
292
|
+
end
|
293
|
+
|
294
|
+
describe Lutaml::Model::TomlAdapter::TomlRbAdapter do
|
295
|
+
it_behaves_like "having root mappings", :toml
|
296
|
+
end
|
297
|
+
end
|
@@ -77,6 +77,25 @@ module SerializeableSpec
|
|
77
77
|
class GlazeTechnique < Lutaml::Model::Serializable
|
78
78
|
attribute :name, :string, values: ["Celadon", "Raku", "Majolica"]
|
79
79
|
end
|
80
|
+
|
81
|
+
class TranslateHelper < Lutaml::Model::Serializable
|
82
|
+
attribute :id, :string
|
83
|
+
attribute :path, :string
|
84
|
+
attribute :name, :string
|
85
|
+
end
|
86
|
+
|
87
|
+
class TranslateMappings < Lutaml::Model::Serializable
|
88
|
+
attribute :translate, TranslateHelper, collection: true
|
89
|
+
|
90
|
+
key_value do
|
91
|
+
map "translate", to: :translate, child_mappings:
|
92
|
+
{
|
93
|
+
id: :key,
|
94
|
+
path: %i[path link],
|
95
|
+
name: %i[path name],
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
80
99
|
end
|
81
100
|
|
82
101
|
RSpec.describe Lutaml::Model::Serializable do
|
@@ -173,7 +192,7 @@ RSpec.describe Lutaml::Model::Serializable do
|
|
173
192
|
end
|
174
193
|
end
|
175
194
|
|
176
|
-
describe ".
|
195
|
+
describe ".translate_mappings" do
|
177
196
|
let(:child_mappings) do
|
178
197
|
{
|
179
198
|
id: :key,
|
@@ -205,17 +224,33 @@ RSpec.describe Lutaml::Model::Serializable do
|
|
205
224
|
}
|
206
225
|
end
|
207
226
|
|
227
|
+
let(:attr) { SerializeableSpec::TranslateMappings.attributes[:translate] }
|
228
|
+
|
208
229
|
let(:expected_value) do
|
209
230
|
[
|
210
|
-
{
|
211
|
-
|
212
|
-
|
231
|
+
SerializeableSpec::TranslateHelper.new({
|
232
|
+
"id" => "foo",
|
233
|
+
"name" => "one",
|
234
|
+
"path" => "link one",
|
235
|
+
}),
|
236
|
+
SerializeableSpec::TranslateHelper.new({
|
237
|
+
"id" => "abc",
|
238
|
+
"name" => "two",
|
239
|
+
"path" => "link two",
|
240
|
+
}),
|
241
|
+
SerializeableSpec::TranslateHelper.new({
|
242
|
+
"id" => "hello",
|
243
|
+
"name" => "three",
|
244
|
+
"path" => "link three",
|
245
|
+
}),
|
213
246
|
]
|
214
247
|
end
|
215
248
|
|
216
249
|
it "generates hash based on child_mappings" do
|
217
|
-
|
218
|
-
|
250
|
+
actual_value = described_class.translate_mappings(hash, child_mappings, attr, :yaml)
|
251
|
+
|
252
|
+
expect(actual_value.map { |obj| [obj.id, obj.name, obj.path] })
|
253
|
+
.to eq(expected_value.map { |obj| [obj.id, obj.name, obj.path] })
|
219
254
|
end
|
220
255
|
end
|
221
256
|
|
@@ -37,6 +37,63 @@ module ChildMapping
|
|
37
37
|
}
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
class JS < Lutaml::Model::Serializable
|
42
|
+
attribute :prop, :string
|
43
|
+
attribute :hook, :string
|
44
|
+
|
45
|
+
key_value do
|
46
|
+
map :prop, to: :prop
|
47
|
+
map :hook, to: :hook
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Symbol < Lutaml::Model::Serializable
|
52
|
+
attribute :ascii, :string
|
53
|
+
attribute :html, :string
|
54
|
+
attribute :latex, :string
|
55
|
+
attribute :unicode, :string
|
56
|
+
attribute :js, JS
|
57
|
+
|
58
|
+
key_value do
|
59
|
+
map :ascii, to: :ascii
|
60
|
+
map :html, to: :html
|
61
|
+
map :latex, to: :latex
|
62
|
+
map :unicode, to: :unicode
|
63
|
+
map :js, to: :js
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Prefix < Lutaml::Model::Serializable
|
68
|
+
attribute :id, :string
|
69
|
+
attribute :name, :string
|
70
|
+
attribute :symbol, Symbol
|
71
|
+
attribute :base, :integer
|
72
|
+
attribute :power, :integer
|
73
|
+
|
74
|
+
key_value do
|
75
|
+
map :prefix_id, to: :id
|
76
|
+
map :name, to: :name
|
77
|
+
map :symbol, to: :symbol
|
78
|
+
map :base, to: :base
|
79
|
+
map :power, to: :power
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Prefixes < Lutaml::Model::Serializable
|
84
|
+
attribute :prefixes, Prefix, collection: true
|
85
|
+
|
86
|
+
key_value do
|
87
|
+
map "prefixes", to: :prefixes, child_mappings:
|
88
|
+
{
|
89
|
+
id: :key,
|
90
|
+
name: :name,
|
91
|
+
symbol: :symbol,
|
92
|
+
base: :base,
|
93
|
+
power: :power,
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
40
97
|
end
|
41
98
|
|
42
99
|
RSpec.describe ChildMapping do
|
@@ -72,11 +129,52 @@ RSpec.describe ChildMapping do
|
|
72
129
|
let(:expected_paths) { ["link one", "link two", "link three"] }
|
73
130
|
let(:expected_names) { ["one", "two", "three"] }
|
74
131
|
|
132
|
+
let(:prefixes_hash) do
|
133
|
+
{
|
134
|
+
"prefixes" => {
|
135
|
+
"NISTp10_30" => {
|
136
|
+
"name" => "quetta",
|
137
|
+
"symbol" => {
|
138
|
+
"ascii" => "Q",
|
139
|
+
"html" => "Q",
|
140
|
+
"latex" => "Q",
|
141
|
+
"unicode" => "Q",
|
142
|
+
"js" => {
|
143
|
+
"prop" => "head",
|
144
|
+
"hook" => "async",
|
145
|
+
},
|
146
|
+
},
|
147
|
+
"base" => 10,
|
148
|
+
"power" => 30,
|
149
|
+
},
|
150
|
+
"NISTp10_27" => {
|
151
|
+
"name" => "ronna",
|
152
|
+
"symbol" => {
|
153
|
+
"ascii" => "R",
|
154
|
+
"html" => "R",
|
155
|
+
"latex" => "R",
|
156
|
+
"unicode" => "R",
|
157
|
+
"js" => {
|
158
|
+
"prop" => "head",
|
159
|
+
"hook" => "async",
|
160
|
+
},
|
161
|
+
},
|
162
|
+
"base" => 10,
|
163
|
+
"power" => 27,
|
164
|
+
},
|
165
|
+
},
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
75
169
|
context "with json" do
|
76
170
|
let(:json) do
|
77
171
|
hash.to_json
|
78
172
|
end
|
79
173
|
|
174
|
+
let(:prefixes_json) do
|
175
|
+
prefixes_hash.to_json
|
176
|
+
end
|
177
|
+
|
80
178
|
describe ".from_json" do
|
81
179
|
it "create model according to json" do
|
82
180
|
instance = mapper.from_json(json)
|
@@ -86,6 +184,14 @@ RSpec.describe ChildMapping do
|
|
86
184
|
expect(instance.schemas.map(&:path)).to eq(expected_paths)
|
87
185
|
expect(instance.schemas.map(&:name)).to eq(expected_names)
|
88
186
|
end
|
187
|
+
|
188
|
+
it "create model according to json with nesting values" do
|
189
|
+
instance = ChildMapping::Prefixes.from_json(prefixes_json)
|
190
|
+
|
191
|
+
expect(instance.prefixes.first.id).to eq("NISTp10_30")
|
192
|
+
expect(instance.prefixes.first.symbol.ascii).to eq("Q")
|
193
|
+
expect(instance.prefixes.first.symbol.js.hook).to eq("async")
|
194
|
+
end
|
89
195
|
end
|
90
196
|
|
91
197
|
describe ".to_json" do
|
@@ -98,6 +204,13 @@ RSpec.describe ChildMapping do
|
|
98
204
|
|
99
205
|
expect(instance.to_json).to eq(json)
|
100
206
|
end
|
207
|
+
|
208
|
+
it "converts object to json with nesting values" do
|
209
|
+
instance = ChildMapping::Prefixes.from_json(prefixes_json)
|
210
|
+
serialized = instance.to_json
|
211
|
+
|
212
|
+
expect(serialized).to be_equivalent_to(prefixes_json)
|
213
|
+
end
|
101
214
|
end
|
102
215
|
end
|
103
216
|
|
@@ -106,6 +219,10 @@ RSpec.describe ChildMapping do
|
|
106
219
|
hash.to_yaml
|
107
220
|
end
|
108
221
|
|
222
|
+
let(:prefixes_yaml) do
|
223
|
+
prefixes_hash.to_yaml
|
224
|
+
end
|
225
|
+
|
109
226
|
describe ".from_yaml" do
|
110
227
|
it "create model according to yaml" do
|
111
228
|
instance = mapper.from_yaml(yaml)
|
@@ -115,6 +232,14 @@ RSpec.describe ChildMapping do
|
|
115
232
|
expect(instance.schemas.map(&:path)).to eq(expected_paths)
|
116
233
|
expect(instance.schemas.map(&:name)).to eq(expected_names)
|
117
234
|
end
|
235
|
+
|
236
|
+
it "create model according to yaml with nesting values" do
|
237
|
+
instance = ChildMapping::Prefixes.from_yaml(prefixes_yaml)
|
238
|
+
|
239
|
+
expect(instance.prefixes.first.id).to eq("NISTp10_30")
|
240
|
+
expect(instance.prefixes.first.symbol.ascii).to eq("Q")
|
241
|
+
expect(instance.prefixes.first.symbol.js.hook).to eq("async")
|
242
|
+
end
|
118
243
|
end
|
119
244
|
|
120
245
|
describe ".to_yaml" do
|
@@ -127,6 +252,13 @@ RSpec.describe ChildMapping do
|
|
127
252
|
|
128
253
|
expect(instance.to_yaml).to eq(yaml)
|
129
254
|
end
|
255
|
+
|
256
|
+
it "converts object to yaml with nesting values" do
|
257
|
+
instance = ChildMapping::Prefixes.from_yaml(prefixes_yaml)
|
258
|
+
serialized = instance.to_yaml
|
259
|
+
|
260
|
+
expect(YAML.safe_load(serialized)).to eq(YAML.safe_load(prefixes_yaml))
|
261
|
+
end
|
130
262
|
end
|
131
263
|
end
|
132
264
|
|
@@ -145,6 +277,40 @@ RSpec.describe ChildMapping do
|
|
145
277
|
TOML
|
146
278
|
end
|
147
279
|
|
280
|
+
let(:prefixes_toml) do
|
281
|
+
<<~TOML
|
282
|
+
[prefixes.NISTp10_30]
|
283
|
+
name = "quetta"
|
284
|
+
base = 10
|
285
|
+
power = 30
|
286
|
+
|
287
|
+
[prefixes.NISTp10_30.symbol]
|
288
|
+
ascii = "Q"
|
289
|
+
html = "Q"
|
290
|
+
latex = "Q"
|
291
|
+
unicode = "Q"
|
292
|
+
|
293
|
+
[prefixes.NISTp10_30.symbol.js]
|
294
|
+
prop = "head"
|
295
|
+
hook = "async"
|
296
|
+
|
297
|
+
[prefixes.NISTp10_27]
|
298
|
+
name = "ronna"
|
299
|
+
base = 10
|
300
|
+
power = 27
|
301
|
+
|
302
|
+
[prefixes.NISTp10_27.symbol]
|
303
|
+
ascii = "R"
|
304
|
+
html = "R"
|
305
|
+
latex = "R"
|
306
|
+
unicode = "R"
|
307
|
+
|
308
|
+
[prefixes.NISTp10_27.symbol.js]
|
309
|
+
prop = "head"
|
310
|
+
hook = "async"
|
311
|
+
TOML
|
312
|
+
end
|
313
|
+
|
148
314
|
describe ".from_toml" do
|
149
315
|
it "create model according to toml" do
|
150
316
|
instance = mapper.from_toml(toml)
|
@@ -154,6 +320,14 @@ RSpec.describe ChildMapping do
|
|
154
320
|
expect(instance.schemas.map(&:path)).to eq(expected_paths)
|
155
321
|
expect(instance.schemas.map(&:name)).to eq(expected_names)
|
156
322
|
end
|
323
|
+
|
324
|
+
it "create model according to toml with nesting values" do
|
325
|
+
instance = ChildMapping::Prefixes.from_toml(prefixes_toml)
|
326
|
+
|
327
|
+
expect(instance.prefixes.first.id).to eq("NISTp10_30")
|
328
|
+
expect(instance.prefixes.first.symbol.ascii).to eq("Q")
|
329
|
+
expect(instance.prefixes.first.symbol.js.hook).to eq("async")
|
330
|
+
end
|
157
331
|
end
|
158
332
|
|
159
333
|
describe ".to_toml" do
|
@@ -169,6 +343,14 @@ RSpec.describe ChildMapping do
|
|
169
343
|
|
170
344
|
expect(actual.attributes).to eq(expected.attributes)
|
171
345
|
end
|
346
|
+
|
347
|
+
it "converts object to toml with nesting values" do
|
348
|
+
instance = ChildMapping::Prefixes.from_toml(prefixes_toml)
|
349
|
+
actual = Lutaml::Model::Config.toml_adapter.parse(instance.to_toml)
|
350
|
+
expected = Lutaml::Model::Config.toml_adapter.parse(prefixes_toml)
|
351
|
+
|
352
|
+
expect(actual.attributes).to eq(expected.attributes)
|
353
|
+
end
|
172
354
|
end
|
173
355
|
end
|
174
356
|
end
|
@@ -86,6 +86,43 @@ RSpec.shared_context "XML namespace models" do
|
|
86
86
|
prefix: nil
|
87
87
|
end
|
88
88
|
end
|
89
|
+
|
90
|
+
class Body < Lutaml::Model::Serializable
|
91
|
+
attribute :paragraph, :string
|
92
|
+
|
93
|
+
xml do
|
94
|
+
map_element "p", to: :paragraph
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Element < Lutaml::Model::Serializable
|
99
|
+
attribute :text, :string
|
100
|
+
xml do
|
101
|
+
root "test-element"
|
102
|
+
namespace "http://www.test.com/schemas/test/1.0/", "test"
|
103
|
+
map_content to: :text
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class Front < Lutaml::Model::Serializable
|
108
|
+
attribute :test_element, Element
|
109
|
+
|
110
|
+
xml do
|
111
|
+
namespace "http://www.test.com/schemas/test/1.0/", "test"
|
112
|
+
map_element "test-element", to: :test_element
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class Article < Lutaml::Model::Serializable
|
117
|
+
attribute :front, Front
|
118
|
+
attribute :body, Body
|
119
|
+
|
120
|
+
xml do
|
121
|
+
root "article"
|
122
|
+
map_element "front", to: :front, prefix: "test", namespace: "http://www.test.com/schemas/test/1.0/"
|
123
|
+
map_element "body", to: :body
|
124
|
+
end
|
125
|
+
end
|
89
126
|
end
|
90
127
|
|
91
128
|
RSpec.shared_examples "XML serialization with namespace" do |model_class, xml_string|
|
@@ -236,6 +273,35 @@ RSpec.shared_examples "an XML namespace parser" do |adapter_class|
|
|
236
273
|
expect(generated_xml).to be_equivalent_to(xml)
|
237
274
|
end
|
238
275
|
end
|
276
|
+
|
277
|
+
context "when custom namespace is used" do
|
278
|
+
let(:xml_input) do
|
279
|
+
<<~XML
|
280
|
+
<article xmlns:test="http://www.test.com/schemas/test/1.0/">
|
281
|
+
<test:front>
|
282
|
+
<test:test-element>Text Here</test:test-element>
|
283
|
+
</test:front>
|
284
|
+
<body>
|
285
|
+
<p>This is a paragraph</p>
|
286
|
+
</body>
|
287
|
+
</article>
|
288
|
+
XML
|
289
|
+
end
|
290
|
+
|
291
|
+
describe "XML serialization" do
|
292
|
+
it "correctly deserializes from XML" do
|
293
|
+
article = Article.from_xml(xml_input)
|
294
|
+
expect(article.body.paragraph).to eq("This is a paragraph")
|
295
|
+
end
|
296
|
+
|
297
|
+
it "round-trips XML" do
|
298
|
+
article = Article.from_xml(xml_input)
|
299
|
+
output_xml = article.to_xml(pretty: true)
|
300
|
+
|
301
|
+
expect(output_xml).to be_equivalent_to(xml_input)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
239
305
|
end
|
240
306
|
|
241
307
|
RSpec.describe Lutaml::Model::XmlAdapter::NokogiriAdapter do
|