hoodoo 1.12.4 → 1.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|