grape 1.3.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +119 -1
- data/LICENSE +1 -1
- data/README.md +123 -29
- data/UPGRADING.md +265 -39
- data/lib/grape/api/instance.rb +32 -31
- data/lib/grape/api.rb +5 -5
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/dsl/callbacks.rb +1 -1
- data/lib/grape/dsl/helpers.rb +2 -1
- data/lib/grape/dsl/inside_route.rb +77 -43
- data/lib/grape/dsl/parameters.rb +12 -8
- data/lib/grape/dsl/routing.rb +12 -11
- data/lib/grape/dsl/validations.rb +18 -1
- data/lib/grape/eager_load.rb +1 -1
- data/lib/grape/endpoint.rb +8 -6
- data/lib/grape/exceptions/base.rb +0 -4
- data/lib/grape/exceptions/validation.rb +1 -1
- data/lib/grape/exceptions/validation_errors.rb +12 -13
- data/lib/grape/http/headers.rb +26 -0
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/base.rb +4 -5
- data/lib/grape/middleware/error.rb +11 -13
- data/lib/grape/middleware/formatter.rb +3 -3
- data/lib/grape/middleware/stack.rb +10 -2
- data/lib/grape/middleware/versioner/header.rb +4 -4
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +12 -2
- data/lib/grape/path.rb +13 -3
- data/lib/grape/request.rb +13 -8
- data/lib/grape/router/attribute_translator.rb +26 -5
- data/lib/grape/router/pattern.rb +17 -16
- data/lib/grape/router/route.rb +5 -24
- data/lib/grape/router.rb +26 -30
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
- data/lib/grape/util/base_inheritable.rb +15 -8
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +1 -0
- data/lib/grape/util/reverse_stackable_values.rb +2 -0
- data/lib/grape/util/stackable_values.rb +7 -20
- data/lib/grape/validations/attributes_iterator.rb +8 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +10 -8
- data/lib/grape/validations/single_attribute_iterator.rb +1 -1
- data/lib/grape/validations/types/array_coercer.rb +14 -5
- data/lib/grape/validations/types/build_coercer.rb +5 -8
- data/lib/grape/validations/types/custom_type_coercer.rb +16 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +36 -1
- data/lib/grape/validations/types/file.rb +15 -12
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +40 -36
- data/lib/grape/validations/types/primitive_coercer.rb +15 -6
- data/lib/grape/validations/types/set_coercer.rb +6 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +1 -1
- data/lib/grape/validations/types.rb +7 -9
- data/lib/grape/validations/validator_factory.rb +1 -1
- data/lib/grape/validations/validators/as.rb +1 -1
- data/lib/grape/validations/validators/base.rb +8 -8
- data/lib/grape/validations/validators/coerce.rb +11 -15
- data/lib/grape/validations/validators/default.rb +3 -5
- data/lib/grape/validations/validators/exactly_one_of.rb +4 -2
- data/lib/grape/validations/validators/except_values.rb +1 -1
- data/lib/grape/validations/validators/multiple_params_base.rb +2 -1
- data/lib/grape/validations/validators/regexp.rb +1 -1
- data/lib/grape/validations/validators/values.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +5 -5
- data/spec/grape/api/instance_spec.rb +50 -0
- data/spec/grape/api_remount_spec.rb +9 -4
- data/spec/grape/api_spec.rb +82 -6
- data/spec/grape/dsl/inside_route_spec.rb +182 -33
- data/spec/grape/endpoint/declared_spec.rb +601 -0
- data/spec/grape/endpoint_spec.rb +0 -521
- data/spec/grape/entity_spec.rb +7 -1
- data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
- data/spec/grape/integration/rack_sendfile_spec.rb +12 -8
- data/spec/grape/middleware/auth/strategies_spec.rb +1 -1
- data/spec/grape/middleware/error_spec.rb +1 -1
- data/spec/grape/middleware/formatter_spec.rb +3 -3
- data/spec/grape/middleware/stack_spec.rb +10 -0
- data/spec/grape/path_spec.rb +4 -4
- data/spec/grape/request_spec.rb +1 -1
- data/spec/grape/validations/instance_behaivour_spec.rb +1 -1
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +13 -3
- data/spec/grape/validations/params_scope_spec.rb +26 -0
- data/spec/grape/validations/single_attribute_iterator_spec.rb +17 -6
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +1 -1
- data/spec/grape/validations/validators/coerce_spec.rb +366 -86
- data/spec/grape/validations/validators/default_spec.rb +170 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +12 -12
- data/spec/grape/validations/validators/except_values_spec.rb +1 -0
- data/spec/grape/validations/validators/values_spec.rb +1 -1
- data/spec/grape/validations_spec.rb +298 -30
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/shared/versioning_examples.rb +20 -20
- data/spec/spec_helper.rb +3 -10
- data/spec/support/chunks.rb +14 -0
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/versioned_helpers.rb +4 -6
- metadata +27 -10
- data/lib/grape/util/content_types.rb +0 -28
@@ -154,6 +154,49 @@ describe Grape::Validations::CoerceValidator do
|
|
154
154
|
end
|
155
155
|
|
156
156
|
context 'coerces' do
|
157
|
+
context 'json' do
|
158
|
+
let(:headers) { { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' } }
|
159
|
+
|
160
|
+
it 'BigDecimal' do
|
161
|
+
subject.params do
|
162
|
+
requires :bigdecimal, type: BigDecimal
|
163
|
+
end
|
164
|
+
subject.post '/bigdecimal' do
|
165
|
+
"#{params[:bigdecimal].class} #{params[:bigdecimal].to_f}"
|
166
|
+
end
|
167
|
+
|
168
|
+
post '/bigdecimal', { bigdecimal: 45.1 }.to_json, headers
|
169
|
+
expect(last_response.status).to eq(201)
|
170
|
+
expect(last_response.body).to eq('BigDecimal 45.1')
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'Boolean' do
|
174
|
+
subject.params do
|
175
|
+
requires :boolean, type: Boolean
|
176
|
+
end
|
177
|
+
subject.post '/boolean' do
|
178
|
+
params[:boolean]
|
179
|
+
end
|
180
|
+
|
181
|
+
post '/boolean', { boolean: 'true' }.to_json, headers
|
182
|
+
expect(last_response.status).to eq(201)
|
183
|
+
expect(last_response.body).to eq('true')
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'BigDecimal' do
|
188
|
+
subject.params do
|
189
|
+
requires :bigdecimal, coerce: BigDecimal
|
190
|
+
end
|
191
|
+
subject.get '/bigdecimal' do
|
192
|
+
params[:bigdecimal].class
|
193
|
+
end
|
194
|
+
|
195
|
+
get '/bigdecimal', bigdecimal: '45'
|
196
|
+
expect(last_response.status).to eq(200)
|
197
|
+
expect(last_response.body).to eq('BigDecimal')
|
198
|
+
end
|
199
|
+
|
157
200
|
it 'Integer' do
|
158
201
|
subject.params do
|
159
202
|
requires :int, coerce: Integer
|
@@ -167,23 +210,68 @@ describe Grape::Validations::CoerceValidator do
|
|
167
210
|
expect(last_response.body).to eq(integer_class_name)
|
168
211
|
end
|
169
212
|
|
170
|
-
it '
|
213
|
+
it 'String' do
|
171
214
|
subject.params do
|
172
|
-
requires :
|
215
|
+
requires :string, coerce: String
|
173
216
|
end
|
174
|
-
subject.get '/
|
175
|
-
params[:
|
217
|
+
subject.get '/string' do
|
218
|
+
params[:string].class
|
176
219
|
end
|
177
220
|
|
178
|
-
get '
|
221
|
+
get '/string', string: 45
|
222
|
+
expect(last_response.status).to eq(200)
|
223
|
+
expect(last_response.body).to eq('String')
|
179
224
|
|
225
|
+
get '/string', string: nil
|
180
226
|
expect(last_response.status).to eq(200)
|
181
|
-
expect(last_response.body).to eq('
|
227
|
+
expect(last_response.body).to eq('NilClass')
|
228
|
+
end
|
229
|
+
|
230
|
+
context 'a custom type' do
|
231
|
+
it 'coerces the given value' do
|
232
|
+
subject.params do
|
233
|
+
requires :uri, coerce: SecureURIOnly
|
234
|
+
end
|
235
|
+
subject.get '/secure_uri' do
|
236
|
+
params[:uri].class
|
237
|
+
end
|
182
238
|
|
183
|
-
|
239
|
+
get 'secure_uri', uri: 'https://www.example.com'
|
184
240
|
|
185
|
-
|
186
|
-
|
241
|
+
expect(last_response.status).to eq(200)
|
242
|
+
expect(last_response.body).to eq('URI::HTTPS')
|
243
|
+
|
244
|
+
get 'secure_uri', uri: 'http://www.example.com'
|
245
|
+
|
246
|
+
expect(last_response.status).to eq(400)
|
247
|
+
expect(last_response.body).to eq('uri is invalid')
|
248
|
+
end
|
249
|
+
|
250
|
+
context 'returning the InvalidValue instance when invalid' do
|
251
|
+
let(:custom_type) do
|
252
|
+
Class.new do
|
253
|
+
def self.parse(_val)
|
254
|
+
Grape::Types::InvalidValue.new('must be unique')
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'uses a custom message added to the invalid value' do
|
260
|
+
type = custom_type
|
261
|
+
|
262
|
+
subject.params do
|
263
|
+
requires :name, type: type
|
264
|
+
end
|
265
|
+
subject.get '/whatever' do
|
266
|
+
params[:name].class
|
267
|
+
end
|
268
|
+
|
269
|
+
get 'whatever', name: 'Bob'
|
270
|
+
|
271
|
+
expect(last_response.status).to eq(400)
|
272
|
+
expect(last_response.body).to eq('name must be unique')
|
273
|
+
end
|
274
|
+
end
|
187
275
|
end
|
188
276
|
|
189
277
|
context 'Array' do
|
@@ -281,119 +369,247 @@ describe Grape::Validations::CoerceValidator do
|
|
281
369
|
end
|
282
370
|
end
|
283
371
|
|
284
|
-
it '
|
372
|
+
it 'Boolean' do
|
285
373
|
subject.params do
|
286
|
-
requires :
|
374
|
+
requires :boolean, type: Boolean
|
287
375
|
end
|
288
|
-
subject.get '/
|
289
|
-
params[:
|
376
|
+
subject.get '/boolean' do
|
377
|
+
params[:boolean].class
|
290
378
|
end
|
291
379
|
|
292
|
-
get '/
|
380
|
+
get '/boolean', boolean: 1
|
293
381
|
expect(last_response.status).to eq(200)
|
294
382
|
expect(last_response.body).to eq('TrueClass')
|
383
|
+
end
|
295
384
|
|
296
|
-
|
297
|
-
|
298
|
-
|
385
|
+
context 'File' do
|
386
|
+
let(:file) { Rack::Test::UploadedFile.new(__FILE__) }
|
387
|
+
let(:filename) { File.basename(__FILE__).to_s }
|
299
388
|
|
300
|
-
|
301
|
-
|
302
|
-
|
389
|
+
it 'Rack::Multipart::UploadedFile' do
|
390
|
+
subject.params do
|
391
|
+
requires :file, type: Rack::Multipart::UploadedFile
|
392
|
+
end
|
393
|
+
subject.post '/upload' do
|
394
|
+
params[:file][:filename]
|
395
|
+
end
|
303
396
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
end
|
397
|
+
post '/upload', file: file
|
398
|
+
expect(last_response.status).to eq(201)
|
399
|
+
expect(last_response.body).to eq(filename)
|
308
400
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
end
|
313
|
-
subject.get '/boolean' do
|
314
|
-
params[:boolean].class
|
401
|
+
post '/upload', file: 'not a file'
|
402
|
+
expect(last_response.status).to eq(400)
|
403
|
+
expect(last_response.body).to eq('file is invalid')
|
315
404
|
end
|
316
405
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
406
|
+
it 'File' do
|
407
|
+
subject.params do
|
408
|
+
requires :file, coerce: File
|
409
|
+
end
|
410
|
+
subject.post '/upload' do
|
411
|
+
params[:file][:filename]
|
412
|
+
end
|
324
413
|
|
325
|
-
|
326
|
-
|
327
|
-
|
414
|
+
post '/upload', file: file
|
415
|
+
expect(last_response.status).to eq(201)
|
416
|
+
expect(last_response.body).to eq(filename)
|
328
417
|
|
329
|
-
|
330
|
-
|
331
|
-
|
418
|
+
post '/upload', file: 'not a file'
|
419
|
+
expect(last_response.status).to eq(400)
|
420
|
+
expect(last_response.body).to eq('file is invalid')
|
332
421
|
|
333
|
-
|
334
|
-
|
335
|
-
|
422
|
+
post '/upload', file: { filename: 'fake file', tempfile: '/etc/passwd' }
|
423
|
+
expect(last_response.status).to eq(400)
|
424
|
+
expect(last_response.body).to eq('file is invalid')
|
425
|
+
end
|
336
426
|
|
337
|
-
|
338
|
-
|
339
|
-
|
427
|
+
it 'collection' do
|
428
|
+
subject.params do
|
429
|
+
requires :files, type: Array[File]
|
430
|
+
end
|
431
|
+
subject.post '/upload' do
|
432
|
+
params[:files].first[:filename]
|
433
|
+
end
|
340
434
|
|
341
|
-
|
342
|
-
|
343
|
-
|
435
|
+
post '/upload', files: [file]
|
436
|
+
expect(last_response.status).to eq(201)
|
437
|
+
expect(last_response.body).to eq(filename)
|
438
|
+
end
|
344
439
|
end
|
345
440
|
|
346
|
-
it '
|
441
|
+
it 'Nests integers' do
|
347
442
|
subject.params do
|
348
|
-
requires :
|
443
|
+
requires :integers, type: Hash do
|
444
|
+
requires :int, coerce: Integer
|
445
|
+
end
|
349
446
|
end
|
350
|
-
subject.
|
351
|
-
params[:
|
447
|
+
subject.get '/int' do
|
448
|
+
params[:integers][:int].class
|
352
449
|
end
|
353
450
|
|
354
|
-
|
355
|
-
expect(last_response.status).to eq(
|
356
|
-
expect(last_response.body).to eq(
|
357
|
-
|
358
|
-
post '/upload', file: 'not a file'
|
359
|
-
expect(last_response.status).to eq(400)
|
360
|
-
expect(last_response.body).to eq('file is invalid')
|
451
|
+
get '/int', integers: { int: '45' }
|
452
|
+
expect(last_response.status).to eq(200)
|
453
|
+
expect(last_response.body).to eq(integer_class_name)
|
361
454
|
end
|
362
455
|
|
363
|
-
|
364
|
-
|
365
|
-
|
456
|
+
context 'nil values' do
|
457
|
+
context 'primitive types' do
|
458
|
+
Grape::Validations::Types::PRIMITIVES.each do |type|
|
459
|
+
it 'respects the nil value' do
|
460
|
+
subject.params do
|
461
|
+
requires :param, type: type
|
462
|
+
end
|
463
|
+
subject.get '/nil_value' do
|
464
|
+
params[:param].class
|
465
|
+
end
|
466
|
+
|
467
|
+
get '/nil_value', param: nil
|
468
|
+
expect(last_response.status).to eq(200)
|
469
|
+
expect(last_response.body).to eq('NilClass')
|
470
|
+
end
|
471
|
+
end
|
366
472
|
end
|
367
|
-
|
368
|
-
|
473
|
+
|
474
|
+
context 'structures types' do
|
475
|
+
Grape::Validations::Types::STRUCTURES.each do |type|
|
476
|
+
it 'respects the nil value' do
|
477
|
+
subject.params do
|
478
|
+
requires :param, type: type
|
479
|
+
end
|
480
|
+
subject.get '/nil_value' do
|
481
|
+
params[:param].class
|
482
|
+
end
|
483
|
+
|
484
|
+
get '/nil_value', param: nil
|
485
|
+
expect(last_response.status).to eq(200)
|
486
|
+
expect(last_response.body).to eq('NilClass')
|
487
|
+
end
|
488
|
+
end
|
369
489
|
end
|
370
490
|
|
371
|
-
|
372
|
-
|
373
|
-
|
491
|
+
context 'special types' do
|
492
|
+
Grape::Validations::Types::SPECIAL.each_key do |type|
|
493
|
+
it 'respects the nil value' do
|
494
|
+
subject.params do
|
495
|
+
requires :param, type: type
|
496
|
+
end
|
497
|
+
subject.get '/nil_value' do
|
498
|
+
params[:param].class
|
499
|
+
end
|
374
500
|
|
375
|
-
|
376
|
-
|
377
|
-
|
501
|
+
get '/nil_value', param: nil
|
502
|
+
expect(last_response.status).to eq(200)
|
503
|
+
expect(last_response.body).to eq('NilClass')
|
504
|
+
end
|
505
|
+
end
|
378
506
|
|
379
|
-
|
380
|
-
|
381
|
-
|
507
|
+
context 'variant-member-type collections' do
|
508
|
+
[
|
509
|
+
Array[Integer, String],
|
510
|
+
[Integer, String, Array[Integer, String]]
|
511
|
+
].each do |type|
|
512
|
+
it 'respects the nil value' do
|
513
|
+
subject.params do
|
514
|
+
requires :param, type: type
|
515
|
+
end
|
516
|
+
subject.get '/nil_value' do
|
517
|
+
params[:param].class
|
518
|
+
end
|
519
|
+
|
520
|
+
get '/nil_value', param: nil
|
521
|
+
expect(last_response.status).to eq(200)
|
522
|
+
expect(last_response.body).to eq('NilClass')
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
382
527
|
end
|
383
528
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
529
|
+
context 'empty string' do
|
530
|
+
context 'primitive types' do
|
531
|
+
(Grape::Validations::Types::PRIMITIVES - [String]).each do |type|
|
532
|
+
it "is coerced to nil for type #{type}" do
|
533
|
+
subject.params do
|
534
|
+
requires :param, type: type
|
535
|
+
end
|
536
|
+
subject.get '/empty_string' do
|
537
|
+
params[:param].class
|
538
|
+
end
|
539
|
+
|
540
|
+
get '/empty_string', param: ''
|
541
|
+
expect(last_response.status).to eq(200)
|
542
|
+
expect(last_response.body).to eq('NilClass')
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
it 'is not coerced to nil for type String' do
|
547
|
+
subject.params do
|
548
|
+
requires :param, type: String
|
549
|
+
end
|
550
|
+
subject.get '/empty_string' do
|
551
|
+
params[:param].class
|
552
|
+
end
|
553
|
+
|
554
|
+
get '/empty_string', param: ''
|
555
|
+
expect(last_response.status).to eq(200)
|
556
|
+
expect(last_response.body).to eq('String')
|
388
557
|
end
|
389
558
|
end
|
390
|
-
|
391
|
-
|
559
|
+
|
560
|
+
context 'structures types' do
|
561
|
+
(Grape::Validations::Types::STRUCTURES - [Hash]).each do |type|
|
562
|
+
it "is coerced to nil for type #{type}" do
|
563
|
+
subject.params do
|
564
|
+
requires :param, type: type
|
565
|
+
end
|
566
|
+
subject.get '/empty_string' do
|
567
|
+
params[:param].class
|
568
|
+
end
|
569
|
+
|
570
|
+
get '/empty_string', param: ''
|
571
|
+
expect(last_response.status).to eq(200)
|
572
|
+
expect(last_response.body).to eq('NilClass')
|
573
|
+
end
|
574
|
+
end
|
392
575
|
end
|
393
576
|
|
394
|
-
|
395
|
-
|
396
|
-
|
577
|
+
context 'special types' do
|
578
|
+
(Grape::Validations::Types::SPECIAL.keys - [File, Rack::Multipart::UploadedFile]).each do |type|
|
579
|
+
it "is coerced to nil for type #{type}" do
|
580
|
+
subject.params do
|
581
|
+
requires :param, type: type
|
582
|
+
end
|
583
|
+
subject.get '/empty_string' do
|
584
|
+
params[:param].class
|
585
|
+
end
|
586
|
+
|
587
|
+
get '/empty_string', param: ''
|
588
|
+
expect(last_response.status).to eq(200)
|
589
|
+
expect(last_response.body).to eq('NilClass')
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
context 'variant-member-type collections' do
|
594
|
+
[
|
595
|
+
Array[Integer, String],
|
596
|
+
[Integer, String, Array[Integer, String]]
|
597
|
+
].each do |type|
|
598
|
+
it "is coerced to nil for type #{type}" do
|
599
|
+
subject.params do
|
600
|
+
requires :param, type: type
|
601
|
+
end
|
602
|
+
subject.get '/empty_string' do
|
603
|
+
params[:param].class
|
604
|
+
end
|
605
|
+
|
606
|
+
get '/empty_string', param: ''
|
607
|
+
expect(last_response.status).to eq(200)
|
608
|
+
expect(last_response.body).to eq('NilClass')
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
397
613
|
end
|
398
614
|
end
|
399
615
|
|
@@ -432,6 +648,30 @@ describe Grape::Validations::CoerceValidator do
|
|
432
648
|
expect(JSON.parse(last_response.body)).to eq(%w[a b c d])
|
433
649
|
end
|
434
650
|
|
651
|
+
it 'parses parameters with Array[Array[String]] type and coerce_with' do
|
652
|
+
subject.params do
|
653
|
+
requires :values, type: Array[Array[String]], coerce_with: ->(val) { val.is_a?(String) ? [val.split(/,/).map(&:strip)] : val }
|
654
|
+
end
|
655
|
+
subject.post '/coerce_nested_strings' do
|
656
|
+
params[:values]
|
657
|
+
end
|
658
|
+
|
659
|
+
post '/coerce_nested_strings', ::Grape::Json.dump(values: 'a,b,c,d'), 'CONTENT_TYPE' => 'application/json'
|
660
|
+
expect(last_response.status).to eq(201)
|
661
|
+
expect(JSON.parse(last_response.body)).to eq([%w[a b c d]])
|
662
|
+
|
663
|
+
post '/coerce_nested_strings', ::Grape::Json.dump(values: [%w[a c], %w[b]]), 'CONTENT_TYPE' => 'application/json'
|
664
|
+
expect(last_response.status).to eq(201)
|
665
|
+
expect(JSON.parse(last_response.body)).to eq([%w[a c], %w[b]])
|
666
|
+
|
667
|
+
post '/coerce_nested_strings', ::Grape::Json.dump(values: [[]]), 'CONTENT_TYPE' => 'application/json'
|
668
|
+
expect(last_response.status).to eq(201)
|
669
|
+
expect(JSON.parse(last_response.body)).to eq([[]])
|
670
|
+
|
671
|
+
post '/coerce_nested_strings', ::Grape::Json.dump(values: [['a', { bar: 0 }], ['b']]), 'CONTENT_TYPE' => 'application/json'
|
672
|
+
expect(last_response.status).to eq(400)
|
673
|
+
end
|
674
|
+
|
435
675
|
it 'parses parameters with Array[Integer] type' do
|
436
676
|
subject.params do
|
437
677
|
requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(/\s+/).map(&:to_i) }
|
@@ -514,6 +754,46 @@ describe Grape::Validations::CoerceValidator do
|
|
514
754
|
expect(last_response.body).to eq('3')
|
515
755
|
end
|
516
756
|
|
757
|
+
context 'Integer type and coerce_with potentially returning nil' do
|
758
|
+
before do
|
759
|
+
subject.params do
|
760
|
+
requires :int, type: Integer, coerce_with: (lambda do |val|
|
761
|
+
if val == '0'
|
762
|
+
nil
|
763
|
+
elsif val.match?(/^-?\d+$/)
|
764
|
+
val.to_i
|
765
|
+
else
|
766
|
+
val
|
767
|
+
end
|
768
|
+
end)
|
769
|
+
end
|
770
|
+
subject.get '/' do
|
771
|
+
params[:int].class.to_s
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
it 'accepts value that coerces to nil' do
|
776
|
+
get '/', int: '0'
|
777
|
+
|
778
|
+
expect(last_response.status).to eq(200)
|
779
|
+
expect(last_response.body).to eq('NilClass')
|
780
|
+
end
|
781
|
+
|
782
|
+
it 'coerces to Integer' do
|
783
|
+
get '/', int: '1'
|
784
|
+
|
785
|
+
expect(last_response.status).to eq(200)
|
786
|
+
expect(last_response.body).to eq('Integer')
|
787
|
+
end
|
788
|
+
|
789
|
+
it 'returns invalid value if coercion returns a wrong type' do
|
790
|
+
get '/', int: 'lol'
|
791
|
+
|
792
|
+
expect(last_response.status).to eq(400)
|
793
|
+
expect(last_response.body).to eq('int is invalid')
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
517
797
|
it 'must be supplied with :type or :coerce' do
|
518
798
|
expect do
|
519
799
|
subject.params do
|