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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +58 -21
  3. data/Gemfile +1 -0
  4. data/README.adoc +1112 -264
  5. data/lib/lutaml/model/attribute.rb +33 -10
  6. data/lib/lutaml/model/choice.rb +56 -0
  7. data/lib/lutaml/model/config.rb +1 -0
  8. data/lib/lutaml/model/error/choice_lower_bound_error.rb +9 -0
  9. data/lib/lutaml/model/error/choice_upper_bound_error.rb +9 -0
  10. data/lib/lutaml/model/error/import_model_with_root_error.rb +9 -0
  11. data/lib/lutaml/model/error/incorrect_sequence_error.rb +9 -0
  12. data/lib/lutaml/model/error/invalid_choice_range_error.rb +20 -0
  13. data/lib/lutaml/model/error/no_root_mapping_error.rb +9 -0
  14. data/lib/lutaml/model/error/no_root_namespace_error.rb +9 -0
  15. data/lib/lutaml/model/error/unknown_sequence_mapping_error.rb +9 -0
  16. data/lib/lutaml/model/error.rb +8 -0
  17. data/lib/lutaml/model/json_adapter/standard_json_adapter.rb +6 -1
  18. data/lib/lutaml/model/key_value_mapping.rb +3 -1
  19. data/lib/lutaml/model/key_value_mapping_rule.rb +4 -2
  20. data/lib/lutaml/model/liquefiable.rb +59 -0
  21. data/lib/lutaml/model/mapping_hash.rb +1 -1
  22. data/lib/lutaml/model/mapping_rule.rb +15 -2
  23. data/lib/lutaml/model/schema/xml_compiler.rb +68 -26
  24. data/lib/lutaml/model/schema_location.rb +7 -0
  25. data/lib/lutaml/model/sequence.rb +71 -0
  26. data/lib/lutaml/model/serialize.rb +125 -35
  27. data/lib/lutaml/model/type/decimal.rb +0 -4
  28. data/lib/lutaml/model/type/time.rb +3 -3
  29. data/lib/lutaml/model/utils.rb +19 -15
  30. data/lib/lutaml/model/validation.rb +12 -1
  31. data/lib/lutaml/model/version.rb +1 -1
  32. data/lib/lutaml/model/xml_adapter/builder/oga.rb +10 -7
  33. data/lib/lutaml/model/xml_adapter/builder/ox.rb +20 -13
  34. data/lib/lutaml/model/xml_adapter/element.rb +32 -0
  35. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +8 -8
  36. data/lib/lutaml/model/xml_adapter/oga/element.rb +14 -13
  37. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +86 -19
  38. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +19 -15
  39. data/lib/lutaml/model/xml_adapter/xml_document.rb +74 -13
  40. data/lib/lutaml/model/xml_adapter/xml_element.rb +57 -3
  41. data/lib/lutaml/model/xml_mapping.rb +49 -7
  42. data/lib/lutaml/model/xml_mapping_rule.rb +8 -3
  43. data/lib/lutaml/model.rb +1 -0
  44. data/lutaml-model.gemspec +5 -0
  45. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +75 -0
  46. data/spec/ceramic_spec.rb +39 -0
  47. data/spec/fixtures/ceramic.rb +23 -0
  48. data/spec/fixtures/xml/address_example_260.xsd +9 -0
  49. data/spec/fixtures/xml/user.xsd +10 -0
  50. data/spec/lutaml/model/cdata_spec.rb +4 -5
  51. data/spec/lutaml/model/choice_spec.rb +168 -0
  52. data/spec/lutaml/model/collection_spec.rb +1 -1
  53. data/spec/lutaml/model/custom_model_spec.rb +6 -7
  54. data/spec/lutaml/model/custom_serialization_spec.rb +74 -2
  55. data/spec/lutaml/model/defaults_spec.rb +3 -1
  56. data/spec/lutaml/model/delegation_spec.rb +7 -5
  57. data/spec/lutaml/model/enum_spec.rb +35 -0
  58. data/spec/lutaml/model/group_spec.rb +160 -0
  59. data/spec/lutaml/model/inheritance_spec.rb +25 -0
  60. data/spec/lutaml/model/liquefiable_spec.rb +121 -0
  61. data/spec/lutaml/model/mixed_content_spec.rb +80 -41
  62. data/spec/lutaml/model/multiple_mapping_spec.rb +22 -10
  63. data/spec/lutaml/model/schema/xml_compiler_spec.rb +218 -25
  64. data/spec/lutaml/model/sequence_spec.rb +216 -0
  65. data/spec/lutaml/model/transformation_spec.rb +230 -0
  66. data/spec/lutaml/model/type_spec.rb +138 -31
  67. data/spec/lutaml/model/utils_spec.rb +32 -0
  68. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -7
  69. data/spec/lutaml/model/xml_mapping_rule_spec.rb +51 -0
  70. data/spec/lutaml/model/xml_mapping_spec.rb +143 -112
  71. 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
- # Test classes for type registration scenarios
6
- class CustomType < Lutaml::Model::Type::Value
7
- def self.cast(value)
8
- value.to_s.upcase
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 InvalidType
13
- def self.cast(value)
14
- value
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
- class TypeTestModel < Lutaml::Model::Serializable
163
- attribute :string_symbol, :string
164
- attribute :string_class, Lutaml::Model::Type::String
165
- attribute :integer_value, :integer
166
- attribute :float_value, :float
167
- attribute :date_value, :date
168
- attribute :time_value, :time
169
- attribute :time_without_date_value, :time_without_date
170
- attribute :date_time_value, :date_time
171
- attribute :boolean_value, :boolean
172
- attribute :hash_value, :hash
173
-
174
- xml do
175
- root "test"
176
- map_element "string_symbol", to: :string_symbol
177
- map_element "string_class", to: :string_class
178
- map_element "integer", to: :integer_value
179
- map_element "float", to: :float_value
180
- map_element "date", to: :date_value
181
- map_element "time", to: :time_value
182
- map_element "time_without_date", to: :time_without_date_value
183
- map_element "date_time", to: :date_time_value
184
- map_element "boolean", to: :boolean_value
185
- map_element "hash", to: :hash_value
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[1] }
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.parse_xml(xml_output)
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[1]
50
- expect(child.expanded_name).to eq("prefix: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
- expect(child.get("attr")).to eq("value")
53
- expect(child.get("prefix:attr")).to eq("prefixed_value")
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