attributor 5.1.0 → 5.5
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/.travis.yml +4 -3
- data/CHANGELOG.md +145 -135
- data/attributor.gemspec +5 -6
- data/lib/attributor.rb +17 -2
- data/lib/attributor/attribute.rb +39 -9
- data/lib/attributor/dsl_compiler.rb +17 -9
- data/lib/attributor/exceptions.rb +5 -0
- data/lib/attributor/extras/field_selector.rb +4 -0
- data/lib/attributor/families/numeric.rb +19 -6
- data/lib/attributor/families/temporal.rb +16 -9
- data/lib/attributor/hash_dsl_compiler.rb +6 -6
- data/lib/attributor/smart_attribute_selector.rb +149 -0
- data/lib/attributor/type.rb +27 -4
- data/lib/attributor/types/bigdecimal.rb +7 -2
- data/lib/attributor/types/boolean.rb +7 -2
- data/lib/attributor/types/class.rb +2 -2
- data/lib/attributor/types/collection.rb +22 -5
- data/lib/attributor/types/container.rb +3 -3
- data/lib/attributor/types/csv.rb +5 -1
- data/lib/attributor/types/date.rb +9 -3
- data/lib/attributor/types/date_time.rb +8 -2
- data/lib/attributor/types/float.rb +4 -3
- data/lib/attributor/types/hash.rb +105 -21
- data/lib/attributor/types/integer.rb +7 -1
- data/lib/attributor/types/model.rb +2 -2
- data/lib/attributor/types/object.rb +5 -0
- data/lib/attributor/types/polymorphic.rb +3 -2
- data/lib/attributor/types/string.rb +20 -1
- data/lib/attributor/types/struct.rb +1 -1
- data/lib/attributor/types/symbol.rb +5 -0
- data/lib/attributor/types/tempfile.rb +4 -0
- data/lib/attributor/types/time.rb +7 -3
- data/lib/attributor/types/uri.rb +9 -1
- data/lib/attributor/version.rb +1 -1
- data/spec/attribute_spec.rb +42 -7
- data/spec/dsl_compiler_spec.rb +16 -6
- data/spec/extras/field_selector/field_selector_spec.rb +9 -0
- data/spec/hash_dsl_compiler_spec.rb +2 -2
- data/spec/smart_attribute_selector_spec.rb +272 -0
- data/spec/support/integers.rb +7 -0
- data/spec/type_spec.rb +1 -1
- data/spec/types/bigdecimal_spec.rb +8 -0
- data/spec/types/boolean_spec.rb +10 -0
- data/spec/types/class_spec.rb +0 -1
- data/spec/types/collection_spec.rb +16 -0
- data/spec/types/date_spec.rb +9 -0
- data/spec/types/date_time_spec.rb +9 -0
- data/spec/types/float_spec.rb +8 -0
- data/spec/types/hash_spec.rb +181 -9
- data/spec/types/integer_spec.rb +10 -1
- data/spec/types/model_spec.rb +14 -3
- data/spec/types/string_spec.rb +10 -0
- data/spec/types/temporal_spec.rb +5 -1
- data/spec/types/time_spec.rb +9 -0
- data/spec/types/uri_spec.rb +9 -0
- metadata +24 -34
@@ -1,13 +1,14 @@
|
|
1
1
|
require 'bigdecimal'
|
2
2
|
|
3
3
|
module Attributor
|
4
|
-
class BigDecimal
|
4
|
+
class BigDecimal
|
5
|
+
include Numeric
|
5
6
|
def self.native_type
|
6
7
|
::BigDecimal
|
7
8
|
end
|
8
9
|
|
9
10
|
def self.example(_context = nil, options: {})
|
10
|
-
|
11
|
+
BigDecimal("#{/\d{3}/.gen}.#{/\d{3}/.gen}")
|
11
12
|
end
|
12
13
|
|
13
14
|
def self.load(value, _context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
|
@@ -16,5 +17,9 @@ module Attributor
|
|
16
17
|
return BigDecimal(value, 10) if value.is_a?(::Float)
|
17
18
|
BigDecimal(value)
|
18
19
|
end
|
20
|
+
|
21
|
+
def self.json_schema_type
|
22
|
+
:number
|
23
|
+
end
|
19
24
|
end
|
20
25
|
end
|
@@ -17,14 +17,19 @@ module Attributor
|
|
17
17
|
def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **_options)
|
18
18
|
return nil if value.nil?
|
19
19
|
|
20
|
-
raise CoercionError
|
20
|
+
raise CoercionError.new(context: context, from: value.class, to: self, value: value) if value.is_a?(::Float)
|
21
21
|
return false if [false, 'false', 'FALSE', '0', 0, 'f', 'F'].include?(value)
|
22
22
|
return true if [true, 'true', 'TRUE', '1', 1, 't', 'T'].include?(value)
|
23
|
-
raise CoercionError
|
23
|
+
raise CoercionError.new(context: context, from: value.class, to: self)
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.family
|
27
27
|
'boolean'
|
28
28
|
end
|
29
|
+
|
30
|
+
def self.json_schema_type
|
31
|
+
:boolean
|
32
|
+
end
|
33
|
+
|
29
34
|
end
|
30
35
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'active_support'
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
2
|
|
3
3
|
require_relative '../exceptions'
|
4
4
|
|
@@ -16,7 +16,7 @@ module Attributor
|
|
16
16
|
|
17
17
|
# Must be given a String object or nil
|
18
18
|
unless value.is_a?(::String) || value.nil?
|
19
|
-
raise IncompatibleTypeError
|
19
|
+
raise IncompatibleTypeError.new(context: context, value_type: value.class, type: self)
|
20
20
|
end
|
21
21
|
|
22
22
|
value = '::' + value if value[0..1] != '::'
|
@@ -51,7 +51,7 @@ module Attributor
|
|
51
51
|
|
52
52
|
def self.member_attribute
|
53
53
|
@member_attribute ||= begin
|
54
|
-
construct(nil
|
54
|
+
construct(nil)
|
55
55
|
|
56
56
|
@member_attribute
|
57
57
|
end
|
@@ -59,6 +59,7 @@ module Attributor
|
|
59
59
|
|
60
60
|
# generates an example Collection
|
61
61
|
# @return An Array of native type objects conforming to the specified member_type
|
62
|
+
# TODO: ALLOW to pass "values" for the members?...as values: {id: 1, ...}
|
62
63
|
def self.example(context = nil, options: {})
|
63
64
|
result = []
|
64
65
|
size = options[:size] || (rand(3) + 1)
|
@@ -90,7 +91,7 @@ module Attributor
|
|
90
91
|
elsif value.respond_to?(:to_a)
|
91
92
|
loaded_value = value.to_a
|
92
93
|
else
|
93
|
-
raise Attributor::IncompatibleTypeError
|
94
|
+
raise Attributor::IncompatibleTypeError.new(context: context, value_type: value.class, type: self)
|
94
95
|
end
|
95
96
|
|
96
97
|
new(loaded_value.collect { |member| member_attribute.load(member, context) })
|
@@ -102,7 +103,7 @@ module Attributor
|
|
102
103
|
|
103
104
|
def self.dump(values, **opts)
|
104
105
|
return nil if values.nil?
|
105
|
-
values.collect { |value| member_attribute.dump(value, opts) }
|
106
|
+
values.collect { |value| member_attribute.dump(value, **opts) }
|
106
107
|
end
|
107
108
|
|
108
109
|
def self.describe(shallow = false, example: nil)
|
@@ -116,11 +117,27 @@ module Attributor
|
|
116
117
|
hash
|
117
118
|
end
|
118
119
|
|
120
|
+
def self.json_schema_type
|
121
|
+
:array
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.as_json_schema( shallow: false, example: nil, attribute_options: {} )
|
125
|
+
hash = super
|
126
|
+
opts = self.options.merge( attribute_options )
|
127
|
+
hash[:description] = opts[:description] if opts[:description]
|
128
|
+
hash[:default] = opts[:default] if opts[:default]
|
129
|
+
|
130
|
+
#hash[:examples] = [ example.dump ] if example
|
131
|
+
member_example = example && example.first
|
132
|
+
hash[:items] = member_attribute.as_json_schema(example: member_example)
|
133
|
+
hash
|
134
|
+
end
|
135
|
+
|
119
136
|
def self.constructable?
|
120
137
|
true
|
121
138
|
end
|
122
139
|
|
123
|
-
def self.construct(constructor_block, options)
|
140
|
+
def self.construct(constructor_block, **options)
|
124
141
|
member_options = (options[:member_options] || {}).clone
|
125
142
|
if options.key?(:reference) && !member_options.key?(:reference)
|
126
143
|
member_options[:reference] = options[:reference]
|
@@ -172,7 +189,7 @@ module Attributor
|
|
172
189
|
end
|
173
190
|
|
174
191
|
def dump(**opts)
|
175
|
-
collect { |value| self.class.member_attribute.dump(value, opts) }
|
192
|
+
collect { |value| self.class.member_attribute.dump(value, **opts) }
|
176
193
|
end
|
177
194
|
end
|
178
195
|
end
|
@@ -19,17 +19,17 @@ module Attributor
|
|
19
19
|
# @return [Array] a normal Ruby Array
|
20
20
|
#
|
21
21
|
def decode_json(value, context = Attributor::DEFAULT_ROOT_CONTEXT)
|
22
|
-
raise Attributor::DeserializationError
|
22
|
+
raise Attributor::DeserializationError.new(context: context, from: value.class, encoding: 'JSON', value: value) unless value.is_a? ::String
|
23
23
|
|
24
24
|
# attempt to parse as JSON
|
25
25
|
parsed_value = JSON.parse(value)
|
26
26
|
unless valid_type?(parsed_value)
|
27
|
-
raise Attributor::CoercionError
|
27
|
+
raise Attributor::CoercionError.new(context: context, from: parsed_value.class, to: name, value: parsed_value)
|
28
28
|
end
|
29
29
|
|
30
30
|
parsed_value
|
31
31
|
rescue JSON::JSONError
|
32
|
-
raise Attributor::DeserializationError
|
32
|
+
raise Attributor::DeserializationError.new(context: context, from: value.class, encoding: 'JSON', value: value)
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
data/lib/attributor/types/csv.rb
CHANGED
@@ -9,7 +9,7 @@ module Attributor
|
|
9
9
|
when ::String
|
10
10
|
values
|
11
11
|
when ::Array
|
12
|
-
values.collect { |value| member_attribute.dump(value, opts).to_s }.join(',')
|
12
|
+
values.collect { |value| member_attribute.dump(value, **opts).to_s }.join(',')
|
13
13
|
when nil
|
14
14
|
nil
|
15
15
|
else
|
@@ -37,5 +37,9 @@ module Attributor
|
|
37
37
|
def self.family
|
38
38
|
Collection.family
|
39
39
|
end
|
40
|
+
|
41
|
+
def self.json_schema_type
|
42
|
+
:string
|
43
|
+
end
|
40
44
|
end
|
41
45
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'date'
|
2
2
|
|
3
3
|
module Attributor
|
4
|
-
class Date
|
4
|
+
class Date
|
5
|
+
include Temporal
|
6
|
+
|
5
7
|
def self.native_type
|
6
8
|
::Date
|
7
9
|
end
|
@@ -21,11 +23,15 @@ module Attributor
|
|
21
23
|
begin
|
22
24
|
return ::Date.parse(value)
|
23
25
|
rescue ArgumentError
|
24
|
-
raise Attributor::DeserializationError
|
26
|
+
raise Attributor::DeserializationError.new(context: context, from: value.class, encoding: 'Date', value: value)
|
25
27
|
end
|
26
28
|
else
|
27
|
-
raise CoercionError
|
29
|
+
raise CoercionError.new(context: context, from: value.class, to: self, value: value)
|
28
30
|
end
|
29
31
|
end
|
32
|
+
|
33
|
+
def self.json_schema_string_format
|
34
|
+
:date
|
35
|
+
end
|
30
36
|
end
|
31
37
|
end
|
@@ -5,7 +5,9 @@ require_relative '../exceptions'
|
|
5
5
|
require 'date'
|
6
6
|
|
7
7
|
module Attributor
|
8
|
-
class DateTime
|
8
|
+
class DateTime
|
9
|
+
include Temporal
|
10
|
+
|
9
11
|
def self.native_type
|
10
12
|
::DateTime
|
11
13
|
end
|
@@ -24,8 +26,12 @@ module Attributor
|
|
24
26
|
begin
|
25
27
|
return ::DateTime.parse(value)
|
26
28
|
rescue ArgumentError
|
27
|
-
raise Attributor::DeserializationError
|
29
|
+
raise Attributor::DeserializationError.new(context: context, from: value.class, encoding: 'DateTime', value: value)
|
28
30
|
end
|
29
31
|
end
|
32
|
+
|
33
|
+
def self.json_schema_string_format
|
34
|
+
:'date-time'
|
35
|
+
end
|
30
36
|
end
|
31
37
|
end
|
@@ -2,8 +2,9 @@
|
|
2
2
|
# See: http://ruby-doc.org/core-2.1.0/Float.html
|
3
3
|
|
4
4
|
module Attributor
|
5
|
+
|
5
6
|
class Float
|
6
|
-
include
|
7
|
+
include Numeric
|
7
8
|
|
8
9
|
def self.native_type
|
9
10
|
::Float
|
@@ -22,8 +23,8 @@ module Attributor
|
|
22
23
|
super
|
23
24
|
end
|
24
25
|
|
25
|
-
def self.
|
26
|
-
|
26
|
+
def self.json_schema_type
|
27
|
+
:number
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
@@ -16,7 +16,7 @@ module Attributor
|
|
16
16
|
end
|
17
17
|
|
18
18
|
class Hash
|
19
|
-
MAX_EXAMPLE_DEPTH =
|
19
|
+
MAX_EXAMPLE_DEPTH = 10
|
20
20
|
CIRCULAR_REFERENCE_MARKER = '...'.freeze
|
21
21
|
|
22
22
|
include Container
|
@@ -82,7 +82,7 @@ module Attributor
|
|
82
82
|
def self.attributes(**options, &key_spec)
|
83
83
|
raise @error if @error
|
84
84
|
|
85
|
-
keys(options, &key_spec)
|
85
|
+
keys(**options, &key_spec)
|
86
86
|
end
|
87
87
|
|
88
88
|
def self.keys(**options, &key_spec)
|
@@ -97,6 +97,13 @@ module Attributor
|
|
97
97
|
@keys
|
98
98
|
end
|
99
99
|
|
100
|
+
def self.requirements
|
101
|
+
if @saved_blocks.any?
|
102
|
+
definition
|
103
|
+
end
|
104
|
+
@requirements
|
105
|
+
end
|
106
|
+
|
100
107
|
def self.definition
|
101
108
|
opts = {
|
102
109
|
key_type: @key_type,
|
@@ -104,7 +111,7 @@ module Attributor
|
|
104
111
|
}.merge(@options)
|
105
112
|
|
106
113
|
blocks = @saved_blocks.shift(@saved_blocks.size)
|
107
|
-
compiler = dsl_class.new(self, opts)
|
114
|
+
compiler = dsl_class.new(self, **opts)
|
108
115
|
compiler.parse(*blocks)
|
109
116
|
|
110
117
|
if opts[:case_insensitive_load] == true
|
@@ -164,15 +171,27 @@ module Attributor
|
|
164
171
|
raise Attributor::AttributorException, ":case_insensitive_load may not be used with keys of type #{key_type.name}"
|
165
172
|
end
|
166
173
|
|
167
|
-
keys(options, &constructor_block)
|
174
|
+
keys(**options, &constructor_block)
|
168
175
|
self
|
169
176
|
end
|
170
177
|
|
178
|
+
|
179
|
+
|
171
180
|
def self.example_contents(context, parent, **values)
|
172
181
|
hash = ::Hash.new
|
173
182
|
example_depth = context.size
|
174
|
-
|
175
|
-
|
183
|
+
# Be smart about what attributes to use for the example: i.e. have into account complex requirements
|
184
|
+
# that might have been defined in the hash like at_most(1).of ..., exactly(2).of ...etc.
|
185
|
+
# But play it safe and default to the previous behavior in case there is any error processing them
|
186
|
+
# ( that is until the SmartAttributeSelector class isn't fully tested and ready for prime time)
|
187
|
+
begin
|
188
|
+
stack = SmartAttributeSelector.new( requirements.map(&:describe), keys.keys , values)
|
189
|
+
selected = stack.process
|
190
|
+
rescue => e
|
191
|
+
selected = keys.keys
|
192
|
+
end
|
193
|
+
|
194
|
+
keys.select{|n,attr| selected.include? n}.each do |sub_attribute_name, sub_attribute|
|
176
195
|
if sub_attribute.attributes
|
177
196
|
# TODO: add option to raise an exception in this case?
|
178
197
|
next if example_depth > MAX_EXAMPLE_DEPTH
|
@@ -202,7 +221,7 @@ module Attributor
|
|
202
221
|
result = new
|
203
222
|
result.extend(ExampleMixin)
|
204
223
|
|
205
|
-
result.lazy_attributes = example_contents(context, result, values)
|
224
|
+
result.lazy_attributes = example_contents(context, result, **values)
|
206
225
|
else
|
207
226
|
hash = ::Hash.new
|
208
227
|
|
@@ -243,11 +262,11 @@ module Attributor
|
|
243
262
|
end
|
244
263
|
|
245
264
|
def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, recurse: false, **_options)
|
246
|
-
context = Array(context)
|
247
265
|
|
248
266
|
return value if value.is_a?(self)
|
249
267
|
return nil if value.nil? && !recurse
|
250
268
|
|
269
|
+
context = Array(context)
|
251
270
|
loaded_value = self.parse(value, context)
|
252
271
|
|
253
272
|
return from_hash(loaded_value, context, recurse: recurse) if keys.any?
|
@@ -263,10 +282,12 @@ module Attributor
|
|
263
282
|
value
|
264
283
|
elsif value.is_a?(::String)
|
265
284
|
decode_json(value, context)
|
266
|
-
elsif value.respond_to?(:
|
285
|
+
elsif value.respond_to?(:to_h)
|
286
|
+
value.to_h
|
287
|
+
elsif value.respond_to?(:to_hash) # Deprecate this in lieu of to_h only?
|
267
288
|
value.to_hash
|
268
289
|
else
|
269
|
-
raise Attributor::IncompatibleTypeError
|
290
|
+
raise Attributor::IncompatibleTypeError.new(context: context, value_type: value.class, type: self)
|
270
291
|
end
|
271
292
|
end
|
272
293
|
|
@@ -282,6 +303,10 @@ module Attributor
|
|
282
303
|
context + ["key(#{key_name.inspect})"]
|
283
304
|
end
|
284
305
|
|
306
|
+
def to_h
|
307
|
+
Attributor.recursive_to_h(@contents)
|
308
|
+
end
|
309
|
+
|
285
310
|
def generate_subcontext(context, key_name)
|
286
311
|
self.class.generate_subcontext(context, key_name)
|
287
312
|
end
|
@@ -412,11 +437,11 @@ module Attributor
|
|
412
437
|
|
413
438
|
if keys.any?
|
414
439
|
# Spit keys if it's the root or if it's an anonymous structures
|
415
|
-
if !shallow || name
|
416
|
-
|
440
|
+
if ( !shallow || self.name == nil)
|
441
|
+
required_names_from_attr = []
|
417
442
|
# FIXME: change to :keys when the praxis doc browser supports displaying those
|
418
|
-
hash[:attributes] = keys.each_with_object({}) do |(sub_name, sub_attribute), sub_attributes|
|
419
|
-
|
443
|
+
hash[:attributes] = self.keys.each_with_object({}) do |(sub_name, sub_attribute), sub_attributes|
|
444
|
+
required_names_from_attr << sub_name if sub_attribute.options[:required] == true
|
420
445
|
sub_example = example.get(sub_name) if example
|
421
446
|
sub_attributes[sub_name] = sub_attribute.describe(true, example: sub_example)
|
422
447
|
end
|
@@ -424,23 +449,79 @@ module Attributor
|
|
424
449
|
described_req = req.describe(shallow)
|
425
450
|
if described_req[:type] == :all
|
426
451
|
# Add the names of the attributes that have the required flag too
|
427
|
-
described_req[:attributes] |=
|
428
|
-
|
452
|
+
described_req[:attributes] |= required_names_from_attr
|
453
|
+
required_names_from_attr = []
|
429
454
|
end
|
430
455
|
list << described_req
|
431
456
|
end
|
432
457
|
# Make sure we create an :all requirement, if there wasn't one so we can add the required: true attributes
|
433
|
-
unless
|
434
|
-
hash[:requirements] << {
|
458
|
+
unless required_names_from_attr.empty?
|
459
|
+
hash[:requirements] << {type: :all, attributes: required_names_from_attr }
|
435
460
|
end
|
436
461
|
end
|
437
462
|
else
|
438
463
|
hash[:value] = { type: value_type.describe(true) }
|
464
|
+
hash[:example] = example if example
|
465
|
+
end
|
466
|
+
|
467
|
+
hash
|
468
|
+
end
|
469
|
+
|
470
|
+
def self.as_json_schema( shallow: false, example: nil, attribute_options: {} )
|
471
|
+
hash = super
|
472
|
+
opts = self.options.merge( attribute_options )
|
473
|
+
|
474
|
+
if key_type
|
475
|
+
hash[:'x-key_type'] = key_type.as_json_schema
|
476
|
+
end
|
477
|
+
|
478
|
+
if self.keys.any?
|
479
|
+
# Spit keys if it's the root or if it's an anonymous structures
|
480
|
+
if ( !shallow || self.name == nil)
|
481
|
+
required_names_from_attr = []
|
482
|
+
# FIXME: change to :keys when the praxis doc browser supports displaying those
|
483
|
+
hash[:properties] = self.keys.each_with_object({}) do |(sub_name, sub_attribute), sub_attributes|
|
484
|
+
required_names_from_attr << sub_name if sub_attribute.options[:required] == true
|
485
|
+
sub_example = example.get(sub_name) if example
|
486
|
+
sub_attributes[sub_name] = sub_attribute.as_json_schema(shallow: true, example: sub_example)
|
487
|
+
end
|
488
|
+
|
489
|
+
# Expose the more complex requirements to in the x-tended attribute
|
490
|
+
extended_requirements = self.requirements.each_with_object([]) do |req, list|
|
491
|
+
described_req = req.describe(shallow)
|
492
|
+
if described_req[:type] == :all
|
493
|
+
# Add the names of the attributes that have the required flag too
|
494
|
+
described_req[:attributes] |= required_names_from_attr
|
495
|
+
required_names_from_attr = []
|
496
|
+
end
|
497
|
+
list << described_req
|
498
|
+
end
|
499
|
+
all = extended_requirements.find{|r| r[:type] == :all }
|
500
|
+
if ( all && !all[:attributes].empty? )
|
501
|
+
hash[:required] = all[:attributes]
|
502
|
+
end
|
503
|
+
hash[:'x-requirements'] = extended_requirements unless extended_requirements.empty?
|
504
|
+
end
|
505
|
+
else
|
506
|
+
hash[:'x-value_type'] = value_type.as_json_schema(shallow:true)
|
439
507
|
end
|
440
508
|
|
509
|
+
if opts[:allow_extra]
|
510
|
+
hash[:additionalProperties] = if value_type == Attributor::Object
|
511
|
+
true
|
512
|
+
else
|
513
|
+
value_type.as_json_schema(shallow: true)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
# TODO: minProperties and maxProperties and patternProperties
|
517
|
+
# TODO: map our required_if (and possible our above requirements 'at_least...' to json schema dependencies)
|
441
518
|
hash
|
442
519
|
end
|
443
520
|
|
521
|
+
def self.json_schema_type
|
522
|
+
:object
|
523
|
+
end
|
524
|
+
|
444
525
|
# TODO: Think about the format of the subcontexts to use: let's use .at(key.to_s)
|
445
526
|
attr_reader :contents
|
446
527
|
|
@@ -528,6 +609,7 @@ module Attributor
|
|
528
609
|
end
|
529
610
|
|
530
611
|
def validate(context = Attributor::DEFAULT_ROOT_CONTEXT)
|
612
|
+
@validating = true
|
531
613
|
context = [context] if context.is_a? ::String
|
532
614
|
|
533
615
|
if self.class.keys.any?
|
@@ -535,6 +617,8 @@ module Attributor
|
|
535
617
|
else
|
536
618
|
self.validate_generic(context)
|
537
619
|
end
|
620
|
+
ensure
|
621
|
+
@validating = false
|
538
622
|
end
|
539
623
|
|
540
624
|
def validate_keys(context)
|
@@ -587,12 +671,12 @@ module Attributor
|
|
587
671
|
@dumping = true
|
588
672
|
|
589
673
|
contents.each_with_object({}) do |(k, v), hash|
|
590
|
-
k = key_attribute.dump(k, opts)
|
674
|
+
k = key_attribute.dump(k, **opts)
|
591
675
|
|
592
676
|
v = if (attribute_for_value = self.class.keys[k])
|
593
|
-
attribute_for_value.dump(v, opts)
|
677
|
+
attribute_for_value.dump(v, **opts)
|
594
678
|
else
|
595
|
-
value_attribute.dump(v, opts)
|
679
|
+
value_attribute.dump(v, **opts)
|
596
680
|
end
|
597
681
|
|
598
682
|
hash[k] = v
|