lutaml-model 0.3.30 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|