compel 0.1.3 → 0.2.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -0
  3. data/README.md +65 -54
  4. data/lib/compel/builder/boolean.rb +13 -0
  5. data/lib/compel/builder/common.rb +24 -0
  6. data/lib/compel/builder/common_value.rb +34 -0
  7. data/lib/compel/builder/date.rb +23 -0
  8. data/lib/compel/builder/datetime.rb +23 -0
  9. data/lib/compel/builder/float.rb +15 -0
  10. data/lib/compel/builder/hash.rb +22 -0
  11. data/lib/compel/builder/integer.rb +15 -0
  12. data/lib/compel/builder/json.rb +13 -0
  13. data/lib/compel/builder/methods.rb +60 -0
  14. data/lib/compel/builder/schema.rb +27 -0
  15. data/lib/compel/builder/string.rb +30 -0
  16. data/lib/compel/builder/time.rb +23 -0
  17. data/lib/compel/coercion/boolean.rb +1 -1
  18. data/lib/compel/coercion/date.rb +19 -2
  19. data/lib/compel/coercion/datetime.rb +19 -2
  20. data/lib/compel/coercion/float.rb +1 -1
  21. data/lib/compel/coercion/hash.rb +1 -1
  22. data/lib/compel/coercion/integer.rb +1 -1
  23. data/lib/compel/coercion/json.rb +1 -1
  24. data/lib/compel/coercion/regexp.rb +17 -0
  25. data/lib/compel/coercion/string.rb +1 -1
  26. data/lib/compel/coercion/time.rb +19 -2
  27. data/lib/compel/coercion/type.rb +0 -13
  28. data/lib/compel/coercion.rb +8 -10
  29. data/lib/compel/contract.rb +18 -62
  30. data/lib/compel/exceptions/invalid_hash_error.rb +9 -0
  31. data/lib/compel/exceptions/type_error.rb +7 -0
  32. data/lib/compel/exceptions/validation_error.rb +7 -0
  33. data/lib/compel/validation.rb +36 -50
  34. data/lib/compel/validators/base.rb +19 -0
  35. data/lib/compel/validators/hash_validator.rb +51 -0
  36. data/lib/compel/validators/type_validator.rb +30 -0
  37. data/lib/compel/version.rb +1 -1
  38. data/lib/compel.rb +16 -11
  39. data/spec/compel/builder_spec.rb +226 -0
  40. data/spec/compel/coercion_spec.rb +85 -18
  41. data/spec/compel/compel_spec.rb +368 -160
  42. data/spec/compel/sinatra_integration_spec.rb +73 -0
  43. data/spec/compel/validation_spec.rb +122 -8
  44. data/spec/spec_helper.rb +19 -0
  45. data/spec/support/sinatra_app.rb +43 -0
  46. metadata +28 -10
  47. data/lib/compel/invalid_params_error.rb +0 -9
  48. data/lib/compel/param.rb +0 -46
  49. data/lib/compel/param_type_error.rb +0 -7
  50. data/lib/compel/param_validation_error.rb +0 -7
  51. data/spec/compel/contract_spec.rb +0 -36
  52. data/spec/compel/param_spec.rb +0 -25
@@ -1,181 +1,132 @@
1
1
  describe Compel do
2
2
 
3
- def make_the_call(method, params)
4
- Compel.send(method, params) do
5
- param :first_name, String, required: true
6
- param :last_name, String, required: true
7
- param :birth_date, DateTime
8
- end
9
- end
10
-
11
- context '#run!' do
12
-
13
- it 'should raise InvalidParamsError exception' do
14
- params = {
15
- first_name: 'Joaquim'
16
- }
17
-
18
- expect{ make_the_call(:run!, params) }.to \
19
- raise_error Compel::InvalidParamsError, 'params are invalid'
20
- end
21
-
22
- it 'should raise InvalidParamsError exception with errors' do
23
- params = {
24
- first_name: 'Joaquim'
25
- }
26
-
27
- expect{ make_the_call(:run!, params) }.to raise_error do |exception|
28
- expect(exception.params).to eq \
29
- Hashie::Mash.new(first_name: 'Joaquim')
30
-
31
- expect(exception.errors).to eq \
32
- Hashie::Mash.new(last_name: ['is required'])
33
- end
34
- end
35
-
36
- end
37
-
38
- context '#run?' do
39
-
40
- it 'should return true' do
41
- params = {
42
- first_name: 'Joaquim',
43
- last_name: 'Adráz',
44
- birth_date: '1989-08-06T09:00:00'
45
- }
46
-
47
- expect(make_the_call(:run?, params)).to eq(true)
48
- end
49
-
50
- it 'should return false' do
51
- params = {
52
- first_name: 'Joaquim'
53
- }
54
-
55
- expect(make_the_call(:run?, params)).to eq(false)
56
- end
57
-
58
- end
59
-
60
3
  context '#run' do
61
4
 
62
- def make_the_call(method, params)
63
- Compel.send(method, params) do
64
- param :user, Hash, required: true do
65
- param :first_name, String, required: true
66
- param :last_name, String, required: true
67
- param :birth_date, DateTime
68
- param :age, Integer
69
- param :admin, Compel::Boolean
70
- param :blog_role, Hash do
71
- param :admin, Compel::Boolean, required: true
72
- end
73
- end
74
- end
75
- end
5
+ context 'User validation example' do
6
+
7
+ def make_the_call(method, hash)
8
+ schema = Compel.hash.keys({
9
+ user: Compel.hash.keys({
10
+ first_name: Compel.string.required,
11
+ last_name: Compel.string.required,
12
+ birth_date: Compel.datetime,
13
+ age: Compel.integer,
14
+ admin: Compel.boolean,
15
+ blog_role: Compel.hash.keys({
16
+ admin: Compel.boolean.required
17
+ })
18
+ }).required
19
+ })
76
20
 
77
- it 'should compel returning coerced values' do
78
- params = {
79
- user: {
80
- first_name: 'Joaquim',
81
- last_name: 'Adráz',
82
- birth_date: '1989-08-06T09:00:00',
83
- age: '26',
84
- admin: 'f',
85
- blog_role: {
86
- admin: '0'
87
- }
88
- }
89
- }
21
+ Compel.send(method, hash, schema)
22
+ end
90
23
 
91
- expect(make_the_call(:run, params)).to eq \
92
- Hashie::Mash.new({
24
+ it 'should compel returning coerced values' do
25
+ hash = {
93
26
  user: {
94
27
  first_name: 'Joaquim',
95
28
  last_name: 'Adráz',
96
- birth_date: DateTime.parse('1989-08-06T09:00:00'),
97
- age: 26,
98
- admin: false,
29
+ birth_date: '1989-08-06T09:00:00',
30
+ age: '26',
31
+ admin: 'f',
99
32
  blog_role: {
100
- admin: false
33
+ admin: '0'
101
34
  }
102
35
  }
103
- })
104
- end
105
-
106
- it 'should not compel and leave other params untouched' do
107
- params = {
108
- other_param: 1,
109
- user: {
110
- first_name: 'Joaquim'
111
36
  }
112
- }
113
37
 
114
- expect(make_the_call(:run, params)).to eq \
115
- Hashie::Mash.new({
38
+ expect(make_the_call(:run, hash)).to eq \
39
+ Hashie::Mash.new({
40
+ user: {
41
+ first_name: 'Joaquim',
42
+ last_name: 'Adráz',
43
+ birth_date: DateTime.parse('1989-08-06T09:00:00'),
44
+ age: 26,
45
+ admin: false,
46
+ blog_role: {
47
+ admin: false
48
+ }
49
+ }
50
+ })
51
+ end
52
+
53
+ it 'should not compel and leave other hash untouched' do
54
+ hash = {
116
55
  other_param: 1,
117
56
  user: {
118
- first_name: 'Joaquim',
119
- },
120
- errors: {
57
+ first_name: 'Joaquim'
58
+ }
59
+ }
60
+
61
+ expect(make_the_call(:run, hash)).to eq \
62
+ Hashie::Mash.new({
63
+ other_param: 1,
121
64
  user: {
122
- last_name: ['is required']
65
+ first_name: 'Joaquim',
66
+ },
67
+ errors: {
68
+ user: {
69
+ last_name: ['is required']
70
+ }
123
71
  }
124
- }
125
- })
126
- end
72
+ })
73
+ end
127
74
 
128
- it 'should not compel for invalid params' do
129
- expect{ make_the_call(:run, 1) }.to \
130
- raise_error Compel::ParamTypeError, 'params must be an Hash'
131
- end
75
+ it 'should not compel for invalid hash' do
76
+ expect{ make_the_call(:run, 1) }.to \
77
+ raise_error Compel::TypeError, 'must be an Hash'
78
+ end
132
79
 
133
- it 'should not compel for invalid params 1' do
134
- expect{ make_the_call(:run, nil) }.to \
135
- raise_error Compel::ParamTypeError, 'params must be an Hash'
136
- end
80
+ it 'should not compel for invalid hash 1' do
81
+ expect{ make_the_call(:run, nil) }.to \
82
+ raise_error Compel::TypeError, 'must be an Hash'
83
+ end
137
84
 
138
- it 'should not compel' do
139
- params = {
140
- user: {
141
- first_name: 'Joaquim'
85
+ it 'should not compel' do
86
+ hash = {
87
+ user: {
88
+ first_name: 'Joaquim'
89
+ }
142
90
  }
143
- }
144
91
 
145
- expect(make_the_call(:run, params)).to eq \
146
- Hashie::Mash.new({
147
- user:{
148
- first_name: 'Joaquim',
149
- },
150
- errors: {
151
- user: {
152
- last_name: ['is required']
92
+ expect(make_the_call(:run, hash)).to eq \
93
+ Hashie::Mash.new({
94
+ user:{
95
+ first_name: 'Joaquim',
96
+ },
97
+ errors: {
98
+ user: {
99
+ last_name: ['is required']
100
+ }
153
101
  }
154
- }
155
- })
102
+ })
103
+ end
104
+
156
105
  end
157
106
 
158
- context 'nested Hash' do
159
-
160
- def make_the_call(method, params)
161
- Compel.send(method, params) do
162
- param :address, Hash, required: true do
163
- param :line_one, String, required: true
164
- param :line_two, String
165
- param :post_code, Hash, required: true do
166
- param :prefix, Integer, length: 4, required: true
167
- param :suffix, Integer, length: 3
168
- param :county, Hash do
169
- param :code, String, length: 2, required: true
170
- param :name, String
171
- end
172
- end
173
- end
174
- end
107
+ context 'Address validation example' do
108
+
109
+ def make_the_call(method, hash)
110
+ schema = Compel.hash.keys({
111
+ address: Compel.hash.keys({
112
+ line_one: Compel.string.required,
113
+ line_two: Compel.string,
114
+ post_code: Compel.hash.keys({
115
+ prefix: Compel.integer.length(4).required,
116
+ suffix: Compel.integer.length(3),
117
+ county: Compel.hash.keys({
118
+ code: Compel.string.length(2).required,
119
+ name: Compel.string
120
+ })
121
+ }).required
122
+ }).required
123
+ })
124
+
125
+ Compel.send(method, hash, schema)
175
126
  end
176
127
 
177
128
  it 'should run?' do
178
- params = {
129
+ hash = {
179
130
  address: {
180
131
  line_one: 'Lisbon',
181
132
  line_two: 'Portugal',
@@ -186,17 +137,17 @@ describe Compel do
186
137
  }
187
138
  }
188
139
 
189
- expect(make_the_call(:run?, params)).to eq(true)
140
+ expect(make_the_call(:run?, hash)).to eq(true)
190
141
  end
191
142
 
192
143
  it 'should not compel' do
193
- params = {
144
+ hash = {
194
145
  address: {
195
146
  line_two: 'Portugal'
196
147
  }
197
148
  }
198
149
 
199
- expect(make_the_call(:run, params)).to eq \
150
+ expect(make_the_call(:run, hash)).to eq \
200
151
  Hashie::Mash.new({
201
152
  address: {
202
153
  line_two: 'Portugal'
@@ -211,7 +162,7 @@ describe Compel do
211
162
  end
212
163
 
213
164
  it 'should not compel 1' do
214
- params = {
165
+ hash = {
215
166
  address: {
216
167
  line_two: 'Portugal',
217
168
  post_code: {
@@ -223,7 +174,7 @@ describe Compel do
223
174
  }
224
175
  }
225
176
 
226
- expect(make_the_call(:run, params)).to eq \
177
+ expect(make_the_call(:run, hash)).to eq \
227
178
  Hashie::Mash.new({
228
179
  address: {
229
180
  line_two: 'Portugal',
@@ -246,7 +197,7 @@ describe Compel do
246
197
  end
247
198
 
248
199
  it 'should not compel 2' do
249
- params = {
200
+ hash = {
250
201
  address: {
251
202
  post_code: {
252
203
  suffix: '1234'
@@ -254,7 +205,7 @@ describe Compel do
254
205
  }
255
206
  }
256
207
 
257
- expect(make_the_call(:run, params)).to eq \
208
+ expect(make_the_call(:run, hash)).to eq \
258
209
  Hashie::Mash.new({
259
210
  address: {
260
211
  post_code: {
@@ -274,7 +225,7 @@ describe Compel do
274
225
  end
275
226
 
276
227
  it 'should not compel 3' do
277
- params = {
228
+ hash = {
278
229
  address: {
279
230
  post_code: {
280
231
  prefix: '1100',
@@ -284,7 +235,7 @@ describe Compel do
284
235
  }
285
236
  }
286
237
 
287
- expect(make_the_call(:run, params)).to eq \
238
+ expect(make_the_call(:run, hash)).to eq \
288
239
  Hashie::Mash.new({
289
240
  address: {
290
241
  post_code: {
@@ -308,12 +259,13 @@ describe Compel do
308
259
  end
309
260
 
310
261
  it 'should not compel 4' do
311
- params = {
262
+ hash = {
312
263
  address: nil
313
264
  }
314
265
 
315
- expect(make_the_call(:run, params)).to eq \
266
+ expect(make_the_call(:run, hash)).to eq \
316
267
  Hashie::Mash.new({
268
+ address: nil,
317
269
  errors: {
318
270
  address: ['is required']
319
271
  }
@@ -331,6 +283,262 @@ describe Compel do
331
283
 
332
284
  end
333
285
 
286
+ context 'Boolean' do
287
+
288
+ context 'required option' do
289
+
290
+ it 'should compel' do
291
+ schema = Compel.hash.keys({
292
+ admin: Compel.boolean.required
293
+ })
294
+
295
+ expect(Compel.run?({ admin: 1 }, schema)).to be true
296
+ end
297
+
298
+ it 'should not compel' do
299
+ schema = Compel.hash.keys({
300
+ admin: Compel.boolean.required
301
+ })
302
+
303
+ expect(Compel.run({ admin: nil }, schema).errors[:admin]).to \
304
+ include('is required')
305
+ end
306
+
307
+ end
308
+
309
+ context 'default option' do
310
+
311
+ it 'should compel' do
312
+ schema = Compel.hash.keys({
313
+ admin: Compel.boolean.default(false)
314
+ })
315
+
316
+ expect(Compel.run({ admin: nil }, schema).admin).to be false
317
+ end
318
+
319
+ end
320
+
321
+ context 'is option' do
322
+
323
+ it 'should compel' do
324
+ schema = Compel.hash.keys({
325
+ admin: Compel.boolean.is(1)
326
+ })
327
+
328
+ expect(Compel.run?({ admin: 1 }, schema)).to be true
329
+ end
330
+
331
+ it 'should not compel' do
332
+ schema = Compel.hash.keys({
333
+ admin: Compel.boolean.is(1)
334
+ })
335
+
336
+ expect(Compel.run({ admin: 0 }, schema).errors[:admin]).to \
337
+ include('must be 1')
338
+ end
339
+
340
+ end
341
+
342
+ end
343
+
344
+ context 'String' do
345
+
346
+ context 'format option' do
347
+
348
+ it 'should compel' do
349
+ schema = Compel.hash.keys({
350
+ post_code: Compel.string.format(/^\d{4}-\d{3}$/)
351
+ })
352
+
353
+ expect(Compel.run?({ post_code: '1100-100' }, schema)).to be true
354
+ end
355
+
356
+ it 'should not compel' do
357
+ schema = Compel.hash.keys({
358
+ post_code: Compel.string.format(/^\d{4}-\d{3}$/)
359
+ })
360
+
361
+ expect(Compel.run({ post_code: '110-100' }, schema).errors[:post_code]).to \
362
+ include('must match format ^\\d{4}-\\d{3}$')
363
+ end
364
+
365
+ end
366
+
367
+ end
368
+
369
+ context 'Time' do
370
+
371
+ context 'format option' do
372
+
373
+ it 'should not compel' do
374
+ schema = Compel.hash.keys({
375
+ birth_date: Compel.time
376
+ })
377
+
378
+ expect(Compel.run({ birth_date: '1989-08-06' }, schema).errors.birth_date).to \
379
+ include("'1989-08-06' is not a parsable time with format: %FT%T")
380
+ end
381
+
382
+ it 'should compel with format' do
383
+ schema = Compel.hash.keys({
384
+ birth_date: Compel.time.format('%Y-%m-%d')
385
+ })
386
+
387
+ expect(Compel.run({ birth_date: '1989-08-06' }, schema).birth_date).to \
388
+ eq(Time.new(1989, 8, 6))
389
+ end
390
+
391
+ it 'should compel by default' do
392
+ schema = Compel.hash.keys({
393
+ birth_date: Compel.time
394
+ })
395
+
396
+ expect(Compel.run({ birth_date: '1989-08-06T09:00:00' }, schema).birth_date).to \
397
+ eq(Time.new(1989, 8, 6, 9))
398
+ end
399
+
400
+ it 'should compel with iso8601 format' do
401
+ schema = Compel.hash.keys({
402
+ birth_date: Compel.time.iso8601
403
+ })
404
+
405
+ expect(Compel.run({ birth_date: '1989-08-06T09:00:00' }, schema).birth_date).to \
406
+ eq(Time.new(1989, 8, 6, 9))
407
+ end
408
+
409
+ end
410
+
411
+ end
412
+
413
+ end
414
+
415
+ context 'DateTime' do
416
+
417
+ context 'format option' do
418
+
419
+ it 'should not compel' do
420
+ schema = Compel.hash.keys({
421
+ birth_date: Compel.datetime
422
+ })
423
+
424
+ expect(Compel.run({ birth_date: '1989-08-06' }, schema).errors.birth_date).to \
425
+ include("'1989-08-06' is not a parsable datetime with format: %FT%T")
426
+ end
427
+
428
+ it 'should compel with format' do
429
+ schema = Compel.hash.keys({
430
+ birth_date: Compel.datetime.format('%Y-%m-%d')
431
+ })
432
+
433
+ expect(Compel.run({ birth_date: '1989-08-06' }, schema).birth_date).to \
434
+ eq(DateTime.new(1989, 8, 6))
435
+ end
436
+
437
+ it 'should compel by default' do
438
+ schema = Compel.hash.keys({
439
+ birth_date: Compel.datetime
440
+ })
441
+
442
+ expect(Compel.run({ birth_date: '1989-08-06T09:00:00' }, schema).birth_date).to \
443
+ eq(DateTime.new(1989, 8, 6, 9))
444
+ end
445
+
446
+ it 'should compel with iso8601 format' do
447
+ schema = Compel.hash.keys({
448
+ birth_date: Compel.datetime.iso8601
449
+ })
450
+
451
+ expect(Compel.run({ birth_date: '1989-08-06T09:00:00' }, schema).birth_date).to \
452
+ eq(DateTime.new(1989, 8, 6, 9))
453
+ end
454
+
455
+ end
456
+
334
457
  end
335
458
 
459
+ context 'Compel methods' do
460
+
461
+ def make_the_call(method, hash)
462
+ schema = Compel.hash.keys({
463
+ first_name: Compel.string.required,
464
+ last_name: Compel.string.required,
465
+ birth_date: Compel.datetime
466
+ })
467
+
468
+ Compel.send(method, hash, schema)
469
+ end
470
+
471
+ context '#run!' do
472
+
473
+ it 'should compel' do
474
+ hash = {
475
+ first_name: 'Joaquim',
476
+ last_name: 'Adráz',
477
+ birth_date: DateTime.new(1988, 12, 24)
478
+ }
479
+
480
+ expect(make_the_call(:run!, hash)).to \
481
+ eq \
482
+ Hashie::Mash.new({
483
+ first_name: 'Joaquim',
484
+ last_name: 'Adráz',
485
+ birth_date: DateTime.new(1988, 12, 24)
486
+ })
487
+ end
488
+
489
+ it 'should raise InvalidHashError exception' do
490
+ hash = {
491
+ first_name: 'Joaquim'
492
+ }
493
+
494
+ expect{ make_the_call(:run!, hash) }.to \
495
+ raise_error Compel::InvalidHashError, 'hash has errors'
496
+ end
497
+
498
+ it 'should raise InvalidHashError exception with errors' do
499
+ hash = {
500
+ first_name: 'Joaquim'
501
+ }
502
+
503
+ expect{ make_the_call(:run!, hash) }.to raise_error do |exception|
504
+ expect(exception.object).to eq \
505
+ Hashie::Mash.new(first_name: 'Joaquim')
506
+
507
+ expect(exception.errors).to eq \
508
+ Hashie::Mash.new(last_name: ['is required'])
509
+ end
510
+ end
511
+
512
+ it 'should raise InvalidHashError exception for missing hash' do
513
+ expect{ make_the_call(:run!, {}) }.to \
514
+ raise_error Compel::InvalidHashError, 'hash has errors'
515
+ end
516
+
517
+ end
518
+
519
+ context '#run?' do
520
+
521
+ it 'should return true' do
522
+ hash = {
523
+ first_name: 'Joaquim',
524
+ last_name: 'Adráz',
525
+ birth_date: '1989-08-06T09:00:00'
526
+ }
527
+
528
+ expect(make_the_call(:run?, hash)).to eq(true)
529
+ end
530
+
531
+ it 'should return false' do
532
+ hash = {
533
+ first_name: 'Joaquim'
534
+ }
535
+
536
+ expect(make_the_call(:run?, hash)).to eq(false)
537
+ end
538
+
539
+ end
540
+
541
+ end
542
+
543
+
336
544
  end
@@ -0,0 +1,73 @@
1
+ describe Compel do
2
+
3
+ context 'Sinatra Integration' do
4
+
5
+ it 'should return 400 for missing params' do
6
+ post('/api/posts') do |response|
7
+ response_json = JSON.parse(response.body)
8
+
9
+ expect(response.status).to eq(400)
10
+ expect(response_json['errors']['post']).to \
11
+ include('is required')
12
+ end
13
+ end
14
+
15
+ it 'should return 400 for missing title' do
16
+ params = {
17
+ post: {
18
+ body: 'Body',
19
+ published: 0
20
+ }
21
+ }
22
+
23
+ post('/api/posts', params) do |response|
24
+ response_json = JSON.parse(response.body)
25
+
26
+ expect(response.status).to eq(400)
27
+ expect(response_json['errors']['post']['title']).to \
28
+ include('is required')
29
+ end
30
+ end
31
+
32
+ it 'should return 400 for invalid boolean' do
33
+ params = {
34
+ post: {
35
+ title: 'Title',
36
+ published: 'falss'
37
+ }
38
+ }
39
+
40
+ post('/api/posts', params) do |response|
41
+ response_json = JSON.parse(response.body)
42
+
43
+ expect(response.status).to eq(400)
44
+ expect(response_json['errors']['post']['published']).to \
45
+ include("'falss' is not a valid Boolean")
46
+ end
47
+ end
48
+
49
+ it 'should return 200' do
50
+ params = {
51
+ post: {
52
+ title: 'Title',
53
+ body: 'Body',
54
+ published: false
55
+ }
56
+ }
57
+
58
+ post('/api/posts', params) do |response|
59
+ response_json = JSON.parse(response.body)
60
+
61
+ expect(response.status).to eq(200)
62
+ expect(response_json['post']).to \
63
+ eq({
64
+ 'title' => 'Title',
65
+ 'body' => 'Body',
66
+ 'published' => false
67
+ })
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end