lutaml-model 0.5.4 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +58 -21
- data/Gemfile +1 -0
- data/README.adoc +1112 -264
- data/lib/lutaml/model/attribute.rb +33 -10
- data/lib/lutaml/model/choice.rb +56 -0
- data/lib/lutaml/model/config.rb +1 -0
- data/lib/lutaml/model/error/choice_lower_bound_error.rb +9 -0
- data/lib/lutaml/model/error/choice_upper_bound_error.rb +9 -0
- data/lib/lutaml/model/error/import_model_with_root_error.rb +9 -0
- data/lib/lutaml/model/error/incorrect_sequence_error.rb +9 -0
- data/lib/lutaml/model/error/invalid_choice_range_error.rb +20 -0
- data/lib/lutaml/model/error/no_root_mapping_error.rb +9 -0
- data/lib/lutaml/model/error/no_root_namespace_error.rb +9 -0
- data/lib/lutaml/model/error/unknown_sequence_mapping_error.rb +9 -0
- data/lib/lutaml/model/error.rb +8 -0
- data/lib/lutaml/model/json_adapter/standard_json_adapter.rb +6 -1
- data/lib/lutaml/model/key_value_mapping.rb +3 -1
- data/lib/lutaml/model/key_value_mapping_rule.rb +4 -2
- data/lib/lutaml/model/liquefiable.rb +59 -0
- data/lib/lutaml/model/mapping_hash.rb +1 -1
- data/lib/lutaml/model/mapping_rule.rb +15 -2
- data/lib/lutaml/model/schema/xml_compiler.rb +68 -26
- data/lib/lutaml/model/schema_location.rb +7 -0
- data/lib/lutaml/model/sequence.rb +71 -0
- data/lib/lutaml/model/serialize.rb +125 -35
- data/lib/lutaml/model/type/decimal.rb +0 -4
- data/lib/lutaml/model/type/time.rb +3 -3
- data/lib/lutaml/model/utils.rb +19 -15
- data/lib/lutaml/model/validation.rb +12 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/oga.rb +10 -7
- data/lib/lutaml/model/xml_adapter/builder/ox.rb +20 -13
- data/lib/lutaml/model/xml_adapter/element.rb +32 -0
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +8 -8
- data/lib/lutaml/model/xml_adapter/oga/element.rb +14 -13
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +86 -19
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +19 -15
- data/lib/lutaml/model/xml_adapter/xml_document.rb +74 -13
- data/lib/lutaml/model/xml_adapter/xml_element.rb +57 -3
- data/lib/lutaml/model/xml_mapping.rb +49 -7
- data/lib/lutaml/model/xml_mapping_rule.rb +8 -3
- data/lib/lutaml/model.rb +1 -0
- data/lutaml-model.gemspec +5 -0
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +75 -0
- data/spec/ceramic_spec.rb +39 -0
- data/spec/fixtures/ceramic.rb +23 -0
- data/spec/fixtures/xml/address_example_260.xsd +9 -0
- data/spec/fixtures/xml/user.xsd +10 -0
- data/spec/lutaml/model/cdata_spec.rb +4 -5
- data/spec/lutaml/model/choice_spec.rb +168 -0
- data/spec/lutaml/model/collection_spec.rb +1 -1
- data/spec/lutaml/model/custom_model_spec.rb +6 -7
- data/spec/lutaml/model/custom_serialization_spec.rb +74 -2
- data/spec/lutaml/model/defaults_spec.rb +3 -1
- data/spec/lutaml/model/delegation_spec.rb +7 -5
- data/spec/lutaml/model/enum_spec.rb +35 -0
- data/spec/lutaml/model/group_spec.rb +160 -0
- data/spec/lutaml/model/inheritance_spec.rb +25 -0
- data/spec/lutaml/model/liquefiable_spec.rb +121 -0
- data/spec/lutaml/model/mixed_content_spec.rb +80 -41
- data/spec/lutaml/model/multiple_mapping_spec.rb +22 -10
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +218 -25
- data/spec/lutaml/model/sequence_spec.rb +216 -0
- data/spec/lutaml/model/transformation_spec.rb +230 -0
- data/spec/lutaml/model/type_spec.rb +138 -31
- data/spec/lutaml/model/utils_spec.rb +32 -0
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -7
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +51 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +143 -112
- metadata +67 -2
@@ -0,0 +1,230 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe "Value Transformations" do
|
4
|
+
module TransformationSpec
|
5
|
+
# Class with only attribute-level transformations
|
6
|
+
class AttributeTransformPerson < Lutaml::Model::Serializable
|
7
|
+
attribute :name, :string, transform: {
|
8
|
+
export: ->(value) { value.to_s.upcase },
|
9
|
+
}
|
10
|
+
attribute :email, :string, transform: {
|
11
|
+
import: ->(value) { "#{value}@example.com" },
|
12
|
+
}
|
13
|
+
attribute :tags, :string, collection: true, transform: {
|
14
|
+
export: ->(value) { value.map(&:upcase) },
|
15
|
+
import: ->(value) { value.map { |v| "#{v}-1" } },
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Class with only mapping-level transformations
|
20
|
+
class MappingTransformPerson < Lutaml::Model::Serializable
|
21
|
+
attribute :name, :string
|
22
|
+
attribute :email, :string
|
23
|
+
attribute :tags, :string, collection: true
|
24
|
+
|
25
|
+
json do
|
26
|
+
map "fullName", to: :name, transform: {
|
27
|
+
export: ->(value) { "Dr. #{value}" },
|
28
|
+
}
|
29
|
+
map "emailAddress", to: :email, transform: {
|
30
|
+
import: ->(value) { value.gsub("at", "@") },
|
31
|
+
}
|
32
|
+
map "labels", to: :tags, transform: {
|
33
|
+
export: ->(value) { value.join("-|-") },
|
34
|
+
import: ->(value) { value.split("|") },
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
xml do
|
39
|
+
root "person"
|
40
|
+
map_element "full-name", to: :name, transform: {
|
41
|
+
export: ->(value) { "Dr. #{value}" },
|
42
|
+
}
|
43
|
+
map_element "email-address", to: :email, transform: {
|
44
|
+
import: ->(value) { value.gsub("at", "@") },
|
45
|
+
}
|
46
|
+
map_element "labels", to: :tags, transform: {
|
47
|
+
export: ->(value) { value.join("-|-") },
|
48
|
+
import: ->(value) { value.split("|") },
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Class with both attribute and mapping transformations
|
54
|
+
class CombinedTransformPerson < Lutaml::Model::Serializable
|
55
|
+
attribute :name, :string, transform: {
|
56
|
+
export: ->(value) { value.to_s.capitalize },
|
57
|
+
import: ->(value) { value.to_s.downcase },
|
58
|
+
}
|
59
|
+
attribute :email, :string, transform: {
|
60
|
+
export: lambda(&:downcase),
|
61
|
+
import: lambda(&:downcase),
|
62
|
+
}
|
63
|
+
attribute :tags, :string, collection: true, transform: {
|
64
|
+
export: ->(value) { value.map(&:upcase) },
|
65
|
+
import: ->(value) { value.map { |v| "#{v}-1" } },
|
66
|
+
}
|
67
|
+
|
68
|
+
json do
|
69
|
+
map "fullName", to: :name, transform: {
|
70
|
+
export: ->(value) { "Prof. #{value}" },
|
71
|
+
import: ->(value) { value.gsub("Prof. ", "") },
|
72
|
+
}
|
73
|
+
map "contactEmail", to: :email, transform: {
|
74
|
+
export: ->(value) { "contact+#{value}" },
|
75
|
+
import: ->(value) { value.gsub("contact+", "") },
|
76
|
+
}
|
77
|
+
map "skills", to: :tags
|
78
|
+
end
|
79
|
+
|
80
|
+
xml do
|
81
|
+
root "person"
|
82
|
+
map_element "full-name", to: :name, transform: {
|
83
|
+
export: ->(value) { "Prof. #{value}" },
|
84
|
+
import: ->(value) { value.gsub("Prof. ", "") },
|
85
|
+
}
|
86
|
+
map_element "contact-email", to: :email, transform: {
|
87
|
+
export: ->(value) { "contact+#{value}" },
|
88
|
+
import: ->(value) { value.gsub("contact+", "") },
|
89
|
+
}
|
90
|
+
map_element "skills", to: :tags
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "Attribute-only transformations" do
|
96
|
+
let(:attribute_person) do
|
97
|
+
TransformationSpec::AttributeTransformPerson.new(
|
98
|
+
name: "john",
|
99
|
+
email: "smith",
|
100
|
+
tags: ["ruby", "rails"],
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
let(:expected_xml) do
|
105
|
+
<<~XML
|
106
|
+
<AttributeTransformPerson>
|
107
|
+
<name>JOHN</name>
|
108
|
+
<email>smith</email>
|
109
|
+
<tags>RUBY</tags>
|
110
|
+
<tags>RAILS</tags>
|
111
|
+
</AttributeTransformPerson>
|
112
|
+
XML
|
113
|
+
end
|
114
|
+
|
115
|
+
it "applies attribute transformations during serialization" do
|
116
|
+
parsed_json = JSON.parse(attribute_person.to_json)
|
117
|
+
expect(parsed_json["name"]).to eq("JOHN")
|
118
|
+
expect(parsed_json["email"]).to eq("smith")
|
119
|
+
expect(parsed_json["tags"]).to eq(["RUBY", "RAILS"])
|
120
|
+
end
|
121
|
+
|
122
|
+
it "applies attribute transformations during deserialization" do
|
123
|
+
json = {
|
124
|
+
"name" => "jane",
|
125
|
+
"email" => "doe",
|
126
|
+
"tags" => ["python", "django"],
|
127
|
+
}.to_json
|
128
|
+
|
129
|
+
parsed = TransformationSpec::AttributeTransformPerson.from_json(json)
|
130
|
+
expect(parsed.name).to eq("jane")
|
131
|
+
expect(parsed.email).to eq("doe@example.com")
|
132
|
+
expect(parsed.tags).to eq(["python-1", "django-1"])
|
133
|
+
end
|
134
|
+
|
135
|
+
it "applies attribute transformations during XML serialization" do
|
136
|
+
xml = attribute_person.to_xml
|
137
|
+
expect(xml).to be_equivalent_to(expected_xml)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "Mapping-only transformations" do
|
142
|
+
let(:mapping_person) do
|
143
|
+
TransformationSpec::MappingTransformPerson.new(
|
144
|
+
name: "alice",
|
145
|
+
email: "aliceattest.com",
|
146
|
+
tags: ["developer", "architect"],
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
let(:expected_xml) do
|
151
|
+
<<~XML
|
152
|
+
<person>
|
153
|
+
<full-name>Dr. alice</full-name>
|
154
|
+
<email-address>aliceattest.com</email-address>
|
155
|
+
<labels>developer-|-architect</labels>
|
156
|
+
</person>
|
157
|
+
XML
|
158
|
+
end
|
159
|
+
|
160
|
+
it "applies mapping transformations during JSON serialization" do
|
161
|
+
json = mapping_person.to_json
|
162
|
+
parsed = JSON.parse(json)
|
163
|
+
expect(parsed["fullName"]).to eq("Dr. alice")
|
164
|
+
expect(parsed["emailAddress"]).to eq("aliceattest.com")
|
165
|
+
expect(parsed["labels"]).to eq("developer-|-architect")
|
166
|
+
end
|
167
|
+
|
168
|
+
it "applies mapping transformations during JSON deserialization" do
|
169
|
+
json = {
|
170
|
+
"fullName" => "Dr. bob",
|
171
|
+
"emailAddress" => "bobattest.com",
|
172
|
+
"labels" => "senior|lead",
|
173
|
+
}.to_json
|
174
|
+
|
175
|
+
parsed = TransformationSpec::MappingTransformPerson.from_json(json)
|
176
|
+
expect(parsed.name).to eq("Dr. bob")
|
177
|
+
expect(parsed.email).to eq("bob@test.com")
|
178
|
+
expect(parsed.tags).to eq(["senior", "lead"])
|
179
|
+
end
|
180
|
+
|
181
|
+
it "applies mapping transformations during XML serialization" do
|
182
|
+
xml = mapping_person.to_xml
|
183
|
+
expect(xml).to be_equivalent_to(expected_xml)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "Combined transformations" do
|
188
|
+
let(:combined_person) do
|
189
|
+
TransformationSpec::CombinedTransformPerson.new(
|
190
|
+
name: "carol",
|
191
|
+
email: "CAROL@TEST.COM",
|
192
|
+
tags: ["manager", "agile"],
|
193
|
+
)
|
194
|
+
end
|
195
|
+
|
196
|
+
let(:expected_xml) do
|
197
|
+
<<~XML
|
198
|
+
<person>
|
199
|
+
<full-name>Prof. carol</full-name>
|
200
|
+
<contact-email>contact+CAROL@TEST.COM</contact-email>
|
201
|
+
<skills>MANAGER-1</skills>
|
202
|
+
<skills>AGILE-1</skills>
|
203
|
+
</person>
|
204
|
+
XML
|
205
|
+
end
|
206
|
+
|
207
|
+
it "applies both transformations with correct precedence in JSON" do
|
208
|
+
json = combined_person.to_json
|
209
|
+
parsed = JSON.parse(json)
|
210
|
+
|
211
|
+
expect(parsed["fullName"]).to eq("Prof. carol")
|
212
|
+
expect(parsed["contactEmail"]).to eq("contact+CAROL@TEST.COM")
|
213
|
+
end
|
214
|
+
|
215
|
+
it "handles round-trip transformations across formats" do
|
216
|
+
# JSON -> Key-Value -> JSON cycle
|
217
|
+
json = combined_person.to_json
|
218
|
+
parsed_json = TransformationSpec::CombinedTransformPerson.from_json(json)
|
219
|
+
expect(parsed_json.name).to eq("carol")
|
220
|
+
expect(parsed_json.email).to eq("CAROL@TEST.COM")
|
221
|
+
expect(parsed_json.tags).to eq(["MANAGER-1", "AGILE-1"])
|
222
|
+
end
|
223
|
+
|
224
|
+
it "applies both transformations with correct precedence in XML" do
|
225
|
+
xml = combined_person.to_xml
|
226
|
+
parsed_xml = TransformationSpec::CombinedTransformPerson.from_xml(xml)
|
227
|
+
expect(parsed_xml.to_xml).to be_equivalent_to(expected_xml)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -2,16 +2,47 @@
|
|
2
2
|
require "spec_helper"
|
3
3
|
require "bigdecimal"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
class CustomSerializationType < Lutaml::Model::Type::Value
|
6
|
+
def self.from_xml(_xml_string)
|
7
|
+
"from_xml_overrided"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.from_json(_value)
|
11
|
+
"from_json_overrided"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.serialize(_value)
|
15
|
+
"serialize_overrided"
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_xml
|
19
|
+
"to_xml_overrided"
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_json(*_args)
|
23
|
+
"to_json_overrided"
|
9
24
|
end
|
10
25
|
end
|
11
26
|
|
12
|
-
class
|
13
|
-
|
14
|
-
|
27
|
+
class SampleModel < Lutaml::Model::Serializable
|
28
|
+
attribute :custom_type, CustomSerializationType
|
29
|
+
xml do
|
30
|
+
root "sample"
|
31
|
+
map_element "custom_type", to: :custom_type
|
32
|
+
end
|
33
|
+
json do
|
34
|
+
map_element "custom_type", to: :custom_type
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class SampleModelAttribute < Lutaml::Model::Serializable
|
39
|
+
attribute :custom_type, CustomSerializationType
|
40
|
+
xml do
|
41
|
+
root "sample"
|
42
|
+
map_attribute "custom_type", to: :custom_type
|
43
|
+
end
|
44
|
+
json do
|
45
|
+
map_element "custom_type", to: :custom_type
|
15
46
|
end
|
16
47
|
end
|
17
48
|
|
@@ -19,6 +50,17 @@ RSpec.describe Lutaml::Model::Type do
|
|
19
50
|
describe "Type System" do
|
20
51
|
describe ".register and .lookup" do
|
21
52
|
context "with valid types" do
|
53
|
+
before do
|
54
|
+
# Test class for type registration scenarios
|
55
|
+
custom_type = Class.new(Lutaml::Model::Type::Value) do
|
56
|
+
def self.cast(value)
|
57
|
+
value.to_s.upcase
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
stub_const("CustomType", custom_type)
|
62
|
+
end
|
63
|
+
|
22
64
|
it "registers and looks up a custom type" do
|
23
65
|
described_class.register(:custom, CustomType)
|
24
66
|
expect(described_class.lookup(:custom)).to eq(CustomType)
|
@@ -33,6 +75,16 @@ RSpec.describe Lutaml::Model::Type do
|
|
33
75
|
end
|
34
76
|
|
35
77
|
context "with invalid types" do
|
78
|
+
before do
|
79
|
+
invalid_type = Class.new do
|
80
|
+
def self.cast(value)
|
81
|
+
value
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
stub_const("InvalidType", invalid_type)
|
86
|
+
end
|
87
|
+
|
36
88
|
it "raises TypeError when registering non-Type::Value class" do
|
37
89
|
expect do
|
38
90
|
described_class.register(:invalid,
|
@@ -159,31 +211,35 @@ RSpec.describe Lutaml::Model::Type do
|
|
159
211
|
end
|
160
212
|
|
161
213
|
describe "Type Usage in Models" do
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
214
|
+
before do
|
215
|
+
type_test_model = Class.new(Lutaml::Model::Serializable) do
|
216
|
+
attribute :string_symbol, :string
|
217
|
+
attribute :string_class, Lutaml::Model::Type::String
|
218
|
+
attribute :integer_value, :integer
|
219
|
+
attribute :float_value, :float
|
220
|
+
attribute :date_value, :date
|
221
|
+
attribute :time_value, :time
|
222
|
+
attribute :time_without_date_value, :time_without_date
|
223
|
+
attribute :date_time_value, :date_time
|
224
|
+
attribute :boolean_value, :boolean
|
225
|
+
attribute :hash_value, :hash
|
226
|
+
|
227
|
+
xml do
|
228
|
+
root "test"
|
229
|
+
map_element "string_symbol", to: :string_symbol
|
230
|
+
map_element "string_class", to: :string_class
|
231
|
+
map_element "integer", to: :integer_value
|
232
|
+
map_element "float", to: :float_value
|
233
|
+
map_element "date", to: :date_value
|
234
|
+
map_element "time", to: :time_value
|
235
|
+
map_element "time_without_date", to: :time_without_date_value
|
236
|
+
map_element "date_time", to: :date_time_value
|
237
|
+
map_element "boolean", to: :boolean_value
|
238
|
+
map_element "hash", to: :hash_value
|
239
|
+
end
|
186
240
|
end
|
241
|
+
|
242
|
+
stub_const("TypeTestModel", type_test_model)
|
187
243
|
end
|
188
244
|
|
189
245
|
let(:test_instance) do
|
@@ -272,5 +328,56 @@ RSpec.describe Lutaml::Model::Type do
|
|
272
328
|
expect(deserialized.hash_value).to eq({ "key" => "value" })
|
273
329
|
end
|
274
330
|
end
|
331
|
+
|
332
|
+
describe "Serialization Of Custom Type" do
|
333
|
+
let(:xml) do
|
334
|
+
<<~XML
|
335
|
+
<sample>
|
336
|
+
<custom_type>test_string</custom_type>
|
337
|
+
</sample>
|
338
|
+
XML
|
339
|
+
end
|
340
|
+
|
341
|
+
let(:xml_attribute) do
|
342
|
+
<<~XML
|
343
|
+
<sample custom_type="test_string"/>
|
344
|
+
XML
|
345
|
+
end
|
346
|
+
|
347
|
+
let(:sample_instance) { SampleModel.from_xml(xml) }
|
348
|
+
let(:sample_instance_attribute) do
|
349
|
+
SampleModelAttribute.from_xml(xml_attribute)
|
350
|
+
end
|
351
|
+
|
352
|
+
it "correctly serializes to XML" do
|
353
|
+
expected_xml = <<~XML
|
354
|
+
<sample>
|
355
|
+
<custom_type>to_xml_overrided</custom_type>
|
356
|
+
</sample>
|
357
|
+
XML
|
358
|
+
expect(sample_instance.to_xml).to be_equivalent_to(expected_xml)
|
359
|
+
end
|
360
|
+
|
361
|
+
it "correctly serializes to XML attribute" do
|
362
|
+
expected_xml = <<~XML
|
363
|
+
<sample custom_type="to_xml_overrided"/>
|
364
|
+
XML
|
365
|
+
expect(sample_instance_attribute.to_xml).to be_equivalent_to(expected_xml)
|
366
|
+
end
|
367
|
+
|
368
|
+
it "correctly serializes to JSON" do
|
369
|
+
expect(sample_instance.to_json).to eq('{"custom_type":"to_json_overrided"}')
|
370
|
+
end
|
371
|
+
|
372
|
+
it "correctly deserializes from XML" do
|
373
|
+
expect(sample_instance.custom_type).to eq("from_xml_overrided")
|
374
|
+
end
|
375
|
+
|
376
|
+
it "correctly deserializes from JSON" do
|
377
|
+
json_sample_instance = SampleModel.from_json('{"custom_type":"test_string"}')
|
378
|
+
json_sample_instance.to_json
|
379
|
+
expect(json_sample_instance.custom_type).to eq("from_json_overrided")
|
380
|
+
end
|
381
|
+
end
|
275
382
|
end
|
276
383
|
end
|
@@ -54,7 +54,19 @@ RSpec.describe Lutaml::Model::Utils do
|
|
54
54
|
}
|
55
55
|
end
|
56
56
|
|
57
|
+
let(:original_array) do
|
58
|
+
[
|
59
|
+
"one", [
|
60
|
+
"one_one", [
|
61
|
+
"one_one1", "one_one2"
|
62
|
+
],
|
63
|
+
"one_two"
|
64
|
+
]
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
57
68
|
let(:duplicate_hash) { utils.deep_dup(original_hash) }
|
69
|
+
let(:duplicate_array) { utils.deep_dup(original_array) }
|
58
70
|
|
59
71
|
it "creates deep duplicate of hash" do
|
60
72
|
expect(original_hash).to eq(duplicate_hash)
|
@@ -75,5 +87,25 @@ RSpec.describe Lutaml::Model::Utils do
|
|
75
87
|
expect(original_hash[:one][:one_two]).to eq(duplicate_hash[:one][:one_two])
|
76
88
|
expect(original_hash[:one][:one_two].object_id).not_to eq(duplicate_hash[:one][:one_two].object_id)
|
77
89
|
end
|
90
|
+
|
91
|
+
it "creates deep duplicate of array" do
|
92
|
+
expect(original_array).to eq(duplicate_array)
|
93
|
+
expect(original_array.object_id).not_to eq(duplicate_array.object_id)
|
94
|
+
|
95
|
+
expect(original_array[0]).to eq(duplicate_array[0])
|
96
|
+
expect(original_array[0].object_id).not_to eq(duplicate_array[0].object_id)
|
97
|
+
|
98
|
+
expect(original_array[1][0]).to eq(duplicate_array[1][0])
|
99
|
+
expect(original_array[1][0].object_id).not_to eq(duplicate_array[1][0].object_id)
|
100
|
+
|
101
|
+
expect(original_array[1][1][0]).to eq(duplicate_array[1][1][0])
|
102
|
+
expect(original_array[1][1][0].object_id).not_to eq(duplicate_array[1][1][0].object_id)
|
103
|
+
|
104
|
+
expect(original_array[1][1][1]).to eq(duplicate_array[1][1][1])
|
105
|
+
expect(original_array[1][1][1].object_id).not_to eq(duplicate_array[1][1][1].object_id)
|
106
|
+
|
107
|
+
expect(original_array[1][2]).to eq(duplicate_array[1][2])
|
108
|
+
expect(original_array[1][2].object_id).not_to eq(duplicate_array[1][2].object_id)
|
109
|
+
end
|
78
110
|
end
|
79
111
|
end
|
@@ -14,7 +14,7 @@ RSpec.describe Lutaml::Model::XmlAdapter::OgaAdapter do
|
|
14
14
|
let(:document) { described_class.parse(xml_string) }
|
15
15
|
|
16
16
|
context "parsing XML with namespaces" do
|
17
|
-
let(:child) { document.root.children
|
17
|
+
let(:child) { document.root.children.first }
|
18
18
|
|
19
19
|
it "parses the root element with default namespace" do
|
20
20
|
expect(document.root.name).to eq("root")
|
@@ -39,18 +39,22 @@ RSpec.describe Lutaml::Model::XmlAdapter::OgaAdapter do
|
|
39
39
|
|
40
40
|
context "generating XML with namespaces" do
|
41
41
|
it "generates XML with namespaces correctly" do
|
42
|
-
xml_output = document.to_xml
|
43
|
-
parsed_output = Oga.
|
42
|
+
xml_output = document.root.to_xml
|
43
|
+
parsed_output = Moxml::Adapter::Oga.parse(xml_output)
|
44
44
|
|
45
45
|
root = parsed_output.children.first
|
46
46
|
expect(root.name).to eq("root")
|
47
47
|
expect(root.namespace.uri).to eq("http://example.com/default")
|
48
48
|
|
49
|
-
child = root.children
|
50
|
-
expect(child
|
49
|
+
child = root.children.first
|
50
|
+
expect(described_class.prefixed_name_of(child)).to eq("prefix:child")
|
51
51
|
expect(child.namespace.uri).to eq("http://example.com/prefixed")
|
52
|
-
|
53
|
-
expect(
|
52
|
+
unprefixed_attr = child.attributes.find { |attr| attr.name == "attr" }
|
53
|
+
expect(unprefixed_attr.value).to eq("value")
|
54
|
+
prefixed_attr = child.attributes.find do |attr|
|
55
|
+
described_class.prefixed_name_of(attr) == "prefix:attr"
|
56
|
+
end
|
57
|
+
expect(prefixed_attr.value).to eq("prefixed_value")
|
54
58
|
end
|
55
59
|
end
|
56
60
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
require "lutaml/model/xml_adapter/ox_adapter"
|
4
|
+
require "lutaml/model/xml_adapter/oga_adapter"
|
5
|
+
|
6
|
+
require_relative "../../../lib/lutaml/model/xml_mapping_rule"
|
7
|
+
|
8
|
+
def content_to_xml(model, parent, doc)
|
9
|
+
content = model.all_content.sub(/^<div>/, "").sub(/<\/div>$/, "")
|
10
|
+
doc.add_xml_fragment(parent, content)
|
11
|
+
end
|
12
|
+
|
13
|
+
def content_from_xml(model, value)
|
14
|
+
model.all_content = "<div>#{value}</div>"
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec.describe Lutaml::Model::XmlMappingRule do
|
18
|
+
context "with Xml Mapping Rule" do
|
19
|
+
let(:orig_mapping_rule) do
|
20
|
+
described_class.new(
|
21
|
+
"name",
|
22
|
+
to: :name,
|
23
|
+
render_nil: true,
|
24
|
+
render_default: true,
|
25
|
+
with: { to: :content_to_xml, from: :content_from_xml },
|
26
|
+
delegate: true,
|
27
|
+
namespace: "http://child-namespace",
|
28
|
+
prefix: "cn",
|
29
|
+
mixed_content: true,
|
30
|
+
cdata: true,
|
31
|
+
namespace_set: true,
|
32
|
+
prefix_set: true,
|
33
|
+
attribute: true,
|
34
|
+
default_namespace: "http://parent-namespace",
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:dup_mapping_rule) do
|
39
|
+
orig_mapping_rule.deep_dup
|
40
|
+
end
|
41
|
+
|
42
|
+
it "duplicates all instance variables" do
|
43
|
+
orig_mapping_rule.instance_variables.each do |variable|
|
44
|
+
orig_var = orig_mapping_rule.instance_variable_get(variable)
|
45
|
+
dup_var = dup_mapping_rule.instance_variable_get(variable)
|
46
|
+
|
47
|
+
expect(orig_var).to eq(dup_var)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|