grape 0.7.0 → 0.8.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.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (62) hide show
  1. checksums.yaml +5 -13
  2. data/.rubocop.yml +6 -6
  3. data/.travis.yml +11 -2
  4. data/CHANGELOG.md +23 -1
  5. data/Gemfile +11 -10
  6. data/Guardfile +5 -6
  7. data/README.md +194 -13
  8. data/UPGRADING.md +3 -3
  9. data/grape.gemspec +1 -1
  10. data/grape.png +0 -0
  11. data/lib/grape/api.rb +21 -13
  12. data/lib/grape/endpoint.rb +31 -13
  13. data/lib/grape/exceptions/validation.rb +2 -2
  14. data/lib/grape/locale/en.yml +2 -0
  15. data/lib/grape/middleware/auth/oauth2.rb +69 -65
  16. data/lib/grape/middleware/error.rb +4 -2
  17. data/lib/grape/middleware/formatter.rb +2 -2
  18. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
  19. data/lib/grape/middleware/versioner/header.rb +3 -3
  20. data/lib/grape/util/hash_stack.rb +1 -1
  21. data/lib/grape/validations.rb +22 -8
  22. data/lib/grape/validations/default.rb +1 -1
  23. data/lib/grape/validations/exactly_one_of.rb +26 -0
  24. data/lib/grape/validations/mutual_exclusion.rb +25 -0
  25. data/lib/grape/validations/presence.rb +1 -1
  26. data/lib/grape/validations/regexp.rb +2 -1
  27. data/lib/grape/validations/values.rb +7 -1
  28. data/lib/grape/version.rb +1 -1
  29. data/spec/grape/api_spec.rb +390 -333
  30. data/spec/grape/endpoint_spec.rb +129 -99
  31. data/spec/grape/entity_spec.rb +47 -27
  32. data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
  33. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +1 -1
  34. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -2
  35. data/spec/grape/exceptions/missing_option_spec.rb +1 -1
  36. data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
  37. data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
  38. data/spec/grape/middleware/auth/basic_spec.rb +3 -3
  39. data/spec/grape/middleware/auth/digest_spec.rb +4 -4
  40. data/spec/grape/middleware/auth/oauth2_spec.rb +11 -11
  41. data/spec/grape/middleware/base_spec.rb +9 -9
  42. data/spec/grape/middleware/error_spec.rb +4 -4
  43. data/spec/grape/middleware/exception_spec.rb +17 -17
  44. data/spec/grape/middleware/formatter_spec.rb +38 -38
  45. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +11 -11
  46. data/spec/grape/middleware/versioner/header_spec.rb +39 -39
  47. data/spec/grape/middleware/versioner/param_spec.rb +10 -10
  48. data/spec/grape/middleware/versioner/path_spec.rb +7 -7
  49. data/spec/grape/middleware/versioner_spec.rb +4 -4
  50. data/spec/grape/path_spec.rb +6 -6
  51. data/spec/grape/util/hash_stack_spec.rb +22 -22
  52. data/spec/grape/validations/coerce_spec.rb +34 -34
  53. data/spec/grape/validations/default_spec.rb +16 -16
  54. data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
  55. data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
  56. data/spec/grape/validations/presence_spec.rb +34 -34
  57. data/spec/grape/validations/regexp_spec.rb +11 -4
  58. data/spec/grape/validations/values_spec.rb +34 -20
  59. data/spec/grape/validations_spec.rb +300 -147
  60. data/spec/shared/versioning_examples.rb +18 -18
  61. data/spec/spec_helper.rb +2 -1
  62. metadata +81 -38
@@ -19,12 +19,12 @@ describe Grape::Validations do
19
19
  end
20
20
 
21
21
  get '/optional', a_number: 'string'
22
- last_response.status.should == 400
23
- last_response.body.should == 'a_number is invalid'
22
+ expect(last_response.status).to eq(400)
23
+ expect(last_response.body).to eq('a_number is invalid')
24
24
 
25
25
  get '/optional', a_number: 45
26
- last_response.status.should == 200
27
- last_response.body.should == 'optional works!'
26
+ expect(last_response.status).to eq(200)
27
+ expect(last_response.body).to eq('optional works!')
28
28
  end
29
29
 
30
30
  it "doesn't validate when param not present" do
@@ -36,15 +36,15 @@ describe Grape::Validations do
36
36
  end
37
37
 
38
38
  get '/optional'
39
- last_response.status.should == 200
40
- last_response.body.should == 'optional works!'
39
+ expect(last_response.status).to eq(200)
40
+ expect(last_response.body).to eq('optional works!')
41
41
  end
42
42
 
43
43
  it 'adds to declared parameters' do
44
44
  subject.params do
45
45
  optional :some_param
46
46
  end
47
- subject.settings[:declared_params].should == [:some_param]
47
+ expect(subject.settings[:declared_params]).to eq([:some_param])
48
48
  end
49
49
  end
50
50
 
@@ -60,29 +60,29 @@ describe Grape::Validations do
60
60
 
61
61
  it 'errors when param not present' do
62
62
  get '/required'
63
- last_response.status.should == 400
64
- last_response.body.should == 'key is missing'
63
+ expect(last_response.status).to eq(400)
64
+ expect(last_response.body).to eq('key is missing')
65
65
  end
66
66
 
67
67
  it "doesn't throw a missing param when param is present" do
68
68
  get '/required', key: 'cool'
69
- last_response.status.should == 200
70
- last_response.body.should == 'required works'
69
+ expect(last_response.status).to eq(200)
70
+ expect(last_response.body).to eq('required works')
71
71
  end
72
72
 
73
73
  it 'adds to declared parameters' do
74
74
  subject.params do
75
75
  requires :some_param
76
76
  end
77
- subject.settings[:declared_params].should == [:some_param]
77
+ expect(subject.settings[:declared_params]).to eq([:some_param])
78
78
  end
79
79
  end
80
80
 
81
81
  context 'requires :all using Grape::Entity documentation' do
82
82
  def define_requires_all
83
83
  documentation = {
84
- required_field: { type: String },
85
- optional_field: { type: String }
84
+ required_field: { type: String },
85
+ optional_field: { type: String }
86
86
  }
87
87
  subject.params do
88
88
  requires :all, except: :optional_field, using: documentation
@@ -97,27 +97,27 @@ describe Grape::Validations do
97
97
 
98
98
  it 'adds entity documentation to declared params' do
99
99
  define_requires_all
100
- subject.settings[:declared_params].should == [:required_field, :optional_field]
100
+ expect(subject.settings[:declared_params]).to eq([:required_field, :optional_field])
101
101
  end
102
102
 
103
103
  it 'errors when required_field is not present' do
104
104
  get '/required'
105
- last_response.status.should == 400
106
- last_response.body.should == 'required_field is missing'
105
+ expect(last_response.status).to eq(400)
106
+ expect(last_response.body).to eq('required_field is missing')
107
107
  end
108
108
 
109
109
  it 'works when required_field is present' do
110
110
  get '/required', required_field: 'woof'
111
- last_response.status.should == 200
112
- last_response.body.should == 'required works'
111
+ expect(last_response.status).to eq(200)
112
+ expect(last_response.body).to eq('required works')
113
113
  end
114
114
  end
115
115
 
116
116
  context 'requires :none using Grape::Entity documentation' do
117
117
  def define_requires_none
118
118
  documentation = {
119
- required_field: { type: String },
120
- optional_field: { type: String }
119
+ required_field: { type: String },
120
+ optional_field: { type: String }
121
121
  }
122
122
  subject.params do
123
123
  requires :none, except: :required_field, using: documentation
@@ -132,19 +132,54 @@ describe Grape::Validations do
132
132
 
133
133
  it 'adds entity documentation to declared params' do
134
134
  define_requires_none
135
- subject.settings[:declared_params].should == [:required_field, :optional_field]
135
+ expect(subject.settings[:declared_params]).to eq([:required_field, :optional_field])
136
136
  end
137
137
 
138
138
  it 'errors when required_field is not present' do
139
139
  get '/required'
140
- last_response.status.should == 400
141
- last_response.body.should == 'required_field is missing'
140
+ expect(last_response.status).to eq(400)
141
+ expect(last_response.body).to eq('required_field is missing')
142
142
  end
143
143
 
144
144
  it 'works when required_field is present' do
145
145
  get '/required', required_field: 'woof'
146
- last_response.status.should == 200
147
- last_response.body.should == 'required works'
146
+ expect(last_response.status).to eq(200)
147
+ expect(last_response.body).to eq('required works')
148
+ end
149
+ end
150
+
151
+ context 'requires :all or :none but except a non-existent field using Grape::Entity documentation' do
152
+ context 'requires :all' do
153
+ def define_requires_all
154
+ documentation = {
155
+ required_field: { type: String },
156
+ optional_field: { type: String }
157
+ }
158
+ subject.params do
159
+ requires :all, except: :non_existent_field, using: documentation
160
+ end
161
+ end
162
+
163
+ it 'adds only the entity documentation to declared params, nothing more' do
164
+ define_requires_all
165
+ expect(subject.settings[:declared_params]).to eq([:required_field, :optional_field])
166
+ end
167
+ end
168
+
169
+ context 'requires :none' do
170
+ def define_requires_none
171
+ documentation = {
172
+ required_field: { type: String },
173
+ optional_field: { type: String }
174
+ }
175
+ subject.params do
176
+ requires :none, except: :non_existent_field, using: documentation
177
+ end
178
+ end
179
+
180
+ it 'adds only the entity documentation to declared params, nothing more' do
181
+ expect { define_requires_none }.to raise_error(ArgumentError)
182
+ end
148
183
  end
149
184
  end
150
185
 
@@ -162,24 +197,24 @@ describe Grape::Validations do
162
197
 
163
198
  it 'errors when param not present' do
164
199
  get '/required'
165
- last_response.status.should == 400
166
- last_response.body.should == 'items is missing'
200
+ expect(last_response.status).to eq(400)
201
+ expect(last_response.body).to eq('items is missing')
167
202
  end
168
203
 
169
204
  it "errors when param is not an Array" do
170
205
  get '/required', items: "hello"
171
- last_response.status.should == 400
172
- last_response.body.should == 'items is invalid, items[key] is missing'
206
+ expect(last_response.status).to eq(400)
207
+ expect(last_response.body).to eq('items is invalid, items[key] is missing')
173
208
 
174
209
  get '/required', items: { key: 'foo' }
175
- last_response.status.should == 400
176
- last_response.body.should == 'items is invalid'
210
+ expect(last_response.status).to eq(400)
211
+ expect(last_response.body).to eq('items is invalid')
177
212
  end
178
213
 
179
214
  it "doesn't throw a missing param when param is present" do
180
215
  get '/required', items: [{ key: 'hello' }, { key: 'world' }]
181
- last_response.status.should == 200
182
- last_response.body.should == 'required works'
216
+ expect(last_response.status).to eq(200)
217
+ expect(last_response.body).to eq('required works')
183
218
  end
184
219
 
185
220
  it "doesn't allow any key in the options hash other than type" do
@@ -198,7 +233,7 @@ describe Grape::Validations do
198
233
  requires :key
199
234
  end
200
235
  end
201
- subject.settings[:declared_params].should == [items: [:key]]
236
+ expect(subject.settings[:declared_params]).to eq([items: [:key]])
202
237
  end
203
238
  end
204
239
 
@@ -216,24 +251,24 @@ describe Grape::Validations do
216
251
 
217
252
  it 'errors when param not present' do
218
253
  get '/required'
219
- last_response.status.should == 400
220
- last_response.body.should == 'items is missing, items[key] is missing'
254
+ expect(last_response.status).to eq(400)
255
+ expect(last_response.body).to eq('items is missing, items[key] is missing')
221
256
  end
222
257
 
223
258
  it "errors when param is not a Hash" do
224
259
  get '/required', items: "hello"
225
- last_response.status.should == 400
226
- last_response.body.should == 'items is invalid, items[key] is missing'
260
+ expect(last_response.status).to eq(400)
261
+ expect(last_response.body).to eq('items is invalid, items[key] is missing')
227
262
 
228
263
  get '/required', items: [{ key: 'foo' }]
229
- last_response.status.should == 400
230
- last_response.body.should == 'items is invalid'
264
+ expect(last_response.status).to eq(400)
265
+ expect(last_response.body).to eq('items is invalid')
231
266
  end
232
267
 
233
268
  it "doesn't throw a missing param when param is present" do
234
269
  get '/required', items: { key: 'hello' }
235
- last_response.status.should == 200
236
- last_response.body.should == 'required works'
270
+ expect(last_response.status).to eq(200)
271
+ expect(last_response.body).to eq('required works')
237
272
  end
238
273
 
239
274
  it "doesn't allow any key in the options hash other than type" do
@@ -252,7 +287,7 @@ describe Grape::Validations do
252
287
  requires :key
253
288
  end
254
289
  end
255
- subject.settings[:declared_params].should == [items: [:key]]
290
+ expect(subject.settings[:declared_params]).to eq([items: [:key]])
256
291
  end
257
292
  end
258
293
 
@@ -270,14 +305,14 @@ describe Grape::Validations do
270
305
 
271
306
  it 'errors when param not present' do
272
307
  get '/required'
273
- last_response.status.should == 400
274
- last_response.body.should == 'items is missing'
308
+ expect(last_response.status).to eq(400)
309
+ expect(last_response.body).to eq('items is missing')
275
310
  end
276
311
 
277
312
  it "doesn't throw a missing param when param is present" do
278
313
  get '/required', items: [key: 'hello', key: 'world']
279
- last_response.status.should == 200
280
- last_response.body.should == 'required works'
314
+ expect(last_response.status).to eq(200)
315
+ expect(last_response.body).to eq('required works')
281
316
  end
282
317
 
283
318
  it 'adds to declared parameters' do
@@ -286,7 +321,7 @@ describe Grape::Validations do
286
321
  requires :key
287
322
  end
288
323
  end
289
- subject.settings[:declared_params].should == [items: [:key]]
324
+ expect(subject.settings[:declared_params]).to eq([items: [:key]])
290
325
  end
291
326
  end
292
327
 
@@ -310,8 +345,8 @@ describe Grape::Validations do
310
345
  { name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
311
346
  { name: 'Joe', parents: [{ name: 'Josie' }] }
312
347
  ]
313
- last_response.status.should == 200
314
- last_response.body.should == 'within array works'
348
+ expect(last_response.status).to eq(200)
349
+ expect(last_response.body).to eq('within array works')
315
350
  end
316
351
 
317
352
  it 'errors when a parameter is not present' do
@@ -321,34 +356,34 @@ describe Grape::Validations do
321
356
  ]
322
357
  # NOTE: with body parameters in json or XML or similar this
323
358
  # should actually fail with: children[parents][name] is missing.
324
- last_response.status.should == 400
325
- last_response.body.should == 'children[parents] is missing'
359
+ expect(last_response.status).to eq(400)
360
+ expect(last_response.body).to eq('children[parents] is missing')
326
361
  end
327
362
 
328
363
  it 'safely handles empty arrays and blank parameters' do
329
364
  # NOTE: with body parameters in json or XML or similar this
330
365
  # should actually return 200, since an empty array is valid.
331
366
  get '/within_array', children: []
332
- last_response.status.should == 400
333
- last_response.body.should == 'children is missing'
367
+ expect(last_response.status).to eq(400)
368
+ expect(last_response.body).to eq('children is missing')
334
369
  get '/within_array', children: [name: 'Jay']
335
- last_response.status.should == 400
336
- last_response.body.should == 'children[parents] is missing'
370
+ expect(last_response.status).to eq(400)
371
+ expect(last_response.body).to eq('children[parents] is missing')
337
372
  end
338
373
 
339
374
  it "errors when param is not an Array" do
340
375
  # NOTE: would be nicer if these just returned 'children is invalid'
341
376
  get '/within_array', children: "hello"
342
- last_response.status.should == 400
343
- last_response.body.should == 'children is invalid, children[name] is missing, children[parents] is missing, children[parents] is invalid, children[parents][name] is missing'
377
+ expect(last_response.status).to eq(400)
378
+ expect(last_response.body).to eq('children is invalid, children[name] is missing, children[parents] is missing, children[parents] is invalid, children[parents][name] is missing')
344
379
 
345
380
  get '/within_array', children: { name: 'foo' }
346
- last_response.status.should == 400
347
- last_response.body.should == 'children is invalid, children[parents] is missing'
381
+ expect(last_response.status).to eq(400)
382
+ expect(last_response.body).to eq('children is invalid, children[parents] is missing')
348
383
 
349
384
  get '/within_array', children: [name: 'Jay', parents: { name: 'Fred' }]
350
- last_response.status.should == 400
351
- last_response.body.should == 'children[parents] is invalid'
385
+ expect(last_response.status).to eq(400)
386
+ expect(last_response.body).to eq('children[parents] is invalid')
352
387
  end
353
388
  end
354
389
 
@@ -394,53 +429,53 @@ describe Grape::Validations do
394
429
 
395
430
  it 'requires defaults to Array type' do
396
431
  get '/req', planets: "Jupiter, Saturn"
397
- last_response.status.should == 400
398
- last_response.body.should == 'planets is invalid, planets[name] is missing'
432
+ expect(last_response.status).to eq(400)
433
+ expect(last_response.body).to eq('planets is invalid, planets[name] is missing')
399
434
 
400
435
  get '/req', planets: { name: 'Jupiter' }
401
- last_response.status.should == 400
402
- last_response.body.should == 'planets is invalid'
436
+ expect(last_response.status).to eq(400)
437
+ expect(last_response.body).to eq('planets is invalid')
403
438
 
404
439
  get '/req', planets: [{ name: 'Venus' }, { name: 'Mars' }]
405
- last_response.status.should == 200
440
+ expect(last_response.status).to eq(200)
406
441
 
407
442
  put_with_json '/req', planets: []
408
- last_response.status.should == 200
443
+ expect(last_response.status).to eq(200)
409
444
  end
410
445
 
411
446
  it 'optional defaults to Array type' do
412
447
  get '/opt', name: "Jupiter", moons: "Europa, Ganymede"
413
- last_response.status.should == 400
414
- last_response.body.should == 'moons is invalid, moons[name] is missing'
448
+ expect(last_response.status).to eq(400)
449
+ expect(last_response.body).to eq('moons is invalid, moons[name] is missing')
415
450
 
416
451
  get '/opt', name: "Jupiter", moons: { name: 'Ganymede' }
417
- last_response.status.should == 400
418
- last_response.body.should == 'moons is invalid'
452
+ expect(last_response.status).to eq(400)
453
+ expect(last_response.body).to eq('moons is invalid')
419
454
 
420
455
  get '/opt', name: "Jupiter", moons: [{ name: 'Io' }, { name: 'Callisto' }]
421
- last_response.status.should == 200
456
+ expect(last_response.status).to eq(200)
422
457
 
423
458
  put_with_json '/opt', name: "Venus"
424
- last_response.status.should == 200
459
+ expect(last_response.status).to eq(200)
425
460
 
426
461
  put_with_json '/opt', name: "Mercury", moons: []
427
- last_response.status.should == 200
462
+ expect(last_response.status).to eq(200)
428
463
  end
429
464
 
430
465
  it 'group defaults to Array type' do
431
466
  get '/grp', stars: "Sun"
432
- last_response.status.should == 400
433
- last_response.body.should == 'stars is invalid, stars[name] is missing'
467
+ expect(last_response.status).to eq(400)
468
+ expect(last_response.body).to eq('stars is invalid, stars[name] is missing')
434
469
 
435
470
  get '/grp', stars: { name: 'Sun' }
436
- last_response.status.should == 400
437
- last_response.body.should == 'stars is invalid'
471
+ expect(last_response.status).to eq(400)
472
+ expect(last_response.body).to eq('stars is invalid')
438
473
 
439
474
  get '/grp', stars: [{ name: 'Sun' }]
440
- last_response.status.should == 200
475
+ expect(last_response.status).to eq(200)
441
476
 
442
477
  put_with_json '/grp', stars: []
443
- last_response.status.should == 200
478
+ expect(last_response.status).to eq(200)
444
479
  end
445
480
  end
446
481
 
@@ -464,8 +499,8 @@ describe Grape::Validations do
464
499
  { name: 'John', parents: [{ name: 'Jane' }, { name: 'Bob' }] },
465
500
  { name: 'Joe', parents: [{ name: 'Josie' }] }
466
501
  ]
467
- last_response.status.should == 200
468
- last_response.body.should == 'within array works'
502
+ expect(last_response.status).to eq(200)
503
+ expect(last_response.body).to eq('within array works')
469
504
  end
470
505
 
471
506
  it 'errors when a parameter is not present' do
@@ -473,16 +508,16 @@ describe Grape::Validations do
473
508
  { name: 'Jim', parents: [{}] },
474
509
  { name: 'Job', parents: [{ name: 'Joy' }] }
475
510
  ]
476
- last_response.status.should == 400
477
- last_response.body.should == 'children[parents][name] is missing'
511
+ expect(last_response.status).to eq(400)
512
+ expect(last_response.body).to eq('children[parents][name] is missing')
478
513
  end
479
514
 
480
515
  it 'safely handles empty arrays and blank parameters' do
481
516
  put_with_json '/within_array', children: []
482
- last_response.status.should == 200
517
+ expect(last_response.status).to eq(200)
483
518
  put_with_json '/within_array', children: [name: 'Jay']
484
- last_response.status.should == 400
485
- last_response.body.should == 'children[parents] is missing'
519
+ expect(last_response.status).to eq(400)
520
+ expect(last_response.body).to eq('children[parents] is missing')
486
521
  end
487
522
  end
488
523
 
@@ -500,30 +535,30 @@ describe Grape::Validations do
500
535
 
501
536
  it "doesn't throw a missing param when the group isn't present" do
502
537
  get '/optional_group'
503
- last_response.status.should == 200
504
- last_response.body.should == 'optional group works'
538
+ expect(last_response.status).to eq(200)
539
+ expect(last_response.body).to eq('optional group works')
505
540
  end
506
541
 
507
542
  it "doesn't throw a missing param when both group and param are given" do
508
543
  get '/optional_group', items: [{ key: 'foo' }]
509
- last_response.status.should == 200
510
- last_response.body.should == 'optional group works'
544
+ expect(last_response.status).to eq(200)
545
+ expect(last_response.body).to eq('optional group works')
511
546
  end
512
547
 
513
548
  it "errors when group is present, but required param is not" do
514
549
  get '/optional_group', items: [{ not_key: 'foo' }]
515
- last_response.status.should == 400
516
- last_response.body.should == 'items[key] is missing'
550
+ expect(last_response.status).to eq(400)
551
+ expect(last_response.body).to eq('items[key] is missing')
517
552
  end
518
553
 
519
554
  it "errors when param is present but isn't an Array" do
520
555
  get '/optional_group', items: "hello"
521
- last_response.status.should == 400
522
- last_response.body.should == 'items is invalid, items[key] is missing'
556
+ expect(last_response.status).to eq(400)
557
+ expect(last_response.body).to eq('items is invalid, items[key] is missing')
523
558
 
524
559
  get '/optional_group', items: { key: 'foo' }
525
- last_response.status.should == 400
526
- last_response.body.should == 'items is invalid'
560
+ expect(last_response.status).to eq(400)
561
+ expect(last_response.body).to eq('items is invalid')
527
562
  end
528
563
 
529
564
  it 'adds to declared parameters' do
@@ -532,7 +567,7 @@ describe Grape::Validations do
532
567
  requires :key
533
568
  end
534
569
  end
535
- subject.settings[:declared_params].should == [items: [:key]]
570
+ expect(subject.settings[:declared_params]).to eq([items: [:key]])
536
571
  end
537
572
  end
538
573
 
@@ -550,42 +585,42 @@ describe Grape::Validations do
550
585
 
551
586
  it 'does no internal validations if the outer group is blank' do
552
587
  get '/nested_optional_group'
553
- last_response.status.should == 200
554
- last_response.body.should == 'nested optional group works'
588
+ expect(last_response.status).to eq(200)
589
+ expect(last_response.body).to eq('nested optional group works')
555
590
  end
556
591
 
557
592
  it 'does internal validations if the outer group is present' do
558
593
  get '/nested_optional_group', items: [{ key: 'foo' }]
559
- last_response.status.should == 400
560
- last_response.body.should == 'items[required_subitems] is missing'
594
+ expect(last_response.status).to eq(400)
595
+ expect(last_response.body).to eq('items[required_subitems] is missing')
561
596
 
562
597
  get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
563
- last_response.status.should == 200
564
- last_response.body.should == 'nested optional group works'
598
+ expect(last_response.status).to eq(200)
599
+ expect(last_response.body).to eq('nested optional group works')
565
600
  end
566
601
 
567
602
  it 'handles deep nesting' do
568
603
  get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
569
- last_response.status.should == 400
570
- last_response.body.should == 'items[optional_subitems][value] is missing'
604
+ expect(last_response.status).to eq(400)
605
+ expect(last_response.body).to eq('items[optional_subitems][value] is missing')
571
606
 
572
607
  get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ value: 'baz' }] }]
573
- last_response.status.should == 200
574
- last_response.body.should == 'nested optional group works'
608
+ expect(last_response.status).to eq(200)
609
+ expect(last_response.body).to eq('nested optional group works')
575
610
  end
576
611
 
577
612
  it 'handles validation within arrays' do
578
613
  get '/nested_optional_group', items: [{ key: 'foo' }]
579
- last_response.status.should == 400
580
- last_response.body.should == 'items[required_subitems] is missing'
614
+ expect(last_response.status).to eq(400)
615
+ expect(last_response.body).to eq('items[required_subitems] is missing')
581
616
 
582
617
  get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }] }]
583
- last_response.status.should == 200
584
- last_response.body.should == 'nested optional group works'
618
+ expect(last_response.status).to eq(200)
619
+ expect(last_response.body).to eq('nested optional group works')
585
620
 
586
621
  get '/nested_optional_group', items: [{ key: 'foo', required_subitems: [{ value: 'bar' }], optional_subitems: [{ not_value: 'baz' }] }]
587
- last_response.status.should == 400
588
- last_response.body.should == 'items[optional_subitems][value] is missing'
622
+ expect(last_response.status).to eq(400)
623
+ expect(last_response.body).to eq('items[optional_subitems][value] is missing')
589
624
  end
590
625
 
591
626
  it 'adds to declared parameters' do
@@ -596,7 +631,7 @@ describe Grape::Validations do
596
631
  requires(:required_subitems) { requires :value }
597
632
  end
598
633
  end
599
- subject.settings[:declared_params].should == [items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]]
634
+ expect(subject.settings[:declared_params]).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
600
635
  end
601
636
  end
602
637
 
@@ -613,9 +648,9 @@ describe Grape::Validations do
613
648
 
614
649
  it 'throws the validation errors' do
615
650
  get '/two_required'
616
- last_response.status.should == 400
617
- last_response.body.should =~ /yolo is missing/
618
- last_response.body.should =~ /swag is missing/
651
+ expect(last_response.status).to eq(400)
652
+ expect(last_response.body).to match(/yolo is missing/)
653
+ expect(last_response.body).to match(/swag is missing/)
619
654
  end
620
655
  end
621
656
 
@@ -642,18 +677,18 @@ describe Grape::Validations do
642
677
 
643
678
  it 'validates when param is present' do
644
679
  get '/optional_custom', custom: 'im custom'
645
- last_response.status.should == 200
646
- last_response.body.should == 'optional with custom works!'
680
+ expect(last_response.status).to eq(200)
681
+ expect(last_response.body).to eq('optional with custom works!')
647
682
 
648
683
  get '/optional_custom', custom: 'im wrong'
649
- last_response.status.should == 400
650
- last_response.body.should == 'custom is not custom!'
684
+ expect(last_response.status).to eq(400)
685
+ expect(last_response.body).to eq('custom is not custom!')
651
686
  end
652
687
 
653
688
  it "skips validation when parameter isn't present" do
654
689
  get '/optional_custom'
655
- last_response.status.should == 200
656
- last_response.body.should == 'optional with custom works!'
690
+ expect(last_response.status).to eq(200)
691
+ expect(last_response.body).to eq('optional with custom works!')
657
692
  end
658
693
 
659
694
  it 'validates with custom validator when param present and incorrect type' do
@@ -662,8 +697,8 @@ describe Grape::Validations do
662
697
  end
663
698
 
664
699
  get '/optional_custom', custom: 123
665
- last_response.status.should == 400
666
- last_response.body.should == 'custom is not custom!'
700
+ expect(last_response.status).to eq(400)
701
+ expect(last_response.body).to eq('custom is not custom!')
667
702
  end
668
703
  end
669
704
 
@@ -679,18 +714,18 @@ describe Grape::Validations do
679
714
 
680
715
  it 'validates when param is present' do
681
716
  get '/required_custom', custom: 'im wrong, validate me'
682
- last_response.status.should == 400
683
- last_response.body.should == 'custom is not custom!'
717
+ expect(last_response.status).to eq(400)
718
+ expect(last_response.body).to eq('custom is not custom!')
684
719
 
685
720
  get '/required_custom', custom: 'im custom'
686
- last_response.status.should == 200
687
- last_response.body.should == 'required with custom works!'
721
+ expect(last_response.status).to eq(200)
722
+ expect(last_response.body).to eq('required with custom works!')
688
723
  end
689
724
 
690
725
  it 'validates when param is not present' do
691
726
  get '/required_custom'
692
- last_response.status.should == 400
693
- last_response.body.should == 'custom is missing, custom is not custom!'
727
+ expect(last_response.status).to eq(400)
728
+ expect(last_response.body).to eq('custom is missing, custom is not custom!')
694
729
  end
695
730
 
696
731
  context 'nested namespaces' do
@@ -737,33 +772,33 @@ describe Grape::Validations do
737
772
 
738
773
  specify 'the parent namespace uses the validator' do
739
774
  get '/nested/one', custom: 'im wrong, validate me'
740
- last_response.status.should == 400
741
- last_response.body.should == 'custom is not custom!'
775
+ expect(last_response.status).to eq(400)
776
+ expect(last_response.body).to eq('custom is not custom!')
742
777
  end
743
778
 
744
779
  specify 'the nested namesapce inherits the custom validator' do
745
780
  get '/nested/nested/two', custom: 'im wrong, validate me'
746
- last_response.status.should == 400
747
- last_response.body.should == 'custom is not custom!'
781
+ expect(last_response.status).to eq(400)
782
+ expect(last_response.body).to eq('custom is not custom!')
748
783
  end
749
784
 
750
785
  specify 'peer namesapces does not have the validator' do
751
786
  get '/peer/one', custom: 'im not validated'
752
- last_response.status.should == 200
753
- last_response.body.should == 'no validation required'
787
+ expect(last_response.status).to eq(200)
788
+ expect(last_response.body).to eq('no validation required')
754
789
  end
755
790
 
756
791
  specify 'namespaces nested in peers should also not have the validator' do
757
792
  get '/peer/nested/two', custom: 'im not validated'
758
- last_response.status.should == 200
759
- last_response.body.should == 'no validation required'
793
+ expect(last_response.status).to eq(200)
794
+ expect(last_response.body).to eq('no validation required')
760
795
  end
761
796
 
762
797
  specify 'when nested, specifying a route should clear out the validations for deeper nested params' do
763
798
  get '/unrelated/one'
764
- last_response.status.should == 400
799
+ expect(last_response.status).to eq(400)
765
800
  get '/unrelated/double/two'
766
- last_response.status.should == 200
801
+ expect(last_response.status).to eq(200)
767
802
  end
768
803
  end
769
804
  end
@@ -811,17 +846,58 @@ describe Grape::Validations do
811
846
  subject.params do
812
847
  use :pagination
813
848
  end
814
- subject.settings[:declared_params].should eq [:page, :per_page]
849
+ expect(subject.settings[:declared_params]).to eq [:page, :per_page]
815
850
  end
816
851
 
817
852
  it 'by #use with multiple params' do
818
853
  subject.params do
819
854
  use :pagination, :period
820
855
  end
821
- subject.settings[:declared_params].should eq [:page, :per_page, :start_date, :end_date]
856
+ expect(subject.settings[:declared_params]).to eq [:page, :per_page, :start_date, :end_date]
822
857
  end
823
858
 
824
859
  end
860
+
861
+ context 'with block' do
862
+ before do
863
+ subject.helpers do
864
+ params :order do |options|
865
+ optional :order, type: Symbol, values: [:asc, :desc], default: options[:default_order]
866
+ optional :order_by, type: Symbol, values: options[:order_by], default: options[:default_order_by]
867
+ end
868
+ end
869
+ subject.format :json
870
+ subject.params do
871
+ use :order, default_order: :asc, order_by: [:name, :created_at], default_order_by: :created_at
872
+ end
873
+ subject.get '/order' do
874
+ {
875
+ order: params[:order],
876
+ order_by: params[:order_by]
877
+ }
878
+ end
879
+ end
880
+ it 'returns defaults' do
881
+ get '/order'
882
+ expect(last_response.status).to eq(200)
883
+ expect(last_response.body).to eq({ order: :asc, order_by: :created_at }.to_json)
884
+ end
885
+ it 'overrides default value for order' do
886
+ get '/order?order=desc'
887
+ expect(last_response.status).to eq(200)
888
+ expect(last_response.body).to eq({ order: :desc, order_by: :created_at }.to_json)
889
+ end
890
+ it 'overrides default value for order_by' do
891
+ get '/order?order_by=name'
892
+ expect(last_response.status).to eq(200)
893
+ expect(last_response.body).to eq({ order: :asc, order_by: :name }.to_json)
894
+ end
895
+ it 'fails with invalid value' do
896
+ get '/order?order=invalid'
897
+ expect(last_response.status).to eq(400)
898
+ expect(last_response.body).to eq('{"error":"order does not have a valid value"}')
899
+ end
900
+ end
825
901
  end
826
902
 
827
903
  context 'documentation' do
@@ -834,7 +910,84 @@ describe Grape::Validations do
834
910
  subject.get '/' do
835
911
  end
836
912
 
837
- subject.routes.first.route_params['first_name'][:documentation].should eq(documentation)
913
+ expect(subject.routes.first.route_params['first_name'][:documentation]).to eq(documentation)
914
+ end
915
+ end
916
+
917
+ context 'mutually exclusive' do
918
+ context 'optional params' do
919
+ it 'errors when two or more are present' do
920
+ subject.params do
921
+ optional :beer
922
+ optional :wine
923
+ optional :juice
924
+ mutually_exclusive :beer, :wine, :juice
925
+ end
926
+ subject.get '/mutually_exclusive' do
927
+ 'mutually_exclusive works!'
928
+ end
929
+
930
+ get '/mutually_exclusive', beer: 'string', wine: 'anotherstring'
931
+ expect(last_response.status).to eq(400)
932
+ expect(last_response.body).to eq("[:beer, :wine] are mutually exclusive")
933
+ end
934
+ end
935
+
936
+ context 'more than one set of mutually exclusive params' do
937
+ it 'errors for all sets' do
938
+ subject.params do
939
+ optional :beer
940
+ optional :wine
941
+ mutually_exclusive :beer, :wine
942
+ optional :scotch
943
+ optional :aquavit
944
+ mutually_exclusive :scotch, :aquavit
945
+ end
946
+ subject.get '/mutually_exclusive' do
947
+ 'mutually_exclusive works!'
948
+ end
949
+
950
+ get '/mutually_exclusive', beer: 'true', wine: 'true', scotch: 'true', aquavit: 'true'
951
+ expect(last_response.status).to eq(400)
952
+ expect(last_response.body).to match(/\[:beer, :wine\] are mutually exclusive/)
953
+ expect(last_response.body).to match(/\[:scotch, :aquavit\] are mutually exclusive/)
954
+ end
955
+ end
956
+ end
957
+
958
+ context 'exactly one of' do
959
+ context 'params' do
960
+ it 'errors when two or more are present' do
961
+ subject.params do
962
+ optional :beer
963
+ optional :wine
964
+ optional :juice
965
+ exactly_one_of :beer, :wine, :juice
966
+ end
967
+ subject.get '/exactly_one_of' do
968
+ 'exactly_one_of works!'
969
+ end
970
+
971
+ get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
972
+ expect(last_response.status).to eq(400)
973
+ expect(last_response.body).to eq("[:beer, :wine] are mutually exclusive")
974
+ end
975
+
976
+ it 'errors when none is selected' do
977
+ subject.params do
978
+ optional :beer
979
+ optional :wine
980
+ optional :juice
981
+ exactly_one_of :beer, :wine, :juice
982
+ end
983
+ subject.get '/exactly_one_of' do
984
+ 'exactly_one_of works!'
985
+ end
986
+
987
+ get '/exactly_one_of'
988
+ expect(last_response.status).to eq(400)
989
+ expect(last_response.body).to eq("[:beer, :wine, :juice] - exactly one parameter must be provided")
990
+ end
838
991
  end
839
992
  end
840
993
  end