lutaml-model 0.3.30 → 0.4.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.
- checksums.yaml +4 -4
- data/README.adoc +6 -0
- data/lib/lutaml/model/attribute.rb +6 -2
- data/lib/lutaml/model/serialize.rb +117 -19
- data/lib/lutaml/model/type/time.rb +4 -4
- data/lib/lutaml/model/utils.rb +1 -1
- data/lib/lutaml/model/validation.rb +6 -2
- data/lib/lutaml/model/version.rb +1 -1
- data/spec/lutaml/model/custom_serialization_spec.rb +16 -0
- data/spec/lutaml/model/enum_spec.rb +131 -0
- data/spec/lutaml/model/render_nil_spec.rb +0 -2
- data/spec/lutaml/model/serializable_spec.rb +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa60d5495804e6bfc0203ba1a237f38b89b9626d036beeb2be8ca7aebb22426f
|
4
|
+
data.tar.gz: 63d8d18127bee85cfaa73973f57554bc56385805dfeea1dad343c552277089d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 322a29941e7685a6e118d083f1b6e11ce6b8abc1cacb5508dd501f874c5ea3f2dc6f0e6719ce4895deb31c877319a3afe0e20d0820a81b8fd60d9fe59c408472
|
7
|
+
data.tar.gz: ce087acefc221ed41180658b8c40d4cff5c0196c208647b9d1c5d0d39799849860abf80b981fe753b30a1176ca8633ed696440beeddef5a4493c1e829340f250
|
data/README.adoc
CHANGED
@@ -569,6 +569,12 @@ The values set inside the `values:` option can be of any type, but they must
|
|
569
569
|
match the type of the attribute. The values are compared using the `==` operator,
|
570
570
|
so the type must implement the `==` method.
|
571
571
|
|
572
|
+
Also, If all the elements in `values` directive are strings then `lutaml-model` add some enum convenience methods, for each of the value the following three methods are added
|
573
|
+
|
574
|
+
* `value1`: will return value if set
|
575
|
+
* `value1?`: will return true if value is set, false otherwise
|
576
|
+
* `value1=`: will set the value of `name_of_attribute` equal to `value1` if truthy value is given, and remove it otherwise.
|
577
|
+
|
572
578
|
.Using the `values` directive to define acceptable values for an attribute (basic types)
|
573
579
|
[example]
|
574
580
|
====
|
@@ -76,6 +76,10 @@ module Lutaml
|
|
76
76
|
@raw
|
77
77
|
end
|
78
78
|
|
79
|
+
def enum?
|
80
|
+
!enum_values.empty?
|
81
|
+
end
|
82
|
+
|
79
83
|
def default
|
80
84
|
value = if delegate
|
81
85
|
type.attributes[to].default
|
@@ -98,7 +102,7 @@ module Lutaml
|
|
98
102
|
|
99
103
|
def valid_value!(value)
|
100
104
|
return true if value.nil? && !collection?
|
101
|
-
return true
|
105
|
+
return true unless enum?
|
102
106
|
|
103
107
|
unless valid_value?(value)
|
104
108
|
raise Lutaml::Model::InvalidValueError.new(name, value, enum_values)
|
@@ -208,7 +212,7 @@ module Lutaml
|
|
208
212
|
serialize(v, format, options)
|
209
213
|
end
|
210
214
|
elsif type <= Serialize
|
211
|
-
type.
|
215
|
+
type.public_send(:"as_#{format}", value, options)
|
212
216
|
else
|
213
217
|
# Convert to Value instance if not already
|
214
218
|
value = type.new(value) unless value.is_a?(Type::Value)
|
@@ -81,16 +81,104 @@ module Lutaml
|
|
81
81
|
attr = Attribute.new(name, type, options)
|
82
82
|
attributes[name] = attr
|
83
83
|
|
84
|
-
|
85
|
-
|
84
|
+
if attr.enum?
|
85
|
+
add_enum_methods_to_model(
|
86
|
+
model,
|
87
|
+
name,
|
88
|
+
options[:values],
|
89
|
+
collection: options[:collection],
|
90
|
+
)
|
91
|
+
else
|
92
|
+
define_method(name) do
|
93
|
+
instance_variable_get(:"@#{name}")
|
94
|
+
end
|
95
|
+
|
96
|
+
define_method(:"#{name}=") do |value|
|
97
|
+
value_set_for(name)
|
98
|
+
instance_variable_set(:"@#{name}", attr.cast_value(value))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def add_enum_methods_to_model(klass, enum_name, values, collection: false)
|
104
|
+
add_enum_getter_if_not_defined(klass, enum_name, collection)
|
105
|
+
add_enum_setter_if_not_defined(klass, enum_name, values, collection)
|
106
|
+
|
107
|
+
return unless values.all?(::String)
|
108
|
+
|
109
|
+
values.each do |value|
|
110
|
+
Utils.add_method_if_not_defined(klass, "#{value}?") do
|
111
|
+
curr_value = public_send(:"#{enum_name}")
|
112
|
+
|
113
|
+
if collection
|
114
|
+
curr_value.include?(value)
|
115
|
+
else
|
116
|
+
curr_value == value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
Utils.add_method_if_not_defined(klass, value.to_s) do
|
121
|
+
public_send(:"#{value}?")
|
122
|
+
end
|
123
|
+
|
124
|
+
Utils.add_method_if_not_defined(klass, "#{value}=") do |val|
|
125
|
+
value_set_for(enum_name)
|
126
|
+
enum_vals = public_send(:"#{enum_name}")
|
127
|
+
|
128
|
+
enum_vals = if !!val
|
129
|
+
if collection
|
130
|
+
enum_vals << value
|
131
|
+
else
|
132
|
+
[value]
|
133
|
+
end
|
134
|
+
elsif collection
|
135
|
+
enum_vals.delete(value)
|
136
|
+
enum_vals
|
137
|
+
else
|
138
|
+
[]
|
139
|
+
end
|
140
|
+
|
141
|
+
instance_variable_set(:"@#{enum_name}", enum_vals)
|
142
|
+
end
|
143
|
+
|
144
|
+
Utils.add_method_if_not_defined(klass, "#{value}!") do
|
145
|
+
public_send(:"#{value}=", true)
|
146
|
+
end
|
86
147
|
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def add_enum_getter_if_not_defined(klass, enum_name, collection)
|
151
|
+
Utils.add_method_if_not_defined(klass, enum_name) do
|
152
|
+
i = instance_variable_get(:"@#{enum_name}") || []
|
87
153
|
|
88
|
-
|
89
|
-
|
90
|
-
|
154
|
+
if !collection && i.is_a?(Array)
|
155
|
+
i.first
|
156
|
+
else
|
157
|
+
i.uniq
|
158
|
+
end
|
91
159
|
end
|
92
160
|
end
|
93
161
|
|
162
|
+
def add_enum_setter_if_not_defined(klass, enum_name, _values, collection)
|
163
|
+
Utils.add_method_if_not_defined(klass, "#{enum_name}=") do |value|
|
164
|
+
value = [value] unless value.is_a?(Array)
|
165
|
+
|
166
|
+
value_set_for(enum_name)
|
167
|
+
|
168
|
+
if collection
|
169
|
+
curr_value = public_send(:"#{enum_name}")
|
170
|
+
|
171
|
+
instance_variable_set(:"@#{enum_name}", curr_value + value)
|
172
|
+
else
|
173
|
+
instance_variable_set(:"@#{enum_name}", value)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def enums
|
179
|
+
attributes.select { |_, attr| attr.enum? }
|
180
|
+
end
|
181
|
+
|
94
182
|
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
95
183
|
define_method(format) do |&block|
|
96
184
|
klass = format == :xml ? XmlMapping : KeyValueMapping
|
@@ -135,7 +223,7 @@ module Lutaml
|
|
135
223
|
end
|
136
224
|
end
|
137
225
|
|
138
|
-
define_method(:"as_#{format}") do |instance|
|
226
|
+
define_method(:"as_#{format}") do |instance, options = {}|
|
139
227
|
if instance.is_a?(Array)
|
140
228
|
return instance.map { |item| public_send(:"as_#{format}", item) }
|
141
229
|
end
|
@@ -147,7 +235,7 @@ module Lutaml
|
|
147
235
|
|
148
236
|
return instance if format == :xml
|
149
237
|
|
150
|
-
hash_representation(instance, format)
|
238
|
+
hash_representation(instance, format, options)
|
151
239
|
end
|
152
240
|
end
|
153
241
|
|
@@ -166,7 +254,7 @@ module Lutaml
|
|
166
254
|
mappings.each_with_object({}) do |rule, hash|
|
167
255
|
name = rule.to
|
168
256
|
next if except&.include?(name) || (only && !only.include?(name))
|
169
|
-
next if !rule.render_default? && instance.using_default?(rule.to)
|
257
|
+
next if !rule.custom_methods[:to] && (!rule.render_default? && instance.using_default?(rule.to))
|
170
258
|
|
171
259
|
next handle_delegate(instance, rule, hash, format) if rule.delegate
|
172
260
|
|
@@ -176,15 +264,15 @@ module Lutaml
|
|
176
264
|
|
177
265
|
value = instance.send(name)
|
178
266
|
|
179
|
-
next if
|
267
|
+
next if Utils.blank?(value) && !rule.render_nil
|
180
268
|
|
181
269
|
attribute = attributes[name]
|
182
270
|
|
183
|
-
hash[rule.from] = if rule.child_mappings
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
271
|
+
hash[rule.from.to_s] = if rule.child_mappings
|
272
|
+
generate_hash_from_child_mappings(value, rule.child_mappings)
|
273
|
+
else
|
274
|
+
attribute.serialize(value, format, options)
|
275
|
+
end
|
188
276
|
end
|
189
277
|
end
|
190
278
|
|
@@ -194,7 +282,7 @@ module Lutaml
|
|
194
282
|
return if value.nil? && !rule.render_nil
|
195
283
|
|
196
284
|
attribute = instance.send(rule.delegate).class.attributes[name]
|
197
|
-
hash[rule.from] = attribute.serialize(value, format)
|
285
|
+
hash[rule.from.to_s] = attribute.serialize(value, format)
|
198
286
|
end
|
199
287
|
|
200
288
|
def mappings_for(format)
|
@@ -379,10 +467,10 @@ module Lutaml
|
|
379
467
|
|
380
468
|
attr = attribute_for_rule(rule)
|
381
469
|
|
382
|
-
value = if doc.key?(rule.name) || doc.key?(rule.name.to_sym)
|
383
|
-
doc[rule.name] || doc[rule.name.to_sym]
|
470
|
+
value = if doc.key?(rule.name.to_s) || doc.key?(rule.name.to_sym)
|
471
|
+
doc[rule.name.to_s] || doc[rule.name.to_sym]
|
384
472
|
else
|
385
|
-
attr
|
473
|
+
attr&.default
|
386
474
|
end
|
387
475
|
|
388
476
|
if rule.custom_methods[:from]
|
@@ -490,7 +578,9 @@ module Lutaml
|
|
490
578
|
value = []
|
491
579
|
end
|
492
580
|
|
493
|
-
|
581
|
+
default = using_default?(name)
|
582
|
+
public_send(:"#{name}=", self.class.ensure_utf8(value))
|
583
|
+
using_default_for(name) if default
|
494
584
|
end
|
495
585
|
end
|
496
586
|
|
@@ -539,6 +629,14 @@ module Lutaml
|
|
539
629
|
hash[key.to_sym] || hash[key.to_s]
|
540
630
|
end
|
541
631
|
|
632
|
+
def pretty_print_instance_variables
|
633
|
+
(instance_variables - %i[@using_default]).sort
|
634
|
+
end
|
635
|
+
|
636
|
+
def to_yaml_hash
|
637
|
+
self.class.as_yaml(self)
|
638
|
+
end
|
639
|
+
|
542
640
|
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
543
641
|
define_method(:"to_#{format}") do |options = {}|
|
544
642
|
adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
|
@@ -34,10 +34,10 @@ module Lutaml
|
|
34
34
|
# value&.iso8601
|
35
35
|
# end
|
36
36
|
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
# YAML timestamp format (native)
|
38
|
+
def to_yaml
|
39
|
+
value&.iso8601
|
40
|
+
end
|
41
41
|
|
42
42
|
# # TOML time format (HH:MM:SS.mmm)
|
43
43
|
# def to_toml
|
data/lib/lutaml/model/utils.rb
CHANGED
@@ -4,9 +4,13 @@ module Lutaml
|
|
4
4
|
def validate
|
5
5
|
errors = []
|
6
6
|
self.class.attributes.each do |name, attr|
|
7
|
-
value =
|
7
|
+
value = public_send(:"#{name}")
|
8
8
|
begin
|
9
|
-
|
9
|
+
if value.respond_to?(:validate!)
|
10
|
+
value.validate!
|
11
|
+
else
|
12
|
+
attr.validate_value!(value)
|
13
|
+
end
|
10
14
|
rescue Lutaml::Model::InvalidValueError,
|
11
15
|
Lutaml::Model::CollectionCountOutOfRangeError,
|
12
16
|
PatternNotMatchedError => e
|
data/lib/lutaml/model/version.rb
CHANGED
@@ -138,6 +138,22 @@ RSpec.describe CustomSerialization do
|
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
|
+
context "with partial JSON input" do
|
142
|
+
it "deserializes from JSON with missing attributes" do
|
143
|
+
json = {
|
144
|
+
name: "JSON Masterpiece: Vase",
|
145
|
+
color: "BLUE",
|
146
|
+
}.to_json
|
147
|
+
|
148
|
+
ceramic = described_class.from_json(json)
|
149
|
+
|
150
|
+
expect(ceramic.full_name).to eq("Vase")
|
151
|
+
expect(ceramic.color).to eq("blue")
|
152
|
+
expect(ceramic.size).to be_nil
|
153
|
+
expect(ceramic.description).to be_nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
141
157
|
context "with XML serialization" do
|
142
158
|
it "serializes to XML with custom methods" do
|
143
159
|
expected_xml = <<~XML
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "lutaml/model"
|
3
|
+
|
4
|
+
module EnumSpec
|
5
|
+
class WithEnum < Lutaml::Model::Serializable
|
6
|
+
attribute :without_enum, :string
|
7
|
+
attribute :single_value, :string, values: %w[user admin super_admin]
|
8
|
+
attribute :multi_value, :string, values: %w[singular dual plural], collection: true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec.describe "Enum" do
|
13
|
+
let(:single_value_attr) do
|
14
|
+
EnumSpec::WithEnum.attributes[:single_value]
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:multi_value_attr) do
|
18
|
+
EnumSpec::WithEnum.attributes[:multi_value]
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:without_enum_attr) do
|
22
|
+
EnumSpec::WithEnum.attributes[:without_enum]
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:object) do
|
26
|
+
EnumSpec::WithEnum.new
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when values are provided for an attribute" do
|
30
|
+
it "is marked as enum for single_value" do
|
31
|
+
expect(single_value_attr.enum?).to be(true)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "is marked as enum for multi_value" do
|
35
|
+
expect(multi_value_attr.enum?).to be(true)
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with enum convinience methods" do
|
39
|
+
describe "#single_value" do
|
40
|
+
it "returns single value" do
|
41
|
+
expect { object.single_value = "user" }
|
42
|
+
.to change(object, :single_value)
|
43
|
+
.from(nil)
|
44
|
+
.to("user")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#multi_value" do
|
49
|
+
it "returns single value in array" do
|
50
|
+
expect { object.multi_value = "dual" }
|
51
|
+
.to change(object, :multi_value)
|
52
|
+
.from([])
|
53
|
+
.to(["dual"])
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns multiple value in array" do
|
57
|
+
expect { object.multi_value = %w[dual plural] }
|
58
|
+
.to change(object, :multi_value)
|
59
|
+
.from([])
|
60
|
+
.to(%w[dual plural])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
EnumSpec::WithEnum.enums.each_value do |enum_attr|
|
65
|
+
enum_attr.enum_values.each do |value|
|
66
|
+
describe "##{value}=" do
|
67
|
+
it "sets the #{value} if true" do
|
68
|
+
expect { object.public_send(:"#{value}=", true) }
|
69
|
+
.to change { object.public_send(:"#{value}?") }
|
70
|
+
.from(false)
|
71
|
+
.to(true)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "unsets the #{value} if false" do
|
75
|
+
object.public_send(:"#{value}=", true)
|
76
|
+
|
77
|
+
expect { object.public_send(:"#{value}=", false) }
|
78
|
+
.to change(object, "#{value}?")
|
79
|
+
.from(true)
|
80
|
+
.to(false)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "##{value}!" do
|
85
|
+
it "method #{value}? should be present" do
|
86
|
+
expect(object.respond_to?(:"#{value}!")).to be(true)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "sets #{value} to true for enum" do
|
90
|
+
expect { object.public_send(:"#{value}!") }
|
91
|
+
.to change(object, "#{value}?")
|
92
|
+
.from(false)
|
93
|
+
.to(true)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "##{value}?" do
|
98
|
+
it "method #{value}? should be present" do
|
99
|
+
expect(object.respond_to?(:"#{value}?")).to be(true)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "is false if role is not #{value}" do
|
103
|
+
expect(object.public_send(:"#{value}?")).to be(false)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "is true if role is set to #{value}" do
|
107
|
+
expect { object.public_send(:"#{value}=", value) }
|
108
|
+
.to change(object, "#{value}?")
|
109
|
+
.from(false)
|
110
|
+
.to(true)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it "adds a method named #{value}=" do
|
115
|
+
expect(object.respond_to?(:"#{value}?")).to be(true)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "adds a method named #{value}!" do
|
119
|
+
expect(object.respond_to?(:"#{value}!")).to be(true)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when values are not provided for an attribute" do
|
127
|
+
it "is not marked as enum" do
|
128
|
+
expect(without_enum_attr.enum?).to be(false)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -68,7 +68,6 @@ RSpec.describe RenderNil do
|
|
68
68
|
name: nil,
|
69
69
|
clay_type: nil,
|
70
70
|
glaze: nil,
|
71
|
-
dimensions: [],
|
72
71
|
}.to_json
|
73
72
|
|
74
73
|
expect(model.to_json).to eq(expected_json)
|
@@ -113,7 +112,6 @@ RSpec.describe RenderNil do
|
|
113
112
|
---
|
114
113
|
name:
|
115
114
|
glaze:
|
116
|
-
dimensions: []
|
117
115
|
YAML
|
118
116
|
|
119
117
|
generated_yaml = model.to_yaml.strip
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lutaml-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -139,6 +139,7 @@ files:
|
|
139
139
|
- spec/lutaml/model/custom_serialization_spec.rb
|
140
140
|
- spec/lutaml/model/defaults_spec.rb
|
141
141
|
- spec/lutaml/model/delegation_spec.rb
|
142
|
+
- spec/lutaml/model/enum_spec.rb
|
142
143
|
- spec/lutaml/model/inheritance_spec.rb
|
143
144
|
- spec/lutaml/model/json_adapter_spec.rb
|
144
145
|
- spec/lutaml/model/key_value_mapping_spec.rb
|