lutaml-model 0.5.4 → 0.6.0

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.
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