hoodoo 1.12.4 → 1.13.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/lib/hoodoo/presenters/base_dsl.rb +284 -70
- data/lib/hoodoo/presenters/types/array.rb +74 -7
- data/lib/hoodoo/presenters/types/field.rb +1 -1
- data/lib/hoodoo/presenters/types/hash.rb +31 -16
- data/lib/hoodoo/utilities/utilities.rb +2 -0
- data/lib/hoodoo/version.rb +1 -1
- data/spec/presenters/base_dsl_spec.rb +29 -0
- data/spec/presenters/types/array_spec.rb +246 -9
- data/spec/presenters/types/hash_spec.rb +590 -0
- data/spec/utilities/utilities_spec.rb +2 -0
- metadata +3 -3
@@ -11,6 +11,52 @@ module Hoodoo
|
|
11
11
|
#
|
12
12
|
attr_accessor :properties
|
13
13
|
|
14
|
+
# Initialize an Array instance with the appropriate name and options.
|
15
|
+
#
|
16
|
+
# +name+:: The JSON key.
|
17
|
+
# +options+:: A +Hash+ of options, e.g. <tt>:required => true,
|
18
|
+
# :type => :enum, :field_from => [ 1, 2, 3, 4 ]</tt>. If
|
19
|
+
# a +:type+ field is present, the Array contains atomic
|
20
|
+
# types of the given kind. Otherwise, either pass a
|
21
|
+
# block with inner schema DSL calls describing complex
|
22
|
+
# array entry schema, or nothing for no array content
|
23
|
+
# validation. If a block _and_ +:type+ option are
|
24
|
+
# passed, the block is used and option ignored.
|
25
|
+
#
|
26
|
+
def initialize( name, options = {} )
|
27
|
+
super( name, options )
|
28
|
+
|
29
|
+
if options.has_key?( :type )
|
30
|
+
|
31
|
+
# Defining a property via "#property" adds it to the @properties
|
32
|
+
# array, but handling of simple Types in array validation and
|
33
|
+
# rendering is too different from complex types to use the same
|
34
|
+
# code flow; we need the property to be independently used, so
|
35
|
+
# extract it into its own instance variable and delete the item
|
36
|
+
# from @properties.
|
37
|
+
#
|
38
|
+
value_klass = type_option_to_class( options[ :type ] )
|
39
|
+
random_name = Hoodoo::UUID.generate()
|
40
|
+
@value_property = property( random_name,
|
41
|
+
value_klass,
|
42
|
+
extract_field_prefix_options_from( options ) )
|
43
|
+
|
44
|
+
@properties.delete( random_name )
|
45
|
+
|
46
|
+
# This is approaching a blunt hack. Without it, validation errors
|
47
|
+
# will result in e.g. "fields[1].cd2f0a15ec8e4bd6ab1964b25b044e69"
|
48
|
+
# in error messages. By using nil, the validation code's JSON path
|
49
|
+
# array to string code doesn't include the item, giving the
|
50
|
+
# desired result. In addition, the base class Field#render code
|
51
|
+
# has an important check for non-nil but empty and bails out, but
|
52
|
+
# allows the nil name case to render simple types as expected. A
|
53
|
+
# delicate / fragile balance of nil-vs-empty arises.
|
54
|
+
#
|
55
|
+
@value_property.name = nil
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
14
60
|
# Check if data is a valid Array and return a Hoodoo::Errors instance.
|
15
61
|
#
|
16
62
|
def validate( data, path = '' )
|
@@ -18,8 +64,12 @@ module Hoodoo
|
|
18
64
|
return errors if errors.has_errors? || ( ! @required && data.nil? )
|
19
65
|
|
20
66
|
if data.is_a?( ::Array )
|
21
|
-
|
22
|
-
|
67
|
+
|
68
|
+
# A block which defined properties for this instance takes
|
69
|
+
# precedence; then check for a ":type" option via "@@value_property"
|
70
|
+
# stored in the constructor; then give up and do no validation.
|
71
|
+
#
|
72
|
+
if @properties.nil? == false && @properties.empty? == false
|
23
73
|
data.each_with_index do | item, index |
|
24
74
|
@properties.each do | name, property |
|
25
75
|
rdata = ( item.is_a?( ::Hash ) && item.has_key?( name ) ) ? item[ name ] : nil
|
@@ -27,7 +77,13 @@ module Hoodoo
|
|
27
77
|
errors.merge!( property.validate( rdata, indexed_path ) )
|
28
78
|
end
|
29
79
|
end
|
80
|
+
elsif @value_property.nil? == false
|
81
|
+
data.each_with_index do | item, index |
|
82
|
+
indexed_path = "#{ full_path( path ) }[#{ index }]"
|
83
|
+
errors.merge!( @value_property.validate( item, indexed_path ) )
|
84
|
+
end
|
30
85
|
end
|
86
|
+
|
31
87
|
else
|
32
88
|
errors.add_error(
|
33
89
|
'generic.invalid_array',
|
@@ -66,11 +122,7 @@ module Hoodoo
|
|
66
122
|
|
67
123
|
# ...then look at rendering the input entries of 'data' into 'array'.
|
68
124
|
|
69
|
-
if @properties.nil?
|
70
|
-
# Must modify existing instance of 'array', so use 'push()'
|
71
|
-
array.push( *data )
|
72
|
-
|
73
|
-
else
|
125
|
+
if @properties.nil? == false && @properties.empty? == false
|
74
126
|
data.each do | item |
|
75
127
|
|
76
128
|
# We have properties defined so array values (in "item") must be
|
@@ -98,6 +150,21 @@ module Hoodoo
|
|
98
150
|
# Must modify existing instance of 'array', so use 'push()'
|
99
151
|
array.push( rendered )
|
100
152
|
end
|
153
|
+
|
154
|
+
elsif @value_property.nil? == false
|
155
|
+
data.each do | item |
|
156
|
+
subtarget = {}
|
157
|
+
@value_property.render( item, subtarget )
|
158
|
+
rendered = subtarget.empty? ? nil : read_at_path( subtarget, path ).values.first
|
159
|
+
|
160
|
+
# Must modify existing instance of 'array', so use 'push()'
|
161
|
+
array.push( rendered )
|
162
|
+
end
|
163
|
+
|
164
|
+
else
|
165
|
+
# Must modify existing instance of 'array', so use 'push()'
|
166
|
+
array.push( *data )
|
167
|
+
|
101
168
|
end
|
102
169
|
end
|
103
170
|
|
@@ -57,23 +57,30 @@ module Hoodoo
|
|
57
57
|
raise "Can't use \#keys and then \#key in the same hash definition - use one or the other"
|
58
58
|
end
|
59
59
|
|
60
|
+
@specific = true
|
61
|
+
value_klass = block_given? ?
|
62
|
+
Hoodoo::Presenters::Object :
|
63
|
+
type_option_to_class( options.delete( :type ) )
|
64
|
+
|
60
65
|
# If we're defining specific keys and some of those keys have fields
|
61
|
-
# with defaults, we need to merge those up to provide a whole-
|
66
|
+
# with defaults, we need to merge those up to provide a whole-Hash
|
62
67
|
# equivalent default. If someone renders an empty hash they expect a
|
63
68
|
# specific key with some internal defaults to be rendered; doing this
|
64
69
|
# amalgamation up to key level is the easiest way to handle that.
|
65
|
-
|
66
|
-
if options.has_key?( :default )
|
67
|
-
@has_default
|
68
|
-
|
69
|
-
@default
|
70
|
-
@default.merge!( Hoodoo::Utilities.stringify( options[ :default ] ) )
|
70
|
+
#
|
71
|
+
if options.has_key?( :default ) || options.has_key?( :field_default )
|
72
|
+
@has_default = true
|
73
|
+
@default ||= {}
|
74
|
+
@default[ name ] = options[ :default ] || options[ :field_default ]
|
71
75
|
end
|
72
76
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
+
prop = property( name,
|
78
|
+
value_klass,
|
79
|
+
Hoodoo::Utilities.deep_merge_into(
|
80
|
+
options,
|
81
|
+
extract_field_prefix_options_from( options )
|
82
|
+
),
|
83
|
+
&block )
|
77
84
|
|
78
85
|
if prop && prop.respond_to?( :is_internationalised? ) && prop.is_internationalised?
|
79
86
|
internationalised()
|
@@ -148,12 +155,20 @@ module Hoodoo
|
|
148
155
|
end
|
149
156
|
|
150
157
|
@specific = false
|
158
|
+
key_klass = options.has_key?( :length ) ?
|
159
|
+
Hoodoo::Presenters::String :
|
160
|
+
Hoodoo::Presenters::Text
|
161
|
+
|
162
|
+
property( 'keys', key_klass, options )
|
151
163
|
|
152
|
-
|
153
|
-
|
164
|
+
value_klass = block_given? ?
|
165
|
+
Hoodoo::Presenters::Object :
|
166
|
+
type_option_to_class( options[ :type ] )
|
154
167
|
|
155
|
-
|
156
|
-
|
168
|
+
prop = property( 'values',
|
169
|
+
value_klass,
|
170
|
+
extract_field_prefix_options_from( options ),
|
171
|
+
&block )
|
157
172
|
|
158
173
|
if prop && prop.respond_to?( :is_internationalised? ) && prop.is_internationalised?
|
159
174
|
internationalised()
|
@@ -237,7 +252,7 @@ module Hoodoo
|
|
237
252
|
keys_property.rename( key )
|
238
253
|
values_property.rename( key )
|
239
254
|
|
240
|
-
errors.merge!( keys_property.validate(
|
255
|
+
errors.merge!( keys_property.validate( key, local_path ) )
|
241
256
|
errors.merge!( values_property.validate( value, local_path ) )
|
242
257
|
end
|
243
258
|
|
@@ -300,6 +300,7 @@ module Hoodoo
|
|
300
300
|
# 8601 subset date and time, else +false+.
|
301
301
|
#
|
302
302
|
def self.valid_iso8601_subset_datetime?( str )
|
303
|
+
return false unless str.is_a?( String ) || str.is_a?( Symbol )
|
303
304
|
|
304
305
|
# Relies on Ruby evaluation behaviour and operator precedence - "'foo'
|
305
306
|
# && true" => true, but "true && 'foo'" => 'foo'. Don't use "and" here!
|
@@ -324,6 +325,7 @@ module Hoodoo
|
|
324
325
|
# subset date, else +false+.
|
325
326
|
#
|
326
327
|
def self.valid_iso8601_subset_date?( str )
|
328
|
+
return false unless str.is_a?( String ) || str.is_a?( Symbol )
|
327
329
|
|
328
330
|
# Same reliance as 'valid_iso8601_subset_datetime'?.
|
329
331
|
|
data/lib/hoodoo/version.rb
CHANGED
@@ -108,4 +108,33 @@ describe Hoodoo::Presenters::BaseDSL do
|
|
108
108
|
}.not_to raise_error
|
109
109
|
end
|
110
110
|
end
|
111
|
+
|
112
|
+
describe '#type_option_to_class' do
|
113
|
+
it 'should raise an error for unrecognised types' do
|
114
|
+
expect {
|
115
|
+
klass = Hoodoo::Presenters::Object.new
|
116
|
+
klass.send( :type_option_to_class, :foo )
|
117
|
+
}.to raise_error(RuntimeError, "Unsupported 'type' option value of 'foo' in Hoodoo::Presenters::BaseDSL")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#extract_field_prefix_options_from' do
|
122
|
+
it 'should extract options' do
|
123
|
+
data = {
|
124
|
+
:default => :foo,
|
125
|
+
:field_one => :one,
|
126
|
+
'field_two' => :two,
|
127
|
+
'field_three-three three' => { :three => 3 }
|
128
|
+
}
|
129
|
+
|
130
|
+
klass = Hoodoo::Presenters::Object.new
|
131
|
+
result = klass.send( :extract_field_prefix_options_from, data )
|
132
|
+
|
133
|
+
expect( result ).to( eql( {
|
134
|
+
:one => :one,
|
135
|
+
:two => :two,
|
136
|
+
:'three-three three' => { :three => 3 }
|
137
|
+
} ) )
|
138
|
+
end
|
139
|
+
end
|
111
140
|
end
|
@@ -29,6 +29,8 @@ describe Hoodoo::Presenters::Array do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
############################################################################
|
33
|
+
|
32
34
|
describe '#validate' do
|
33
35
|
it 'should return [] when valid array' do
|
34
36
|
expect(@inst.validate([]).errors).to eq([])
|
@@ -37,12 +39,12 @@ describe Hoodoo::Presenters::Array do
|
|
37
39
|
it 'should return correct error when data is not a array' do
|
38
40
|
errors = @inst.validate('asckn')
|
39
41
|
|
40
|
-
err = [ {'code'=>
|
42
|
+
err = [ {'code'=>'generic.invalid_array', 'message'=>'Field `one` is an invalid array', 'reference'=>'one'}]
|
41
43
|
expect(errors.errors).to eq(err)
|
42
44
|
end
|
43
45
|
|
44
46
|
it 'should return correct error with non array types' do
|
45
|
-
err = [ {'code'=>
|
47
|
+
err = [ {'code'=>'generic.invalid_array', 'message'=>'Field `one` is an invalid array', 'reference'=>'one'}]
|
46
48
|
|
47
49
|
expect(@inst.validate('asckn').errors).to eq(err)
|
48
50
|
expect(@inst.validate(34534).errors).to eq(err)
|
@@ -58,14 +60,14 @@ describe Hoodoo::Presenters::Array do
|
|
58
60
|
it 'should return error when required and absent' do
|
59
61
|
@inst.required = true
|
60
62
|
expect(@inst.validate(nil).errors).to eq([
|
61
|
-
{'code'=>
|
63
|
+
{'code'=>'generic.required_field_missing', 'message'=>'Field `one` is required', 'reference'=>'one'}
|
62
64
|
])
|
63
65
|
end
|
64
66
|
|
65
67
|
it 'should return correct error with path' do
|
66
68
|
errors = @inst.validate('scdacs','ordinary')
|
67
69
|
expect(errors.errors).to eq([
|
68
|
-
{'code'=>
|
70
|
+
{'code'=>'generic.invalid_array', 'message'=>'Field `ordinary.one` is an invalid array', 'reference'=>'ordinary.one'}
|
69
71
|
])
|
70
72
|
end
|
71
73
|
|
@@ -75,7 +77,7 @@ describe Hoodoo::Presenters::Array do
|
|
75
77
|
|
76
78
|
errors = TestPresenterArray.validate(data)
|
77
79
|
expect(errors.errors).to eq([
|
78
|
-
{'code'=>
|
80
|
+
{'code'=>'generic.required_field_missing', 'message'=>'Field `an_array` is required', 'reference'=>'an_array'},
|
79
81
|
])
|
80
82
|
|
81
83
|
end
|
@@ -104,8 +106,8 @@ describe Hoodoo::Presenters::Array do
|
|
104
106
|
|
105
107
|
errors = TestPresenterArray.validate(data)
|
106
108
|
expect(errors.errors).to eq([
|
107
|
-
{'code'=>
|
108
|
-
{'code'=>
|
109
|
+
{'code'=>'generic.invalid_integer', 'message'=>'Field `an_array[1].an_integer` is an invalid integer', 'reference'=>'an_array[1].an_integer'},
|
110
|
+
{'code'=>'generic.invalid_datetime', 'message'=>'Field `an_array[2].a_datetime` is an invalid ISO8601 datetime', 'reference'=>'an_array[2].a_datetime'},
|
109
111
|
])
|
110
112
|
end
|
111
113
|
|
@@ -125,6 +127,8 @@ describe Hoodoo::Presenters::Array do
|
|
125
127
|
end
|
126
128
|
end
|
127
129
|
|
130
|
+
############################################################################
|
131
|
+
|
128
132
|
describe '#render' do
|
129
133
|
it 'renders correctly with whole-array default (1)' do
|
130
134
|
data = nil
|
@@ -186,7 +190,7 @@ describe Hoodoo::Presenters::Array do
|
|
186
190
|
}
|
187
191
|
],
|
188
192
|
'a_default_array' => [ { 'an_integer' => 42 }, { 'some_array_text' => 'hello' } ],
|
189
|
-
'an_enum' =>
|
193
|
+
'an_enum' => 'one'
|
190
194
|
})
|
191
195
|
end
|
192
196
|
|
@@ -218,7 +222,7 @@ describe Hoodoo::Presenters::Array do
|
|
218
222
|
{ 'an_integer' => 23 },
|
219
223
|
{ 'an_integer' => 42, 'a_datetime' => time },
|
220
224
|
],
|
221
|
-
'an_enum' =>
|
225
|
+
'an_enum' => 'one'
|
222
226
|
})
|
223
227
|
end
|
224
228
|
|
@@ -245,5 +249,238 @@ describe Hoodoo::Presenters::Array do
|
|
245
249
|
})
|
246
250
|
end
|
247
251
|
end
|
252
|
+
class TestPresenterTypedArray < Hoodoo::Presenters::Base
|
253
|
+
schema do
|
254
|
+
array :array, :type => :array
|
255
|
+
array :boolean, :type => :boolean
|
256
|
+
array :date, :type => :date
|
257
|
+
array :date_time, :type => :date_time
|
258
|
+
array :decimal, :type => :decimal, :field_precision => 2
|
259
|
+
array :enum, :type => :enum, :field_from => [ :one, :two, :three ]
|
260
|
+
array :float, :type => :float
|
261
|
+
array :integer, :type => :integer
|
262
|
+
array :string, :type => :string, :field_length => 4
|
263
|
+
array :tags, :type => :tags
|
264
|
+
array :text, :type => :text
|
265
|
+
array :uuid, :type => :uuid
|
266
|
+
array :field
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
############################################################################
|
271
|
+
|
272
|
+
ARRAY_DATA = {
|
273
|
+
'array' => { :valid => [ [ 2, 3, 4 ] ], :invalid => [ 4, { :one => 1 } ] },
|
274
|
+
'boolean' => { :valid => [ true ], :invalid => [ 4.51, 'false' ] },
|
275
|
+
'date' => { :valid => [ Date.today.iso8601 ], :invalid => [ Date.today, '23rd January 2041' ] },
|
276
|
+
'date_time' => { :valid => [ DateTime.now.iso8601 ], :invalid => [ DateTime.now, '2017-01-27 12:00' ] },
|
277
|
+
'decimal' => { :valid => [ BigDecimal.new(4.51, 2) ], :invalid => [ 4.51, '4.51' ] },
|
278
|
+
'enum' => { :valid => [ 'one' ], :invalid => [ 'One', 1 ] },
|
279
|
+
'float' => { :valid => [ 4.51 ], :invalid => [ BigDecimal.new(4.51, 2), '4.51' ] },
|
280
|
+
'integer' => { :valid => [ 4 ], :invalid => [ '4' ] },
|
281
|
+
'string' => { :valid => [ 'four' ], :invalid => [ 'toolong', 4, true ] },
|
282
|
+
'tags' => { :valid => [ 'tag_a,tag_b,tag_c' ], :invalid => [ 4, true ] },
|
283
|
+
'text' => { :valid => [ 'hello world' ], :invalid => [ 4, true ] },
|
284
|
+
'uuid' => { :valid => [ Hoodoo::UUID.generate() ], :invalid => [ '123456', 4, true ] },
|
285
|
+
'field' => { :valid => [ 4, '4', { :one => 1 } ], :invalid => [ ] }
|
286
|
+
}
|
287
|
+
|
288
|
+
ARRAY_DATA.each do | field, values |
|
289
|
+
context '#render' do
|
290
|
+
values[ :valid ].each_with_index do | value, index |
|
291
|
+
it "renders correctly for '#{ field }' (#{ index + 1 })" do
|
292
|
+
data = { field => [ value, value, value ] }
|
293
|
+
expect( TestPresenterTypedArray.render( data ) ).to eq( data )
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
context '#validate' do
|
299
|
+
values[ :valid ].each_with_index do | value, index |
|
300
|
+
it "accepts a valid value for '#{ field }' (#{ index + 1 })" do
|
301
|
+
data = { field => [ value, value, value ] }
|
302
|
+
expect( TestPresenterTypedArray.validate( data ).errors.size ).to eql( 0 )
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
values[ :invalid ].each_with_index do | value, index |
|
307
|
+
it "rejects an invalid value for '#{ field }' (#{ index + 1 })" do
|
308
|
+
data = { field => [ value, value, value ] }
|
309
|
+
expect( TestPresenterTypedArray.validate( data ).errors.size ).to eql( 3 )
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
############################################################################
|
316
|
+
|
317
|
+
context 'RDoc examples' do
|
318
|
+
context 'VeryWealthy' do
|
319
|
+
class TestHypotheticalCurrency < Hoodoo::Presenters::Base
|
320
|
+
schema do
|
321
|
+
string :currency_code, :length => 16
|
322
|
+
integer :precision
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
class TestVeryWealthy < Hoodoo::Presenters::Base
|
327
|
+
schema do
|
328
|
+
array :currencies, :required => true do
|
329
|
+
type TestHypotheticalCurrency
|
330
|
+
string :notes, :required => true, :length => 32
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
let( :valid_data ) do
|
336
|
+
{
|
337
|
+
'currencies' => [
|
338
|
+
{
|
339
|
+
'currency_code' => 'X_HOODOO',
|
340
|
+
'precision' => 2,
|
341
|
+
'notes' => 'A short note'
|
342
|
+
}
|
343
|
+
]
|
344
|
+
}
|
345
|
+
end
|
346
|
+
|
347
|
+
context '#validate' do
|
348
|
+
it 'enforces a required array' do
|
349
|
+
data = {}
|
350
|
+
|
351
|
+
errors = TestVeryWealthy.validate( data ).errors
|
352
|
+
|
353
|
+
expect( errors.size ).to( eql( 1 ) )
|
354
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.required_field_missing' ) )
|
355
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'currencies' ) )
|
356
|
+
end
|
357
|
+
|
358
|
+
it 'enforces a required array entry field' do
|
359
|
+
data = {
|
360
|
+
'currencies' => [ {} ]
|
361
|
+
}
|
362
|
+
|
363
|
+
errors = TestVeryWealthy.validate( data ).errors
|
364
|
+
|
365
|
+
expect( errors.size ).to( eql( 1 ) )
|
366
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.required_field_missing' ) )
|
367
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'currencies[0].notes' ) )
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'enforces an array entry field length' do
|
371
|
+
data = {
|
372
|
+
'currencies' => [
|
373
|
+
{
|
374
|
+
'notes' => 'This note is too long for the 32-character limit'
|
375
|
+
}
|
376
|
+
]
|
377
|
+
}
|
378
|
+
|
379
|
+
errors = TestVeryWealthy.validate( data ).errors
|
380
|
+
|
381
|
+
expect( errors.size ).to( eql( 1 ) )
|
382
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.invalid_string' ) )
|
383
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'currencies[0].notes' ) )
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'is happy with valid data' do
|
387
|
+
expect( TestVeryWealthy.validate( valid_data() ).errors.size ).to( eql( 0 ) )
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
context '#render' do
|
392
|
+
it 'renders valid data' do
|
393
|
+
expect( TestVeryWealthy.render( valid_data() ) ).to( eql( valid_data() ) )
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
context 'UUIDCollection' do
|
399
|
+
class TestUUIDCollection < Hoodoo::Presenters::Base
|
400
|
+
schema do
|
401
|
+
array :uuids, :type => :uuid
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
let( :valid_data ) do
|
406
|
+
{
|
407
|
+
'uuids' => [
|
408
|
+
Hoodoo::UUID.generate(),
|
409
|
+
Hoodoo::UUID.generate(),
|
410
|
+
Hoodoo::UUID.generate()
|
411
|
+
]
|
412
|
+
}
|
413
|
+
end
|
414
|
+
|
415
|
+
context '#validate' do
|
416
|
+
it 'validates entries' do
|
417
|
+
data = {
|
418
|
+
'uuids' => [
|
419
|
+
Hoodoo::UUID.generate(),
|
420
|
+
'not a UUID'
|
421
|
+
]
|
422
|
+
}
|
423
|
+
|
424
|
+
errors = TestUUIDCollection.validate( data ).errors
|
425
|
+
|
426
|
+
expect( errors.size ).to( eql( 1 ) )
|
427
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.invalid_uuid' ) )
|
428
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'uuids[1]' ) )
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
context '#render' do
|
433
|
+
it 'renders valid data' do
|
434
|
+
expect( TestUUIDCollection.render( valid_data() ) ).to( eql( valid_data() ) )
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
context 'DecimalCollection' do
|
440
|
+
class TestDecimalCollection < Hoodoo::Presenters::Base
|
441
|
+
schema do
|
442
|
+
array :numbers, :type => :decimal, :field_precision => 2
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
let( :valid_data ) do
|
447
|
+
{
|
448
|
+
'numbers' => [
|
449
|
+
BigDecimal.new( '42.55111' ), # Precision is FYI data generators, not the renderer :-/
|
450
|
+
BigDecimal.new( '42.4' ),
|
451
|
+
BigDecimal.new( '42' )
|
452
|
+
]
|
453
|
+
}
|
454
|
+
end
|
455
|
+
|
456
|
+
context '#validate' do
|
457
|
+
it 'validates entries' do
|
458
|
+
data = {
|
459
|
+
'numbers' => [
|
460
|
+
BigDecimal.new( '42.21' ),
|
461
|
+
'not a decimal'
|
462
|
+
]
|
463
|
+
}
|
464
|
+
|
465
|
+
errors = TestDecimalCollection.validate( data ).errors
|
466
|
+
|
467
|
+
expect( errors.size ).to( eql( 1 ) )
|
468
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.invalid_decimal' ) )
|
469
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'numbers[1]' ) )
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
context '#render' do
|
474
|
+
it 'renders valid data' do
|
475
|
+
|
476
|
+
# Precision is FYI data generators, not the renderer so high
|
477
|
+
# precision BigDecimals are returned as-is in rendering :-/
|
478
|
+
#
|
479
|
+
expect( TestDecimalCollection.render( valid_data() ) ).to( eql( valid_data() ) )
|
480
|
+
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
248
485
|
|
249
486
|
end
|