lutaml-model 0.3.24 → 0.3.25
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 +35 -16
- data/README.adoc +274 -28
- data/lib/lutaml/model/attribute.rb +18 -8
- data/lib/lutaml/model/error/type_error.rb +9 -0
- data/lib/lutaml/model/error/unknown_type_error.rb +9 -0
- data/lib/lutaml/model/error/validation_error.rb +0 -1
- data/lib/lutaml/model/error.rb +2 -0
- data/lib/lutaml/model/serialize.rb +6 -1
- data/lib/lutaml/model/type/boolean.rb +38 -0
- data/lib/lutaml/model/type/date.rb +35 -0
- data/lib/lutaml/model/type/date_time.rb +32 -4
- data/lib/lutaml/model/type/decimal.rb +42 -0
- data/lib/lutaml/model/type/float.rb +37 -0
- data/lib/lutaml/model/type/hash.rb +62 -0
- data/lib/lutaml/model/type/integer.rb +41 -0
- data/lib/lutaml/model/type/string.rb +49 -0
- data/lib/lutaml/model/type/time.rb +49 -0
- data/lib/lutaml/model/type/time_without_date.rb +37 -5
- data/lib/lutaml/model/type/value.rb +52 -0
- data/lib/lutaml/model/type.rb +50 -114
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +5 -2
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +2 -1
- data/lib/lutaml/model/xml_adapter/xml_document.rb +0 -2
- data/lutaml-model.gemspec +1 -1
- data/spec/address_spec.rb +170 -0
- data/spec/fixtures/address.rb +33 -0
- data/spec/fixtures/person.rb +73 -0
- data/spec/fixtures/sample_model.rb +40 -0
- data/spec/fixtures/vase.rb +38 -0
- data/spec/fixtures/xml/special_char.xml +13 -0
- data/spec/lutaml/model/attribute_spec.rb +112 -0
- data/spec/lutaml/model/collection_spec.rb +299 -0
- data/spec/lutaml/model/comparable_model_spec.rb +106 -0
- data/spec/lutaml/model/custom_model_spec.rb +410 -0
- data/spec/lutaml/model/custom_serialization_spec.rb +170 -0
- data/spec/lutaml/model/defaults_spec.rb +221 -0
- data/spec/lutaml/model/delegation_spec.rb +340 -0
- data/spec/lutaml/model/inheritance_spec.rb +92 -0
- data/spec/lutaml/model/json_adapter_spec.rb +37 -0
- data/spec/lutaml/model/key_value_mapping_spec.rb +86 -0
- data/spec/lutaml/model/map_content_spec.rb +118 -0
- data/spec/lutaml/model/mixed_content_spec.rb +625 -0
- data/spec/lutaml/model/namespace_spec.rb +57 -0
- data/spec/lutaml/model/ordered_content_spec.rb +83 -0
- data/spec/lutaml/model/render_nil_spec.rb +138 -0
- data/spec/lutaml/model/schema/json_schema_spec.rb +79 -0
- data/spec/lutaml/model/schema/relaxng_schema_spec.rb +60 -0
- data/spec/lutaml/model/schema/xsd_schema_spec.rb +55 -0
- data/spec/lutaml/model/schema/yaml_schema_spec.rb +47 -0
- data/spec/lutaml/model/serializable_spec.rb +297 -0
- data/spec/lutaml/model/serializable_validation_spec.rb +85 -0
- data/spec/lutaml/model/simple_model_spec.rb +314 -0
- data/spec/lutaml/model/toml_adapter_spec.rb +39 -0
- data/spec/lutaml/model/type/boolean_spec.rb +54 -0
- data/spec/lutaml/model/type/date_spec.rb +118 -0
- data/spec/lutaml/model/type/date_time_spec.rb +127 -0
- data/spec/lutaml/model/type/decimal_spec.rb +125 -0
- data/spec/lutaml/model/type/float_spec.rb +191 -0
- data/spec/lutaml/model/type/hash_spec.rb +63 -0
- data/spec/lutaml/model/type/integer_spec.rb +145 -0
- data/spec/lutaml/model/type/string_spec.rb +150 -0
- data/spec/lutaml/model/type/time_spec.rb +142 -0
- data/spec/lutaml/model/type/time_without_date_spec.rb +125 -0
- data/spec/lutaml/model/type_spec.rb +276 -0
- data/spec/lutaml/model/utils_spec.rb +79 -0
- data/spec/lutaml/model/validation_spec.rb +83 -0
- data/spec/lutaml/model/with_child_mapping_spec.rb +174 -0
- data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +56 -0
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +56 -0
- data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +61 -0
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +251 -0
- data/spec/lutaml/model/xml_adapter_spec.rb +178 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +863 -0
- data/spec/lutaml/model/yaml_adapter_spec.rb +30 -0
- data/spec/lutaml/model_spec.rb +1 -0
- data/spec/person_spec.rb +161 -0
- data/spec/spec_helper.rb +33 -0
- metadata +66 -2
@@ -0,0 +1,221 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "lutaml/model"
|
5
|
+
|
6
|
+
module DefaultsSpec
|
7
|
+
class GlazeTag < Lutaml::Model::Serializable
|
8
|
+
attribute :text, :string, default: -> { "" }
|
9
|
+
|
10
|
+
xml do
|
11
|
+
root "Tag"
|
12
|
+
map_content to: :text
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Glaze < Lutaml::Model::Serializable
|
17
|
+
attribute :name, :string, default: -> { "Standard Glaze" }
|
18
|
+
attribute :color, :string, default: -> { "Clear" }
|
19
|
+
attribute :opacity, :string, default: -> { "Opaque" }
|
20
|
+
attribute :temperature, :integer, default: -> { 1050 }
|
21
|
+
attribute :firing_time, :integer, default: -> { 60 }
|
22
|
+
attribute :balance, "Decimal", default: -> { BigDecimal("0.0") }
|
23
|
+
attribute :tags, GlazeTag, collection: true
|
24
|
+
attribute :properties, :hash, default: -> { { food_safe: true } }
|
25
|
+
attribute :status, :string, default: -> { "active" }
|
26
|
+
attribute :batch_number, :integer, default: -> { 0 }
|
27
|
+
attribute :manufacturer, :string, default: -> { "example@glazes.com" }
|
28
|
+
attribute :type, :string, values: %w[earthenware stoneware porcelain], default: -> {
|
29
|
+
"stoneware"
|
30
|
+
}
|
31
|
+
|
32
|
+
xml do
|
33
|
+
root "Glaze"
|
34
|
+
map_element "Name", to: :name
|
35
|
+
map_element "Color", to: :color
|
36
|
+
map_element "Opacity", to: :opacity, render_default: true
|
37
|
+
map_element "Temperature", to: :temperature
|
38
|
+
map_element "FiringTime", to: :firing_time, render_default: true
|
39
|
+
map_element "Balance", to: :balance
|
40
|
+
map_element "Tags", to: :tags
|
41
|
+
map_element "Properties", to: :properties
|
42
|
+
map_element "Status", to: :status
|
43
|
+
map_element "BatchNumber", to: :batch_number
|
44
|
+
map_element "Manufacturer", to: :manufacturer
|
45
|
+
map_element "Type", to: :type
|
46
|
+
end
|
47
|
+
|
48
|
+
json do
|
49
|
+
map "name", to: :name
|
50
|
+
map "color", to: :color
|
51
|
+
map "opacity", to: :opacity, render_default: true
|
52
|
+
map "temperature", to: :temperature
|
53
|
+
map "firingTime", to: :firing_time, render_default: true
|
54
|
+
map "balance", to: :balance
|
55
|
+
map "tags", to: :tags
|
56
|
+
map "properties", to: :properties
|
57
|
+
map "status", to: :status
|
58
|
+
map "batchNumber", to: :batch_number
|
59
|
+
map "manufacturer", to: :manufacturer
|
60
|
+
map "type", to: :type
|
61
|
+
end
|
62
|
+
|
63
|
+
yaml do
|
64
|
+
map "name", to: :name
|
65
|
+
map "color", to: :color
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Class for testing default values without render_default option
|
70
|
+
class BasicGlaze < Lutaml::Model::Serializable
|
71
|
+
attribute :name, :string, default: -> { "Basic Glaze" }
|
72
|
+
attribute :temperature, :integer, default: -> { 1050 }
|
73
|
+
attribute :opacity, :string, default: -> { "Opaque" }
|
74
|
+
|
75
|
+
xml do
|
76
|
+
root "BasicGlaze"
|
77
|
+
map_element "name", to: :name
|
78
|
+
map_element "temperature", to: :temperature
|
79
|
+
map_element "opacity", to: :opacity
|
80
|
+
end
|
81
|
+
|
82
|
+
json do
|
83
|
+
map "name", to: :name
|
84
|
+
map "temperature", to: :temperature
|
85
|
+
map "opacity", to: :opacity
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Class for testing explicit render_default: false
|
90
|
+
class NoDefaultGlaze < Lutaml::Model::Serializable
|
91
|
+
attribute :name, :string, default: -> { "No Default Glaze" }
|
92
|
+
attribute :temperature, :integer, default: -> { 1050 }
|
93
|
+
attribute :opacity, :string, default: -> { "Opaque" }
|
94
|
+
|
95
|
+
xml do
|
96
|
+
root "NoDefaultGlaze"
|
97
|
+
map_element "name", to: :name, render_default: false
|
98
|
+
map_element "temperature", to: :temperature, render_default: false
|
99
|
+
map_element "opacity", to: :opacity, render_default: false
|
100
|
+
end
|
101
|
+
|
102
|
+
json do
|
103
|
+
map "name", to: :name, render_default: false
|
104
|
+
map "temperature", to: :temperature, render_default: false
|
105
|
+
map "opacity", to: :opacity, render_default: false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
RSpec.describe DefaultsSpec::Glaze do
|
111
|
+
describe "Default value initialization" do
|
112
|
+
subject(:default_model) { described_class.new }
|
113
|
+
|
114
|
+
it "initializes with correct default values" do
|
115
|
+
expect(default_model.name).to eq("Standard Glaze")
|
116
|
+
expect(default_model.color).to eq("Clear")
|
117
|
+
expect(default_model.opacity).to eq("Opaque")
|
118
|
+
expect(default_model.temperature).to eq(1050)
|
119
|
+
expect(default_model.firing_time).to eq(60)
|
120
|
+
expect(default_model.balance).to eq(BigDecimal("0.0"))
|
121
|
+
expect(default_model.tags).to eq([])
|
122
|
+
expect(default_model.properties).to eq({ food_safe: true })
|
123
|
+
expect(default_model.status).to eq("active")
|
124
|
+
expect(default_model.batch_number).to eq(0)
|
125
|
+
expect(default_model.manufacturer).to eq("example@glazes.com")
|
126
|
+
expect(default_model.type).to eq("stoneware")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "Default value rendering behavior" do
|
131
|
+
context "with XML serialization" do
|
132
|
+
context "when value is default" do
|
133
|
+
it "does not serialize when render_default is false" do
|
134
|
+
glaze = DefaultsSpec::NoDefaultGlaze.new
|
135
|
+
xml = glaze.to_xml
|
136
|
+
|
137
|
+
expect(xml).not_to include("<name>")
|
138
|
+
expect(xml).not_to include("<temperature>")
|
139
|
+
expect(xml).not_to include("<opacity>")
|
140
|
+
end
|
141
|
+
|
142
|
+
it "does not serialize when render_default is not set" do
|
143
|
+
glaze = DefaultsSpec::BasicGlaze.new
|
144
|
+
xml = glaze.to_xml
|
145
|
+
|
146
|
+
expect(xml).not_to include("<name>")
|
147
|
+
expect(xml).not_to include("<temperature>")
|
148
|
+
expect(xml).not_to include("<opacity>")
|
149
|
+
end
|
150
|
+
|
151
|
+
it "serializes when render_default is true" do
|
152
|
+
glaze = described_class.new
|
153
|
+
xml = glaze.to_xml
|
154
|
+
|
155
|
+
expect(xml).to include("<Opacity>Opaque</Opacity>")
|
156
|
+
expect(xml).to include("<FiringTime>60</FiringTime>")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "when value is not default" do
|
161
|
+
it "serializes regardless of render_default setting" do
|
162
|
+
glaze = DefaultsSpec::NoDefaultGlaze.new(
|
163
|
+
name: "Custom Glaze",
|
164
|
+
temperature: 1200,
|
165
|
+
opacity: "Translucent",
|
166
|
+
)
|
167
|
+
xml = glaze.to_xml
|
168
|
+
|
169
|
+
expect(xml).to include("<name>Custom Glaze</name>")
|
170
|
+
expect(xml).to include("<temperature>1200</temperature>")
|
171
|
+
expect(xml).to include("<opacity>Translucent</opacity>")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "with JSON serialization" do
|
177
|
+
context "when value is default" do
|
178
|
+
it "does not serialize when render_default is false" do
|
179
|
+
glaze = DefaultsSpec::NoDefaultGlaze.new
|
180
|
+
json = JSON.parse(glaze.to_json)
|
181
|
+
|
182
|
+
expect(json).not_to have_key("name")
|
183
|
+
expect(json).not_to have_key("temperature")
|
184
|
+
expect(json).not_to have_key("opacity")
|
185
|
+
end
|
186
|
+
|
187
|
+
it "does not serialize when render_default is not set" do
|
188
|
+
glaze = DefaultsSpec::BasicGlaze.new
|
189
|
+
json = JSON.parse(glaze.to_json)
|
190
|
+
|
191
|
+
expect(json).not_to have_key("name")
|
192
|
+
expect(json).not_to have_key("temperature")
|
193
|
+
expect(json).not_to have_key("opacity")
|
194
|
+
end
|
195
|
+
|
196
|
+
it "serializes when render_default is true" do
|
197
|
+
glaze = described_class.new
|
198
|
+
json = JSON.parse(glaze.to_json)
|
199
|
+
|
200
|
+
expect(json["opacity"]).to eq("Opaque")
|
201
|
+
expect(json["firingTime"]).to eq(60)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context "when value is not default" do
|
206
|
+
it "serializes regardless of render_default setting" do
|
207
|
+
glaze = DefaultsSpec::NoDefaultGlaze.new(
|
208
|
+
name: "Custom Glaze",
|
209
|
+
temperature: 1200,
|
210
|
+
opacity: "Translucent",
|
211
|
+
)
|
212
|
+
json = JSON.parse(glaze.to_json)
|
213
|
+
|
214
|
+
expect(json["name"]).to eq("Custom Glaze")
|
215
|
+
expect(json["temperature"]).to eq(1200)
|
216
|
+
expect(json["opacity"]).to eq("Translucent")
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,340 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "lutaml/model"
|
3
|
+
|
4
|
+
module Delegation
|
5
|
+
class Glaze < Lutaml::Model::Serializable
|
6
|
+
attribute :color, Lutaml::Model::Type::String
|
7
|
+
attribute :finish, Lutaml::Model::Type::String
|
8
|
+
end
|
9
|
+
|
10
|
+
class Ceramic < Lutaml::Model::Serializable
|
11
|
+
attribute :type, Lutaml::Model::Type::String
|
12
|
+
attribute :glaze, Glaze
|
13
|
+
|
14
|
+
json do
|
15
|
+
map "type", to: :type
|
16
|
+
map "color", to: :color, delegate: :glaze
|
17
|
+
map "finish", to: :finish, delegate: :glaze
|
18
|
+
end
|
19
|
+
|
20
|
+
yaml do
|
21
|
+
map "type", to: :type
|
22
|
+
map "color", to: :color, delegate: :glaze
|
23
|
+
map "finish", to: :finish, delegate: :glaze
|
24
|
+
end
|
25
|
+
|
26
|
+
toml do
|
27
|
+
map "type", to: :type
|
28
|
+
map "color", to: :color, delegate: :glaze
|
29
|
+
map "finish", to: :finish, delegate: :glaze
|
30
|
+
end
|
31
|
+
|
32
|
+
xml do
|
33
|
+
root "delegation"
|
34
|
+
map_element "type", to: :type
|
35
|
+
map_element "color", to: :color, delegate: :glaze
|
36
|
+
map_element "finish", to: :finish, delegate: :glaze
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class NamespacedTypeCeramic < Lutaml::Model::Serializable
|
41
|
+
attribute :type, Lutaml::Model::Type::String
|
42
|
+
attribute :glaze, Glaze
|
43
|
+
|
44
|
+
yaml do
|
45
|
+
map "type", to: :type
|
46
|
+
map "color", to: :color, delegate: :glaze
|
47
|
+
map "finish", to: :finish, delegate: :glaze
|
48
|
+
end
|
49
|
+
|
50
|
+
xml do
|
51
|
+
root "delegation"
|
52
|
+
map_element "type",
|
53
|
+
to: :type,
|
54
|
+
namespace: "https://example.com/type/1.2",
|
55
|
+
prefix: "type"
|
56
|
+
map_element "color", to: :color, delegate: :glaze
|
57
|
+
map_element "finish", to: :finish, delegate: :glaze
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
RSpec.describe Delegation do
|
63
|
+
let(:yaml_data) do
|
64
|
+
<<~YAML
|
65
|
+
type: Vase
|
66
|
+
color: Blue
|
67
|
+
finish: Glossy
|
68
|
+
YAML
|
69
|
+
end
|
70
|
+
|
71
|
+
let(:delegation1) { Delegation::NamespacedTypeCeramic.from_yaml(yaml_data) }
|
72
|
+
let(:delegation) { Delegation::Ceramic.from_yaml(yaml_data) }
|
73
|
+
|
74
|
+
it "deserializes from YAML with delegation" do
|
75
|
+
expect(delegation.type).to eq("Vase")
|
76
|
+
expect(delegation.glaze.color).to eq("Blue")
|
77
|
+
expect(delegation.glaze.finish).to eq("Glossy")
|
78
|
+
end
|
79
|
+
|
80
|
+
it "serializes to YAML with delegation" do
|
81
|
+
expected_yaml = <<~YAML
|
82
|
+
---
|
83
|
+
type: Vase
|
84
|
+
color: Blue
|
85
|
+
finish: Glossy
|
86
|
+
YAML
|
87
|
+
expect(delegation.to_yaml.strip).to eq(expected_yaml.strip)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "serializes to JSON with delegation and filtering" do
|
91
|
+
expected_json = {
|
92
|
+
type: "Vase",
|
93
|
+
color: "Blue",
|
94
|
+
}.to_json
|
95
|
+
|
96
|
+
generated_json = delegation.to_json(only: %i[type color])
|
97
|
+
|
98
|
+
expect(JSON.parse(generated_json)).to eq(JSON.parse(expected_json))
|
99
|
+
end
|
100
|
+
|
101
|
+
it "serializes to JSON with pretty formatting" do
|
102
|
+
expected_pretty_json = {
|
103
|
+
type: "Vase",
|
104
|
+
color: "Blue",
|
105
|
+
}.to_json
|
106
|
+
|
107
|
+
generated_json = delegation.to_json(only: %i[type color], pretty: true)
|
108
|
+
|
109
|
+
expect(generated_json.strip).to eq(expected_pretty_json.strip)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "serializes to XML with pretty formatting" do
|
113
|
+
expected_pretty_xml = <<~XML
|
114
|
+
<delegation>
|
115
|
+
<type>Vase</type>
|
116
|
+
<color>Blue</color>
|
117
|
+
<finish>Glossy</finish>
|
118
|
+
</delegation>
|
119
|
+
XML
|
120
|
+
|
121
|
+
generated_xml = delegation.to_xml(pretty: true).strip
|
122
|
+
|
123
|
+
expect(generated_xml).to be_equivalent_to(expected_pretty_xml.strip)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "does not provide XML declaration if no declaration option provided" do
|
127
|
+
xml_data = delegation.to_xml(pretty: true)
|
128
|
+
expect(xml_data).not_to include("<?xml")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "provides XML declaration with default version" \
|
132
|
+
"if declaration: true option provided" do
|
133
|
+
xml_data = delegation.to_xml(pretty: true, declaration: true)
|
134
|
+
expect(xml_data).to include('<?xml version="1.0"?>')
|
135
|
+
end
|
136
|
+
|
137
|
+
it "provides XML declaration with specified version" \
|
138
|
+
"if declaration: '1.1' option provided" do
|
139
|
+
xml_data = delegation.to_xml(pretty: true, declaration: "1.1")
|
140
|
+
expect(xml_data).to include('<?xml version="1.1"?>')
|
141
|
+
end
|
142
|
+
|
143
|
+
it "provides XML declaration without encoding" \
|
144
|
+
"if encoding option not provided" do
|
145
|
+
xml_data = delegation.to_xml(pretty: true, declaration: true)
|
146
|
+
expect(xml_data).to include('<?xml version="1.0"?>')
|
147
|
+
expect(xml_data).not_to include("encoding=")
|
148
|
+
end
|
149
|
+
|
150
|
+
it "provides XML declaration with UTF-8 encoding" \
|
151
|
+
"if encoding: true option provided" do
|
152
|
+
xml_data = delegation.to_xml(
|
153
|
+
pretty: true,
|
154
|
+
declaration: true,
|
155
|
+
encoding: true,
|
156
|
+
)
|
157
|
+
expect(xml_data).to include('<?xml version="1.0" encoding="UTF-8"?>')
|
158
|
+
end
|
159
|
+
|
160
|
+
it "provides XML declaration with specified encoding" \
|
161
|
+
"if encoding: 'ASCII' option provided" do
|
162
|
+
xml_data = delegation.to_xml(
|
163
|
+
pretty: true,
|
164
|
+
declaration: true,
|
165
|
+
encoding: "ASCII",
|
166
|
+
)
|
167
|
+
expect(xml_data).to include('<?xml version="1.0" encoding="ASCII"?>')
|
168
|
+
end
|
169
|
+
|
170
|
+
it "sets the namespace of a particular attribute inside <delegation>" do
|
171
|
+
Delegation::Ceramic.class_eval do
|
172
|
+
attribute :date, Lutaml::Model::Type::Date
|
173
|
+
|
174
|
+
xml do
|
175
|
+
root "delegation"
|
176
|
+
map_attribute "date",
|
177
|
+
to: :date,
|
178
|
+
namespace: "https://example.com/delegation/1.2",
|
179
|
+
prefix: "del"
|
180
|
+
map_element "type", to: :type
|
181
|
+
map_element "color", to: :color, delegate: :glaze
|
182
|
+
map_element "finish", to: :finish, delegate: :glaze
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
delegation_class = Delegation::Ceramic
|
187
|
+
delegation = delegation_class.new(
|
188
|
+
type: "Vase",
|
189
|
+
glaze: Delegation::Glaze.new(
|
190
|
+
color: "Blue",
|
191
|
+
finish: "Glossy",
|
192
|
+
),
|
193
|
+
date: "2024-06-08",
|
194
|
+
)
|
195
|
+
|
196
|
+
xml_data = delegation.to_xml(
|
197
|
+
pretty: true,
|
198
|
+
declaration: true,
|
199
|
+
encoding: "UTF-8",
|
200
|
+
)
|
201
|
+
|
202
|
+
delegation_attributes = [
|
203
|
+
'xmlns:del="https://example.com/delegation/1.2"',
|
204
|
+
'del:date="2024-06-08"',
|
205
|
+
]
|
206
|
+
|
207
|
+
expect(xml_data).to include("<delegation #{delegation_attributes.join(' ')}>")
|
208
|
+
end
|
209
|
+
|
210
|
+
it "sets the default namespace of <delegation>" do
|
211
|
+
Delegation::Ceramic.class_eval do
|
212
|
+
xml do
|
213
|
+
root "delegation"
|
214
|
+
namespace "https://example.com/delegation/1.2"
|
215
|
+
map_element "type", to: :type
|
216
|
+
map_element "color", to: :color, delegate: :glaze
|
217
|
+
map_element "finish", to: :finish, delegate: :glaze
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
delegation_class = Delegation::Ceramic
|
222
|
+
delegation = delegation_class.from_yaml(yaml_data)
|
223
|
+
xml_data = delegation.to_xml(
|
224
|
+
pretty: true,
|
225
|
+
declaration: true,
|
226
|
+
encoding: "UTF-8",
|
227
|
+
)
|
228
|
+
expect(xml_data).to(
|
229
|
+
include('<delegation xmlns="https://example.com/delegation/1.2" xmlns:del="https://example.com/delegation/1.2">'),
|
230
|
+
)
|
231
|
+
end
|
232
|
+
|
233
|
+
it "sets the namespace of <delegation> with a prefix" do
|
234
|
+
Delegation::Ceramic.class_eval do
|
235
|
+
xml do
|
236
|
+
root "delegation"
|
237
|
+
namespace "https://example.com/delegation/1.2", "del"
|
238
|
+
map_element "type", to: :type
|
239
|
+
map_element "color", to: :color, delegate: :glaze
|
240
|
+
map_element "finish", to: :finish, delegate: :glaze
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
delegation_class = Delegation::Ceramic
|
245
|
+
delegation = delegation_class.from_yaml(yaml_data)
|
246
|
+
xml_data = delegation.to_xml(
|
247
|
+
pretty: true,
|
248
|
+
declaration: true,
|
249
|
+
encoding: "UTF-8",
|
250
|
+
)
|
251
|
+
|
252
|
+
expect(xml_data).to(
|
253
|
+
include(
|
254
|
+
'<del:delegation xmlns:del="https://example.com/delegation/1.2">',
|
255
|
+
),
|
256
|
+
)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "sets the namespace of <delegation> and also" \
|
260
|
+
"a particular element inside using :inherit" do
|
261
|
+
Delegation::Ceramic.class_eval do
|
262
|
+
xml do
|
263
|
+
root "delegation"
|
264
|
+
namespace "https://example.com/delegation/1.2", "del"
|
265
|
+
map_element "type", to: :type # , namespace: :inherit
|
266
|
+
map_element "color", to: :color, delegate: :glaze
|
267
|
+
map_element "finish", to: :finish, delegate: :glaze
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
delegation_class = Delegation::Ceramic
|
272
|
+
delegation = delegation_class.from_yaml(yaml_data)
|
273
|
+
xml_data = delegation.to_xml(
|
274
|
+
pretty: true,
|
275
|
+
declaration: true,
|
276
|
+
encoding: "UTF-8",
|
277
|
+
)
|
278
|
+
|
279
|
+
delegation_attribute = 'xmlns:del="https://example.com/delegation/1.2">'
|
280
|
+
|
281
|
+
expect(xml_data).to include("<del:delegation #{delegation_attribute}")
|
282
|
+
expect(xml_data).to include("<del:type>Vase</del:type>")
|
283
|
+
end
|
284
|
+
|
285
|
+
it "sets the namespace of <delegation> and also" \
|
286
|
+
"a particular attribute inside using :inherit" do
|
287
|
+
Delegation::Ceramic.class_eval do
|
288
|
+
attribute :date, Lutaml::Model::Type::Date
|
289
|
+
|
290
|
+
xml do
|
291
|
+
root "delegation"
|
292
|
+
namespace "https://example.com/delegation/1.1", "del1"
|
293
|
+
map_attribute "date",
|
294
|
+
to: :date,
|
295
|
+
namespace: "https://example.com/delegation/1.2",
|
296
|
+
prefix: "del2"
|
297
|
+
|
298
|
+
map_element "type", to: :type, namespace: :inherit
|
299
|
+
map_element "color", to: :color, delegate: :glaze
|
300
|
+
map_element "finish", to: :finish, delegate: :glaze
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
delegation_class = Delegation::Ceramic
|
305
|
+
delegation = delegation_class.new(
|
306
|
+
type: "Vase",
|
307
|
+
glaze: Delegation::Glaze.new(
|
308
|
+
color: "Blue",
|
309
|
+
finish: "Glossy",
|
310
|
+
),
|
311
|
+
date: "2024-06-08",
|
312
|
+
)
|
313
|
+
|
314
|
+
xml_data = delegation.to_xml(
|
315
|
+
pretty: true,
|
316
|
+
declaration: true,
|
317
|
+
encoding: "UTF-8",
|
318
|
+
)
|
319
|
+
|
320
|
+
delegation_attributes = [
|
321
|
+
'xmlns:del1="https://example.com/delegation/1.1"',
|
322
|
+
'xmlns:del2="https://example.com/delegation/1.2"',
|
323
|
+
'del2:date="2024-06-08"',
|
324
|
+
]
|
325
|
+
|
326
|
+
expect(xml_data).to include("<del1:delegation #{delegation_attributes.join(' ')}>")
|
327
|
+
expect(xml_data).to include("<del1:type>Vase</del1:type>")
|
328
|
+
end
|
329
|
+
|
330
|
+
it "sets the namespace of a particular element inside Ceramic" do
|
331
|
+
delegation1 = Delegation::NamespacedTypeCeramic.from_yaml(yaml_data)
|
332
|
+
xml_data = delegation1.to_xml(
|
333
|
+
pretty: true,
|
334
|
+
declaration: true,
|
335
|
+
encoding: "UTF-8",
|
336
|
+
)
|
337
|
+
expect(xml_data).to include('<delegation xmlns:type="https://example.com/type/1.2">')
|
338
|
+
expect(xml_data).to include("<type:type>Vase</type:type>")
|
339
|
+
end
|
340
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "lutaml/model"
|
3
|
+
|
4
|
+
module InheritanceSpec
|
5
|
+
class Parent < Lutaml::Model::Serializable
|
6
|
+
attribute :text, Lutaml::Model::Type::String
|
7
|
+
attribute :id, Lutaml::Model::Type::String
|
8
|
+
attribute :name, Lutaml::Model::Type::String
|
9
|
+
|
10
|
+
xml do
|
11
|
+
map_content to: :text
|
12
|
+
|
13
|
+
map_attribute "id", to: :id
|
14
|
+
map_element "name", to: :name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Child1 < Parent
|
19
|
+
attribute :age, Lutaml::Model::Type::Integer
|
20
|
+
|
21
|
+
xml do
|
22
|
+
root "child"
|
23
|
+
|
24
|
+
map_element "age", to: :age
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Child2 < Parent
|
29
|
+
attribute :age, Lutaml::Model::Type::Integer
|
30
|
+
|
31
|
+
xml do
|
32
|
+
root "child_two"
|
33
|
+
|
34
|
+
map_element "gender", to: :age
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
RSpec.describe "Inheritance" do
|
40
|
+
subject(:child_object) do
|
41
|
+
InheritanceSpec::Child1.new(
|
42
|
+
{
|
43
|
+
text: "Some text",
|
44
|
+
name: "John Doe",
|
45
|
+
id: "foobar",
|
46
|
+
age: 30,
|
47
|
+
},
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
let(:expected_xml) do
|
52
|
+
'<child id="foobar"><name>John Doe</name><age>30</age>Some text</child>'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "uses parent attributes" do
|
56
|
+
expect(child_object.to_xml(pretty: true)).to eq(expected_xml)
|
57
|
+
end
|
58
|
+
|
59
|
+
context "with multiple child classes" do
|
60
|
+
describe "Child1" do
|
61
|
+
let(:child1) { InheritanceSpec::Child1 }
|
62
|
+
|
63
|
+
it "has correct mappings" do
|
64
|
+
expect(child1.mappings_for(:xml).mappings.count).to eq(4)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "has correct attributes" do
|
68
|
+
expect(child1.attributes.count).to eq(4)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "has correct model" do
|
72
|
+
expect(child1.model).to eq(child1)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "Child2" do
|
77
|
+
let(:child2) { InheritanceSpec::Child2 }
|
78
|
+
|
79
|
+
it "has correct mappings" do
|
80
|
+
expect(child2.mappings_for(:xml).mappings.count).to eq(4)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "has correct attributes" do
|
84
|
+
expect(child2.attributes.count).to eq(4)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "has correct model" do
|
88
|
+
expect(child2.model).to eq(child2)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "lutaml/model/json_adapter/standard_json_adapter"
|
3
|
+
require "lutaml/model/json_adapter/multi_json_adapter"
|
4
|
+
require_relative "../../fixtures/sample_model"
|
5
|
+
|
6
|
+
RSpec.shared_examples "a JSON adapter" do |adapter_class|
|
7
|
+
let(:attributes) { { name: "John Doe", age: 30 } }
|
8
|
+
let(:model) { SampleModel.new(attributes) }
|
9
|
+
|
10
|
+
let(:expected_json) do
|
11
|
+
if adapter_class == Lutaml::Model::JsonAdapter::StandardJsonAdapter
|
12
|
+
JSON.generate(attributes)
|
13
|
+
elsif adapter_class == Lutaml::Model::JsonAdapter::MultiJsonAdapter
|
14
|
+
MultiJson.dump(attributes)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "serializes to JSON" do
|
19
|
+
json = adapter_class.new(attributes).to_json
|
20
|
+
expect(json).to eq(expected_json)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "deserializes from JSON" do
|
24
|
+
doc = adapter_class.parse(expected_json)
|
25
|
+
new_model = SampleModel.new(doc.to_h)
|
26
|
+
expect(new_model.name).to eq("John Doe")
|
27
|
+
expect(new_model.age).to eq(30)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
RSpec.describe Lutaml::Model::JsonAdapter::StandardJsonAdapter do
|
32
|
+
it_behaves_like "a JSON adapter", described_class
|
33
|
+
end
|
34
|
+
|
35
|
+
RSpec.describe Lutaml::Model::JsonAdapter::MultiJsonAdapter do
|
36
|
+
it_behaves_like "a JSON adapter", described_class
|
37
|
+
end
|