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.
@@ -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
- # No array entry schema? No array entry validation, then.
22
- unless @properties.nil?
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
 
@@ -71,7 +71,7 @@ module Hoodoo
71
71
  # Returns the full path array that was used (a clone of +@path+).
72
72
  #
73
73
  def render( data, target )
74
- return if @name.empty?
74
+ return if @name.nil? == false && @name.empty?
75
75
 
76
76
  root = target
77
77
  path = @path.clone
@@ -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-key
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 = true
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
- @specific = true
74
-
75
- klass = block_given? ? Hoodoo::Presenters::Object : Hoodoo::Presenters::Field
76
- prop = property( name, klass, options, &block )
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
- klass = options.has_key?( :length ) ? Hoodoo::Presenters::String : Hoodoo::Presenters::Text
153
- property('keys', klass, options)
164
+ value_klass = block_given? ?
165
+ Hoodoo::Presenters::Object :
166
+ type_option_to_class( options[ :type ] )
154
167
 
155
- klass = block_given? ? Hoodoo::Presenters::Object : Hoodoo::Presenters::Field
156
- prop = property( 'values', klass, {}, &block )
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( key, local_path ) )
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
 
@@ -12,6 +12,6 @@ module Hoodoo
12
12
  # The Hoodoo gem version. If this changes, ensure that the date in
13
13
  # "hoodoo.gemspec" is correct and run "bundle install" (or "update").
14
14
  #
15
- VERSION = '1.12.4'
15
+ VERSION = '1.13.0'
16
16
 
17
17
  end
@@ -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'=>"generic.invalid_array", 'message'=>"Field `one` is an invalid array", 'reference'=>"one"}]
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'=>"generic.invalid_array", 'message'=>"Field `one` is an invalid array", 'reference'=>"one"}]
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'=>"generic.required_field_missing", 'message'=>"Field `one` is required", 'reference'=>"one"}
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'=>"generic.invalid_array", 'message'=>"Field `ordinary.one` is an invalid array", 'reference'=>"ordinary.one"}
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'=>"generic.required_field_missing", 'message'=>"Field `an_array` is required", 'reference'=>"an_array"},
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'=>"generic.invalid_integer", 'message'=>"Field `an_array[1].an_integer` is an invalid integer", 'reference'=>"an_array[1].an_integer"},
108
- {'code'=>"generic.invalid_datetime", 'message'=>"Field `an_array[2].a_datetime` is an invalid ISO8601 datetime", 'reference'=>"an_array[2].a_datetime"},
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' => "one"
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' => "one"
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