lutaml-model 0.3.9 → 0.3.11
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 +43 -25
- data/Gemfile +1 -0
- data/LICENSE.md +33 -0
- data/README.adoc +384 -18
- data/lib/lutaml/model/attribute.rb +130 -5
- data/lib/lutaml/model/error/collection_count_out_of_range_error.rb +29 -0
- data/lib/lutaml/model/error/incorrect_mapping_argument_error.rb +6 -0
- data/lib/lutaml/model/error/type_not_enabled_error.rb +9 -0
- data/lib/lutaml/model/error/validation_error.rb +21 -0
- data/lib/lutaml/model/error.rb +4 -0
- data/lib/lutaml/model/json_adapter/standard_json_adapter.rb +1 -2
- data/lib/lutaml/model/key_value_mapping.rb +15 -1
- data/lib/lutaml/model/mapping_hash.rb +14 -0
- data/lib/lutaml/model/mapping_rule.rb +30 -6
- data/lib/lutaml/model/schema_location.rb +59 -0
- data/lib/lutaml/model/serialize.rb +135 -86
- data/lib/lutaml/model/type.rb +8 -0
- data/lib/lutaml/model/utils.rb +16 -0
- data/lib/lutaml/model/validation.rb +24 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +12 -2
- data/lib/lutaml/model/xml_adapter/builder/ox.rb +4 -0
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +2 -1
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +1 -0
- data/lib/lutaml/model/xml_adapter/xml_document.rb +60 -30
- data/lib/lutaml/model/xml_adapter/xml_namespace.rb +1 -1
- data/lib/lutaml/model/xml_mapping.rb +27 -5
- data/lib/lutaml/model/xml_mapping_rule.rb +3 -1
- data/lib/lutaml/model/yaml_adapter/standard_yaml_adapter.rb +10 -5
- data/lutaml-model.gemspec +1 -1
- metadata +11 -17
@@ -0,0 +1,29 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
class CollectionCountOutOfRangeError < Error
|
4
|
+
def initialize(attr_name, value, range)
|
5
|
+
@attr_name = attr_name
|
6
|
+
@value = value
|
7
|
+
@range = range
|
8
|
+
|
9
|
+
super()
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"#{@attr_name} count is #{@value.size}, must be #{range_to_string}"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def range_to_string
|
19
|
+
if @range.end.nil?
|
20
|
+
"at least #{@range.begin}"
|
21
|
+
elsif @range.begin == @range.end
|
22
|
+
"exactly #{@range.begin}"
|
23
|
+
else
|
24
|
+
"between #{@range.begin} and #{@range.end}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# lib/lutaml/model/error/validation_error.rb
|
2
|
+
module Lutaml
|
3
|
+
module Model
|
4
|
+
class ValidationError < Error
|
5
|
+
attr_reader :errors
|
6
|
+
|
7
|
+
def initialize(errors)
|
8
|
+
@errors = errors
|
9
|
+
super(errors.join(", "))
|
10
|
+
end
|
11
|
+
|
12
|
+
def include?(error_class)
|
13
|
+
errors.any?(error_class)
|
14
|
+
end
|
15
|
+
|
16
|
+
def error_messages
|
17
|
+
errors.map(&:message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/lutaml/model/error.rb
CHANGED
@@ -6,4 +6,8 @@ module Lutaml
|
|
6
6
|
end
|
7
7
|
|
8
8
|
require_relative "error/invalid_value_error"
|
9
|
+
require_relative "error/incorrect_mapping_argument_error"
|
9
10
|
require_relative "error/unknown_adapter_type_error"
|
11
|
+
require_relative "error/collection_count_out_of_range_error"
|
12
|
+
require_relative "error/validation_error"
|
13
|
+
require_relative "error/type_not_enabled_error"
|
@@ -11,12 +11,14 @@ module Lutaml
|
|
11
11
|
|
12
12
|
def map(
|
13
13
|
name,
|
14
|
-
to
|
14
|
+
to: nil,
|
15
15
|
render_nil: false,
|
16
16
|
with: {},
|
17
17
|
delegate: nil,
|
18
18
|
child_mappings: nil
|
19
19
|
)
|
20
|
+
validate!(name, to, with)
|
21
|
+
|
20
22
|
@mappings << KeyValueMappingRule.new(
|
21
23
|
name,
|
22
24
|
to: to,
|
@@ -28,6 +30,18 @@ module Lutaml
|
|
28
30
|
end
|
29
31
|
|
30
32
|
alias map_element map
|
33
|
+
|
34
|
+
def validate!(key, to, with)
|
35
|
+
if to.nil? && with.empty?
|
36
|
+
msg = ":to or :with argument is required for mapping '#{key}'"
|
37
|
+
raise IncorrectMappingArgumentsError.new(msg)
|
38
|
+
end
|
39
|
+
|
40
|
+
if !with.empty? && (with[:from].nil? || with[:to].nil?)
|
41
|
+
msg = ":with argument for mapping '#{key}' requires :to and :from keys"
|
42
|
+
raise IncorrectMappingArgumentsError.new(msg)
|
43
|
+
end
|
44
|
+
end
|
31
45
|
end
|
32
46
|
end
|
33
47
|
end
|
@@ -24,6 +24,20 @@ module Lutaml
|
|
24
24
|
@ordered
|
25
25
|
end
|
26
26
|
|
27
|
+
def method_missing(method_name, *args)
|
28
|
+
value = self[method_name] || self[method_name.to_s]
|
29
|
+
return value if value
|
30
|
+
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def respond_to_missing?(method_name, include_private = false)
|
35
|
+
key_present = key?(method_name) || key?(method_name.to_s)
|
36
|
+
return true if key_present
|
37
|
+
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
27
41
|
private
|
28
42
|
|
29
43
|
def normalize(key)
|
@@ -17,6 +17,7 @@ module Lutaml
|
|
17
17
|
delegate: nil,
|
18
18
|
mixed_content: false,
|
19
19
|
namespace_set: false,
|
20
|
+
prefix_set: false,
|
20
21
|
child_mappings: nil
|
21
22
|
)
|
22
23
|
@name = name
|
@@ -26,6 +27,7 @@ module Lutaml
|
|
26
27
|
@delegate = delegate
|
27
28
|
@mixed_content = mixed_content
|
28
29
|
@namespace_set = namespace_set
|
30
|
+
@prefix_set = prefix_set
|
29
31
|
@child_mappings = child_mappings
|
30
32
|
end
|
31
33
|
|
@@ -40,25 +42,47 @@ module Lutaml
|
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
|
-
def
|
45
|
+
def serialize_attribute(model, element, doc)
|
44
46
|
if custom_methods[:to]
|
45
|
-
model.send(custom_methods[:to], model,
|
47
|
+
model.send(custom_methods[:to], model, element, doc)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def serialize(model, parent = nil, doc = nil)
|
52
|
+
if custom_methods[:to]
|
53
|
+
model.send(custom_methods[:to], model, parent, doc)
|
54
|
+
elsif delegate
|
55
|
+
model.public_send(delegate).public_send(to)
|
46
56
|
else
|
47
|
-
|
57
|
+
model.public_send(to)
|
48
58
|
end
|
49
59
|
end
|
50
60
|
|
51
|
-
def deserialize(model,
|
61
|
+
def deserialize(model, value, attributes, mapper_class = nil)
|
52
62
|
if custom_methods[:from]
|
53
|
-
|
63
|
+
mapper_class.new.send(custom_methods[:from], model, value)
|
64
|
+
elsif delegate
|
65
|
+
if model.public_send(delegate).nil?
|
66
|
+
model.public_send(:"#{delegate}=", attributes[delegate].type.new)
|
67
|
+
end
|
68
|
+
|
69
|
+
model.public_send(delegate).public_send(:"#{to}=", value)
|
54
70
|
else
|
55
|
-
|
71
|
+
model.public_send(:"#{to}=", value)
|
56
72
|
end
|
57
73
|
end
|
58
74
|
|
59
75
|
def namespace_set?
|
60
76
|
@namespace_set
|
61
77
|
end
|
78
|
+
|
79
|
+
def prefix_set?
|
80
|
+
@prefix_set
|
81
|
+
end
|
82
|
+
|
83
|
+
def content_mapping?
|
84
|
+
name.nil?
|
85
|
+
end
|
62
86
|
end
|
63
87
|
end
|
64
88
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
class Location
|
4
|
+
attr_reader :namespace, :location
|
5
|
+
|
6
|
+
def initialize(namespace:, location:)
|
7
|
+
@namespace = namespace
|
8
|
+
@location = location
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_xml_attribute
|
12
|
+
"#{@namespace} #{@location}".strip
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class SchemaLocation
|
17
|
+
DEFAULT_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance".freeze
|
18
|
+
|
19
|
+
attr_reader :namespace, :prefix, :schema_location
|
20
|
+
|
21
|
+
def initialize(schema_location:, prefix: "xsi",
|
22
|
+
namespace: DEFAULT_NAMESPACE)
|
23
|
+
@original_schema_location = schema_location
|
24
|
+
@schema_location = parsed_schema_locations(schema_location)
|
25
|
+
@prefix = prefix
|
26
|
+
@namespace = namespace
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_xml_attributes
|
30
|
+
{
|
31
|
+
"xmlns:#{prefix}" => namespace,
|
32
|
+
"#{prefix}:schemaLocation" => schema_location.map(&:to_xml_attribute).join(" "),
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](index)
|
37
|
+
@schema_location[index]
|
38
|
+
end
|
39
|
+
|
40
|
+
def size
|
41
|
+
@schema_location.size
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def parsed_schema_locations(schema_location)
|
47
|
+
locations = if schema_location.is_a?(Hash)
|
48
|
+
schema_location
|
49
|
+
else
|
50
|
+
schema_location.split.each_slice(2)
|
51
|
+
end
|
52
|
+
|
53
|
+
locations.map do |n, l|
|
54
|
+
Location.new(namespace: n, location: l)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -9,11 +9,15 @@ require_relative "xml_mapping"
|
|
9
9
|
require_relative "key_value_mapping"
|
10
10
|
require_relative "json_adapter"
|
11
11
|
require_relative "comparable_model"
|
12
|
+
require_relative "schema_location"
|
13
|
+
require_relative "validation"
|
14
|
+
require_relative "error"
|
12
15
|
|
13
16
|
module Lutaml
|
14
17
|
module Model
|
15
18
|
module Serialize
|
16
19
|
include ComparableModel
|
20
|
+
include Validation
|
17
21
|
|
18
22
|
def self.included(base)
|
19
23
|
base.extend(ClassMethods)
|
@@ -36,11 +40,34 @@ module Lutaml
|
|
36
40
|
def model(klass = nil)
|
37
41
|
if klass
|
38
42
|
@model = klass
|
43
|
+
add_order_handling_methods_to_model(klass)
|
39
44
|
else
|
40
45
|
@model
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
49
|
+
def add_order_handling_methods_to_model(klass)
|
50
|
+
Utils.add_method_if_not_defined(klass, :ordered=) do |ordered|
|
51
|
+
@ordered = ordered
|
52
|
+
end
|
53
|
+
|
54
|
+
Utils.add_method_if_not_defined(klass, :ordered?) do
|
55
|
+
!!@ordered
|
56
|
+
end
|
57
|
+
|
58
|
+
Utils.add_method_if_not_defined(klass, :element_order=) do |order|
|
59
|
+
@element_order = order
|
60
|
+
end
|
61
|
+
|
62
|
+
Utils.add_method_if_not_defined(klass, :element_order) do
|
63
|
+
@element_order
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def cast(value)
|
68
|
+
value
|
69
|
+
end
|
70
|
+
|
44
71
|
# Define an attribute for the model
|
45
72
|
def attribute(name, type, options = {})
|
46
73
|
attr = Attribute.new(name, type, options)
|
@@ -51,26 +78,10 @@ module Lutaml
|
|
51
78
|
end
|
52
79
|
|
53
80
|
define_method(:"#{name}=") do |value|
|
54
|
-
instance_variable_set(:"@#{name}", value)
|
55
|
-
validate
|
81
|
+
instance_variable_set(:"@#{name}", attr.cast_value(value))
|
56
82
|
end
|
57
83
|
end
|
58
84
|
|
59
|
-
# Check if the value to be assigned is valid for the attribute
|
60
|
-
def attr_value_valid?(name, value)
|
61
|
-
attr = attributes[name]
|
62
|
-
|
63
|
-
return true unless attr.options[:values]
|
64
|
-
|
65
|
-
# Allow nil values if there's no default
|
66
|
-
return true if value.nil? && !attr.default
|
67
|
-
|
68
|
-
# Use the default value if the value is nil
|
69
|
-
value = attr.default if value.nil?
|
70
|
-
|
71
|
-
attr.options[:values].include?(value)
|
72
|
-
end
|
73
|
-
|
74
85
|
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
75
86
|
define_method(format) do |&block|
|
76
87
|
klass = format == :xml ? XmlMapping : KeyValueMapping
|
@@ -86,17 +97,17 @@ module Lutaml
|
|
86
97
|
adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
|
87
98
|
|
88
99
|
doc = adapter.parse(data)
|
89
|
-
public_send(:"of_#{format}", doc
|
100
|
+
public_send(:"of_#{format}", doc)
|
90
101
|
end
|
91
102
|
|
92
|
-
define_method(:"of_#{format}") do |
|
93
|
-
if
|
94
|
-
return
|
95
|
-
|
96
|
-
|
103
|
+
define_method(:"of_#{format}") do |doc|
|
104
|
+
if doc.is_a?(Array)
|
105
|
+
return doc.map do |item|
|
106
|
+
send(:"of_#{format}", item)
|
107
|
+
end
|
97
108
|
end
|
98
109
|
|
99
|
-
apply_mappings(
|
110
|
+
apply_mappings(doc.to_h, format)
|
100
111
|
end
|
101
112
|
|
102
113
|
define_method(:"to_#{format}") do |instance|
|
@@ -197,6 +208,7 @@ module Lutaml
|
|
197
208
|
|
198
209
|
def default_mappings(format)
|
199
210
|
klass = format == :xml ? XmlMapping : KeyValueMapping
|
211
|
+
|
200
212
|
klass.new.tap do |mapping|
|
201
213
|
attributes&.each do |name, attr|
|
202
214
|
mapping.map_element(
|
@@ -205,6 +217,8 @@ module Lutaml
|
|
205
217
|
render_nil: attr.render_nil?,
|
206
218
|
)
|
207
219
|
end
|
220
|
+
|
221
|
+
mapping.root(to_s.split("::").last) if format == :xml
|
208
222
|
end
|
209
223
|
end
|
210
224
|
|
@@ -254,20 +268,28 @@ module Lutaml
|
|
254
268
|
hash
|
255
269
|
end
|
256
270
|
|
271
|
+
def valid_rule?(rule)
|
272
|
+
attribute = attribute_for_rule(rule)
|
273
|
+
|
274
|
+
!!attribute || rule.custom_methods[:from]
|
275
|
+
end
|
276
|
+
|
277
|
+
def attribute_for_rule(rule)
|
278
|
+
return attributes[rule.to] unless rule.delegate
|
279
|
+
|
280
|
+
attributes[rule.delegate].type.attributes[rule.to]
|
281
|
+
end
|
282
|
+
|
257
283
|
def apply_mappings(doc, format, options = {})
|
258
284
|
instance = options[:instance] || model.new
|
259
|
-
return instance if
|
285
|
+
return instance if Utils.blank?(doc)
|
260
286
|
return apply_xml_mapping(doc, instance, options) if format == :xml
|
261
287
|
|
262
288
|
mappings = mappings_for(format).mappings
|
263
289
|
mappings.each do |rule|
|
264
|
-
|
265
|
-
attributes[rule.delegate].type.attributes[rule.to]
|
266
|
-
else
|
267
|
-
attributes[rule.to]
|
268
|
-
end
|
290
|
+
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
|
269
291
|
|
270
|
-
|
292
|
+
attr = attribute_for_rule(rule)
|
271
293
|
|
272
294
|
value = if doc.key?(rule.name) || doc.key?(rule.name.to_sym)
|
273
295
|
doc[rule.name] || doc[rule.name.to_sym]
|
@@ -276,26 +298,17 @@ module Lutaml
|
|
276
298
|
end
|
277
299
|
|
278
300
|
if rule.custom_methods[:from]
|
279
|
-
if
|
280
|
-
value = new.send(rule.custom_methods[:from], instance,
|
281
|
-
value)
|
301
|
+
if Utils.present?(value)
|
302
|
+
value = new.send(rule.custom_methods[:from], instance, value)
|
282
303
|
end
|
304
|
+
|
283
305
|
next
|
284
306
|
end
|
285
307
|
|
286
308
|
value = apply_child_mappings(value, rule.child_mappings)
|
287
309
|
value = attr.cast(value, format)
|
288
310
|
|
289
|
-
|
290
|
-
if instance.public_send(rule.delegate).nil?
|
291
|
-
instance.public_send(:"#{rule.delegate}=",
|
292
|
-
attributes[rule.delegate].type.new)
|
293
|
-
end
|
294
|
-
instance.public_send(rule.delegate).public_send(:"#{rule.to}=",
|
295
|
-
value)
|
296
|
-
else
|
297
|
-
instance.public_send(:"#{rule.to}=", value)
|
298
|
-
end
|
311
|
+
rule.deserialize(instance, value, attributes, self)
|
299
312
|
end
|
300
313
|
|
301
314
|
instance
|
@@ -316,45 +329,62 @@ module Lutaml
|
|
316
329
|
instance.ordered = mappings_for(:xml).mixed_content? || options[:mixed_content]
|
317
330
|
end
|
318
331
|
|
319
|
-
|
320
|
-
|
321
|
-
|
332
|
+
if doc["__schema_location"]
|
333
|
+
instance.schema_location = Lutaml::Model::SchemaLocation.new(
|
334
|
+
schema_location: doc["__schema_location"][:schema_location],
|
335
|
+
prefix: doc["__schema_location"][:prefix],
|
336
|
+
namespace: doc["__schema_location"][:namespace],
|
337
|
+
)
|
338
|
+
end
|
322
339
|
|
323
|
-
|
340
|
+
mappings.each do |rule|
|
341
|
+
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
|
324
342
|
|
325
|
-
value = if
|
343
|
+
value = if rule.content_mapping?
|
326
344
|
doc["text"]
|
327
345
|
else
|
328
346
|
doc[rule.name.to_s] || doc[rule.name.to_sym]
|
329
347
|
end
|
330
348
|
|
331
|
-
value =
|
349
|
+
value = normalize_xml_value(value, rule)
|
350
|
+
rule.deserialize(instance, value, attributes, self)
|
351
|
+
end
|
332
352
|
|
333
|
-
|
334
|
-
|
335
|
-
v.is_a?(Hash) && !(attr.type <= Serialize) ? v["text"] : v
|
336
|
-
end
|
337
|
-
elsif !(attr.type <= Serialize) && value.is_a?(Hash) && attr.type != Lutaml::Model::Type::Hash
|
338
|
-
value = value["text"]
|
339
|
-
end
|
353
|
+
instance
|
354
|
+
end
|
340
355
|
|
341
|
-
|
342
|
-
|
343
|
-
value,
|
344
|
-
:xml,
|
345
|
-
caller_class: self,
|
346
|
-
mixed_content: rule.mixed_content,
|
347
|
-
)
|
348
|
-
end
|
356
|
+
def normalize_xml_value(value, rule)
|
357
|
+
attr = attribute_for_rule(rule)
|
349
358
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
359
|
+
value = [value].compact if attr&.collection? && !value.is_a?(Array)
|
360
|
+
|
361
|
+
value = if value.is_a?(Array)
|
362
|
+
value.map do |v|
|
363
|
+
text_hash?(attr, v) ? v["text"] : v
|
364
|
+
end
|
365
|
+
elsif text_hash?(attr, value)
|
366
|
+
value["text"]
|
367
|
+
else
|
368
|
+
value
|
369
|
+
end
|
370
|
+
|
371
|
+
if attr && !rule.content_mapping?
|
372
|
+
value = attr.cast(
|
373
|
+
value,
|
374
|
+
:xml,
|
375
|
+
caller_class: self,
|
376
|
+
mixed_content: rule.mixed_content,
|
377
|
+
)
|
355
378
|
end
|
356
379
|
|
357
|
-
|
380
|
+
value
|
381
|
+
end
|
382
|
+
|
383
|
+
def text_hash?(attr, value)
|
384
|
+
return false unless value.is_a?(Hash)
|
385
|
+
return value.keys == ["text"] unless attr
|
386
|
+
|
387
|
+
!(attr.type <= Serialize) && attr.type != Lutaml::Model::Type::Hash
|
358
388
|
end
|
359
389
|
|
360
390
|
def ensure_utf8(value)
|
@@ -376,9 +406,11 @@ module Lutaml
|
|
376
406
|
end
|
377
407
|
end
|
378
408
|
|
379
|
-
attr_accessor :element_order
|
409
|
+
attr_accessor :element_order, :schema_location
|
380
410
|
|
381
411
|
def initialize(attrs = {})
|
412
|
+
@validate_on_set = attrs.delete(:validate_on_set) || false
|
413
|
+
|
382
414
|
return unless self.class.attributes
|
383
415
|
|
384
416
|
if attrs.is_a?(Lutaml::Model::MappingHash)
|
@@ -386,13 +418,41 @@ module Lutaml
|
|
386
418
|
@element_order = attrs.item_order
|
387
419
|
end
|
388
420
|
|
421
|
+
if attrs.key?(:schema_location)
|
422
|
+
self.schema_location = attrs[:schema_location]
|
423
|
+
end
|
424
|
+
|
389
425
|
self.class.attributes.each do |name, attr|
|
390
|
-
value =
|
426
|
+
value = if attrs.key?(name) || attrs.key?(name.to_s)
|
427
|
+
self.class.attr_value(attrs, name, attr)
|
428
|
+
else
|
429
|
+
attr.default
|
430
|
+
end
|
391
431
|
|
392
|
-
|
432
|
+
# Initialize collections with an empty array if no value is provided
|
433
|
+
if attr.collection? && value.nil?
|
434
|
+
value = []
|
435
|
+
end
|
436
|
+
|
437
|
+
instance_variable_set(:"@#{name}", self.class.ensure_utf8(value))
|
393
438
|
end
|
439
|
+
end
|
394
440
|
|
395
|
-
|
441
|
+
def method_missing(method_name, *args)
|
442
|
+
if method_name.to_s.end_with?("=") && self.class.attributes.key?(method_name.to_s.chomp("=").to_sym)
|
443
|
+
define_singleton_method(method_name) do |value|
|
444
|
+
instance_variable_set(:"@#{method_name.to_s.chomp('=')}", value)
|
445
|
+
end
|
446
|
+
send(method_name, *args)
|
447
|
+
else
|
448
|
+
super
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def validate_attribute!(attr_name)
|
453
|
+
attr = self.class.attributes[attr_name]
|
454
|
+
value = instance_variable_get(:"@#{attr_name}")
|
455
|
+
attr.validate_value!(value)
|
396
456
|
end
|
397
457
|
|
398
458
|
def ordered?
|
@@ -413,7 +473,6 @@ module Lutaml
|
|
413
473
|
|
414
474
|
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
415
475
|
define_method(:"to_#{format}") do |options = {}|
|
416
|
-
validate
|
417
476
|
adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
|
418
477
|
representation = if format == :xml
|
419
478
|
self
|
@@ -425,16 +484,6 @@ module Lutaml
|
|
425
484
|
adapter.new(representation).public_send(:"to_#{format}", options)
|
426
485
|
end
|
427
486
|
end
|
428
|
-
|
429
|
-
def validate
|
430
|
-
self.class.attributes.each do |name, attr|
|
431
|
-
value = send(name)
|
432
|
-
unless self.class.attr_value_valid?(name, value)
|
433
|
-
raise Lutaml::Model::InvalidValueError.new(name, value,
|
434
|
-
attr.options[:values])
|
435
|
-
end
|
436
|
-
end
|
437
|
-
end
|
438
487
|
end
|
439
488
|
end
|
440
489
|
end
|
data/lib/lutaml/model/type.rb
CHANGED
@@ -62,6 +62,10 @@ module Lutaml
|
|
62
62
|
when "Boolean"
|
63
63
|
to_boolean(value)
|
64
64
|
when "Decimal"
|
65
|
+
unless defined?(BigDecimal)
|
66
|
+
raise Lutaml::Model::TypeNotEnabledError.new("Decimal", value)
|
67
|
+
end
|
68
|
+
|
65
69
|
BigDecimal(value.to_s)
|
66
70
|
when "Hash"
|
67
71
|
normalize_hash(Hash(value))
|
@@ -85,6 +89,10 @@ module Lutaml
|
|
85
89
|
when "Boolean"
|
86
90
|
to_boolean(value)
|
87
91
|
when "Decimal"
|
92
|
+
unless defined?(BigDecimal)
|
93
|
+
raise Lutaml::Model::TypeNotEnabledError.new("Decimal", value)
|
94
|
+
end
|
95
|
+
|
88
96
|
value.to_s("F")
|
89
97
|
when "Hash"
|
90
98
|
Hash(value)
|
data/lib/lutaml/model/utils.rb
CHANGED
@@ -34,6 +34,22 @@ module Lutaml
|
|
34
34
|
.downcase
|
35
35
|
end
|
36
36
|
|
37
|
+
def present?(value)
|
38
|
+
!blank?(value)
|
39
|
+
end
|
40
|
+
|
41
|
+
def blank?(value)
|
42
|
+
value.respond_to?(:empty?) ? value.empty? : !value
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_method_if_not_defined(klass, method_name, &block)
|
46
|
+
unless klass.method_defined?(method_name)
|
47
|
+
klass.class_eval do
|
48
|
+
define_method(method_name, &block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
37
53
|
private
|
38
54
|
|
39
55
|
def camelize_part(part)
|