grape 1.3.3 → 1.6.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.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -2
  3. data/CONTRIBUTING.md +2 -1
  4. data/README.md +135 -23
  5. data/UPGRADING.md +237 -46
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +34 -42
  8. data/lib/grape/api.rb +21 -16
  9. data/lib/grape/cookies.rb +2 -0
  10. data/lib/grape/dsl/callbacks.rb +1 -1
  11. data/lib/grape/dsl/desc.rb +3 -5
  12. data/lib/grape/dsl/headers.rb +5 -2
  13. data/lib/grape/dsl/helpers.rb +8 -5
  14. data/lib/grape/dsl/inside_route.rb +72 -53
  15. data/lib/grape/dsl/middleware.rb +4 -4
  16. data/lib/grape/dsl/parameters.rb +11 -7
  17. data/lib/grape/dsl/request_response.rb +9 -6
  18. data/lib/grape/dsl/routing.rb +8 -9
  19. data/lib/grape/dsl/settings.rb +5 -5
  20. data/lib/grape/dsl/validations.rb +18 -1
  21. data/lib/grape/eager_load.rb +1 -1
  22. data/lib/grape/endpoint.rb +29 -42
  23. data/lib/grape/error_formatter/json.rb +2 -6
  24. data/lib/grape/error_formatter/xml.rb +2 -6
  25. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  26. data/lib/grape/exceptions/validation.rb +2 -3
  27. data/lib/grape/exceptions/validation_errors.rb +1 -1
  28. data/lib/grape/formatter/json.rb +1 -0
  29. data/lib/grape/formatter/serializable_hash.rb +2 -1
  30. data/lib/grape/formatter/xml.rb +1 -0
  31. data/lib/grape/locale/en.yml +1 -1
  32. data/lib/grape/middleware/auth/base.rb +3 -3
  33. data/lib/grape/middleware/auth/dsl.rb +7 -1
  34. data/lib/grape/middleware/base.rb +6 -3
  35. data/lib/grape/middleware/error.rb +11 -13
  36. data/lib/grape/middleware/formatter.rb +7 -7
  37. data/lib/grape/middleware/stack.rb +10 -3
  38. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  39. data/lib/grape/middleware/versioner/header.rb +6 -4
  40. data/lib/grape/middleware/versioner/param.rb +1 -0
  41. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  42. data/lib/grape/middleware/versioner/path.rb +2 -0
  43. data/lib/grape/parser/json.rb +1 -1
  44. data/lib/grape/parser/xml.rb +1 -1
  45. data/lib/grape/path.rb +1 -0
  46. data/lib/grape/request.rb +4 -1
  47. data/lib/grape/router/attribute_translator.rb +3 -3
  48. data/lib/grape/router/pattern.rb +1 -1
  49. data/lib/grape/router/route.rb +2 -2
  50. data/lib/grape/router.rb +31 -30
  51. data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
  52. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
  53. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
  54. data/lib/grape/util/base_inheritable.rb +2 -2
  55. data/lib/grape/util/inheritable_setting.rb +1 -3
  56. data/lib/grape/util/lazy_value.rb +4 -2
  57. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  58. data/lib/grape/validations/attributes_iterator.rb +8 -0
  59. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  60. data/lib/grape/validations/params_scope.rb +97 -62
  61. data/lib/grape/validations/single_attribute_iterator.rb +1 -1
  62. data/lib/grape/validations/types/custom_type_coercer.rb +16 -3
  63. data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
  64. data/lib/grape/validations/types/invalid_value.rb +24 -0
  65. data/lib/grape/validations/types/json.rb +2 -1
  66. data/lib/grape/validations/types/primitive_coercer.rb +4 -5
  67. data/lib/grape/validations/types.rb +1 -4
  68. data/lib/grape/validations/validator_factory.rb +1 -1
  69. data/lib/grape/validations/validators/all_or_none.rb +8 -5
  70. data/lib/grape/validations/validators/allow_blank.rb +9 -7
  71. data/lib/grape/validations/validators/as.rb +6 -8
  72. data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
  73. data/lib/grape/validations/validators/base.rb +74 -69
  74. data/lib/grape/validations/validators/coerce.rb +63 -76
  75. data/lib/grape/validations/validators/default.rb +36 -34
  76. data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
  77. data/lib/grape/validations/validators/except_values.rb +13 -11
  78. data/lib/grape/validations/validators/multiple_params_base.rb +24 -19
  79. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
  80. data/lib/grape/validations/validators/presence.rb +7 -4
  81. data/lib/grape/validations/validators/regexp.rb +8 -5
  82. data/lib/grape/validations/validators/same_as.rb +18 -15
  83. data/lib/grape/validations/validators/values.rb +61 -56
  84. data/lib/grape/validations.rb +6 -0
  85. data/lib/grape/version.rb +1 -1
  86. data/lib/grape.rb +7 -3
  87. data/spec/grape/api/custom_validations_spec.rb +77 -45
  88. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  89. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  90. data/spec/grape/api/invalid_format_spec.rb +2 -0
  91. data/spec/grape/api/recognize_path_spec.rb +1 -1
  92. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  93. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  94. data/spec/grape/api_remount_spec.rb +25 -19
  95. data/spec/grape/api_spec.rb +576 -211
  96. data/spec/grape/dsl/callbacks_spec.rb +2 -1
  97. data/spec/grape/dsl/headers_spec.rb +39 -9
  98. data/spec/grape/dsl/helpers_spec.rb +3 -2
  99. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  100. data/spec/grape/dsl/logger_spec.rb +16 -18
  101. data/spec/grape/dsl/middleware_spec.rb +2 -1
  102. data/spec/grape/dsl/parameters_spec.rb +2 -0
  103. data/spec/grape/dsl/request_response_spec.rb +1 -0
  104. data/spec/grape/dsl/routing_spec.rb +10 -7
  105. data/spec/grape/endpoint/declared_spec.rb +848 -0
  106. data/spec/grape/endpoint_spec.rb +77 -589
  107. data/spec/grape/entity_spec.rb +29 -23
  108. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  109. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  110. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  111. data/spec/grape/exceptions/validation_spec.rb +5 -3
  112. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  113. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  114. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  115. data/spec/grape/integration/rack_sendfile_spec.rb +13 -9
  116. data/spec/grape/loading_spec.rb +8 -8
  117. data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
  118. data/spec/grape/middleware/auth/strategies_spec.rb +61 -21
  119. data/spec/grape/middleware/base_spec.rb +24 -15
  120. data/spec/grape/middleware/error_spec.rb +3 -3
  121. data/spec/grape/middleware/exception_spec.rb +111 -161
  122. data/spec/grape/middleware/formatter_spec.rb +28 -7
  123. data/spec/grape/middleware/globals_spec.rb +7 -4
  124. data/spec/grape/middleware/stack_spec.rb +15 -12
  125. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  126. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  127. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  128. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  129. data/spec/grape/middleware/versioner_spec.rb +1 -1
  130. data/spec/grape/parser_spec.rb +4 -0
  131. data/spec/grape/path_spec.rb +52 -52
  132. data/spec/grape/presenters/presenter_spec.rb +7 -6
  133. data/spec/grape/request_spec.rb +6 -4
  134. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  135. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  136. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  137. data/spec/grape/util/stackable_values_spec.rb +7 -5
  138. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  139. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +14 -3
  140. data/spec/grape/validations/params_scope_spec.rb +72 -10
  141. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -6
  142. data/spec/grape/validations/types/primitive_coercer_spec.rb +63 -7
  143. data/spec/grape/validations/types_spec.rb +8 -8
  144. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  145. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  146. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  147. data/spec/grape/validations/validators/coerce_spec.rb +248 -33
  148. data/spec/grape/validations/validators/default_spec.rb +121 -78
  149. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  150. data/spec/grape/validations/validators/except_values_spec.rb +4 -3
  151. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  152. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  153. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  154. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  155. data/spec/grape/validations/validators/values_spec.rb +183 -178
  156. data/spec/grape/validations_spec.rb +342 -29
  157. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  158. data/spec/integration/multi_json/json_spec.rb +1 -1
  159. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  160. data/spec/shared/versioning_examples.rb +32 -29
  161. data/spec/spec_helper.rb +12 -12
  162. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  163. data/spec/support/chunks.rb +14 -0
  164. data/spec/support/versioned_helpers.rb +4 -6
  165. metadata +110 -102
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Grape::Validations::CoerceValidator do
5
+ describe Grape::Validations::Validators::CoerceValidator do
6
6
  subject do
7
7
  Class.new(Grape::API)
8
8
  end
@@ -23,7 +23,7 @@ describe Grape::Validations::CoerceValidator do
23
23
  end
24
24
 
25
25
  context 'i18n' do
26
- after :each do
26
+ after do
27
27
  I18n.available_locales = %i[en]
28
28
  I18n.locale = :en
29
29
  I18n.default_locale = :en
@@ -31,9 +31,9 @@ describe Grape::Validations::CoerceValidator do
31
31
 
32
32
  it 'i18n error on malformed input' do
33
33
  I18n.available_locales = %i[en zh-CN]
34
- I18n.load_path << File.expand_path('../zh-CN.yml', __FILE__)
34
+ I18n.load_path << File.expand_path('zh-CN.yml', __dir__)
35
35
  I18n.reload!
36
- I18n.locale = 'zh-CN'.to_sym
36
+ I18n.locale = :'zh-CN'
37
37
  subject.params do
38
38
  requires :age, type: Integer
39
39
  end
@@ -48,7 +48,7 @@ describe Grape::Validations::CoerceValidator do
48
48
 
49
49
  it 'gives an english fallback error when default locale message is blank' do
50
50
  I18n.available_locales = %i[en pt-BR]
51
- I18n.locale = 'pt-BR'.to_sym
51
+ I18n.locale = :'pt-BR'
52
52
  subject.params do
53
53
  requires :age, type: Integer
54
54
  end
@@ -83,10 +83,11 @@ describe Grape::Validations::CoerceValidator do
83
83
  context 'on custom coercion rules' do
84
84
  before do
85
85
  subject.params do
86
- requires :a, types: { value: [Boolean, String], message: 'type cast is invalid' }, coerce_with: (lambda do |val|
87
- if val == 'yup'
86
+ requires :a, types: { value: [Grape::API::Boolean, String], message: 'type cast is invalid' }, coerce_with: (lambda do |val|
87
+ case val
88
+ when 'yup'
88
89
  true
89
- elsif val == 'false'
90
+ when 'false'
90
91
  0
91
92
  else
92
93
  val
@@ -170,9 +171,9 @@ describe Grape::Validations::CoerceValidator do
170
171
  expect(last_response.body).to eq('BigDecimal 45.1')
171
172
  end
172
173
 
173
- it 'Boolean' do
174
+ it 'Grape::API::Boolean' do
174
175
  subject.params do
175
- requires :boolean, type: Boolean
176
+ requires :boolean, type: Grape::API::Boolean
176
177
  end
177
178
  subject.post '/boolean' do
178
179
  params[:boolean]
@@ -227,23 +228,51 @@ describe Grape::Validations::CoerceValidator do
227
228
  expect(last_response.body).to eq('NilClass')
228
229
  end
229
230
 
230
- it 'is a custom type' do
231
- subject.params do
232
- requires :uri, coerce: SecureURIOnly
233
- end
234
- subject.get '/secure_uri' do
235
- params[:uri].class
231
+ context 'a custom type' do
232
+ it 'coerces the given value' do
233
+ subject.params do
234
+ requires :uri, coerce: SecureURIOnly
235
+ end
236
+ subject.get '/secure_uri' do
237
+ params[:uri].class
238
+ end
239
+
240
+ get 'secure_uri', uri: 'https://www.example.com'
241
+
242
+ expect(last_response.status).to eq(200)
243
+ expect(last_response.body).to eq('URI::HTTPS')
244
+
245
+ get 'secure_uri', uri: 'http://www.example.com'
246
+
247
+ expect(last_response.status).to eq(400)
248
+ expect(last_response.body).to eq('uri is invalid')
236
249
  end
237
250
 
238
- get 'secure_uri', uri: 'https://www.example.com'
251
+ context 'returning the InvalidValue instance when invalid' do
252
+ let(:custom_type) do
253
+ Class.new do
254
+ def self.parse(_val)
255
+ Grape::Types::InvalidValue.new('must be unique')
256
+ end
257
+ end
258
+ end
239
259
 
240
- expect(last_response.status).to eq(200)
241
- expect(last_response.body).to eq('URI::HTTPS')
260
+ it 'uses a custom message added to the invalid value' do
261
+ type = custom_type
242
262
 
243
- get 'secure_uri', uri: 'http://www.example.com'
263
+ subject.params do
264
+ requires :name, type: type
265
+ end
266
+ subject.get '/whatever' do
267
+ params[:name].class
268
+ end
244
269
 
245
- expect(last_response.status).to eq(400)
246
- expect(last_response.body).to eq('uri is invalid')
270
+ get 'whatever', name: 'Bob'
271
+
272
+ expect(last_response.status).to eq(400)
273
+ expect(last_response.body).to eq('name must be unique')
274
+ end
275
+ end
247
276
  end
248
277
 
249
278
  context 'Array' do
@@ -341,9 +370,9 @@ describe Grape::Validations::CoerceValidator do
341
370
  end
342
371
  end
343
372
 
344
- it 'Boolean' do
373
+ it 'Grape::API::Boolean' do
345
374
  subject.params do
346
- requires :boolean, type: Boolean
375
+ requires :boolean, type: Grape::API::Boolean
347
376
  end
348
377
  subject.get '/boolean' do
349
378
  params[:boolean].class
@@ -497,6 +526,92 @@ describe Grape::Validations::CoerceValidator do
497
526
  end
498
527
  end
499
528
  end
529
+
530
+ context 'empty string' do
531
+ context 'primitive types' do
532
+ (Grape::Validations::Types::PRIMITIVES - [String]).each do |type|
533
+ it "is coerced to nil for type #{type}" do
534
+ subject.params do
535
+ requires :param, type: type
536
+ end
537
+ subject.get '/empty_string' do
538
+ params[:param].class
539
+ end
540
+
541
+ get '/empty_string', param: ''
542
+ expect(last_response.status).to eq(200)
543
+ expect(last_response.body).to eq('NilClass')
544
+ end
545
+ end
546
+
547
+ it 'is not coerced to nil for type String' do
548
+ subject.params do
549
+ requires :param, type: String
550
+ end
551
+ subject.get '/empty_string' do
552
+ params[:param].class
553
+ end
554
+
555
+ get '/empty_string', param: ''
556
+ expect(last_response.status).to eq(200)
557
+ expect(last_response.body).to eq('String')
558
+ end
559
+ end
560
+
561
+ context 'structures types' do
562
+ (Grape::Validations::Types::STRUCTURES - [Hash]).each do |type|
563
+ it "is coerced to nil for type #{type}" do
564
+ subject.params do
565
+ requires :param, type: type
566
+ end
567
+ subject.get '/empty_string' do
568
+ params[:param].class
569
+ end
570
+
571
+ get '/empty_string', param: ''
572
+ expect(last_response.status).to eq(200)
573
+ expect(last_response.body).to eq('NilClass')
574
+ end
575
+ end
576
+ end
577
+
578
+ context 'special types' do
579
+ (Grape::Validations::Types::SPECIAL.keys - [File, Rack::Multipart::UploadedFile]).each do |type|
580
+ it "is coerced to nil for type #{type}" do
581
+ subject.params do
582
+ requires :param, type: type
583
+ end
584
+ subject.get '/empty_string' do
585
+ params[:param].class
586
+ end
587
+
588
+ get '/empty_string', param: ''
589
+ expect(last_response.status).to eq(200)
590
+ expect(last_response.body).to eq('NilClass')
591
+ end
592
+ end
593
+
594
+ context 'variant-member-type collections' do
595
+ [
596
+ Array[Integer, String],
597
+ [Integer, String, Array[Integer, String]]
598
+ ].each do |type|
599
+ it "is coerced to nil for type #{type}" do
600
+ subject.params do
601
+ requires :param, type: type
602
+ end
603
+ subject.get '/empty_string' do
604
+ params[:param].class
605
+ end
606
+
607
+ get '/empty_string', param: ''
608
+ expect(last_response.status).to eq(200)
609
+ expect(last_response.body).to eq('NilClass')
610
+ end
611
+ end
612
+ end
613
+ end
614
+ end
500
615
  end
501
616
 
502
617
  context 'using coerce_with' do
@@ -534,6 +649,30 @@ describe Grape::Validations::CoerceValidator do
534
649
  expect(JSON.parse(last_response.body)).to eq(%w[a b c d])
535
650
  end
536
651
 
652
+ it 'parses parameters with Array[Array[String]] type and coerce_with' do
653
+ subject.params do
654
+ requires :values, type: Array[Array[String]], coerce_with: ->(val) { val.is_a?(String) ? [val.split(',').map(&:strip)] : val }
655
+ end
656
+ subject.post '/coerce_nested_strings' do
657
+ params[:values]
658
+ end
659
+
660
+ post '/coerce_nested_strings', ::Grape::Json.dump(values: 'a,b,c,d'), 'CONTENT_TYPE' => 'application/json'
661
+ expect(last_response.status).to eq(201)
662
+ expect(JSON.parse(last_response.body)).to eq([%w[a b c d]])
663
+
664
+ post '/coerce_nested_strings', ::Grape::Json.dump(values: [%w[a c], %w[b]]), 'CONTENT_TYPE' => 'application/json'
665
+ expect(last_response.status).to eq(201)
666
+ expect(JSON.parse(last_response.body)).to eq([%w[a c], %w[b]])
667
+
668
+ post '/coerce_nested_strings', ::Grape::Json.dump(values: [[]]), 'CONTENT_TYPE' => 'application/json'
669
+ expect(last_response.status).to eq(201)
670
+ expect(JSON.parse(last_response.body)).to eq([[]])
671
+
672
+ post '/coerce_nested_strings', ::Grape::Json.dump(values: [['a', { bar: 0 }], ['b']]), 'CONTENT_TYPE' => 'application/json'
673
+ expect(last_response.status).to eq(400)
674
+ end
675
+
537
676
  it 'parses parameters with Array[Integer] type' do
538
677
  subject.params do
539
678
  requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(/\s+/).map(&:to_i) }
@@ -568,6 +707,44 @@ describe Grape::Validations::CoerceValidator do
568
707
  expect(JSON.parse(last_response.body)).to eq([1, 1, 1, 1])
569
708
  end
570
709
 
710
+ context 'Array type and coerce_with should' do
711
+ before do
712
+ subject.params do
713
+ optional :arr, type: Array, coerce_with: (lambda do |val|
714
+ if val.nil?
715
+ []
716
+ else
717
+ val
718
+ end
719
+ end)
720
+ end
721
+ subject.get '/' do
722
+ params[:arr].class.to_s
723
+ end
724
+ end
725
+
726
+ it 'coerce nil value to array' do
727
+ get '/', arr: nil
728
+
729
+ expect(last_response.status).to eq(200)
730
+ expect(last_response.body).to eq('Array')
731
+ end
732
+
733
+ it 'not coerce missing field' do
734
+ get '/'
735
+
736
+ expect(last_response.status).to eq(200)
737
+ expect(last_response.body).to eq('NilClass')
738
+ end
739
+
740
+ it 'coerce array as array' do
741
+ get '/', arr: []
742
+
743
+ expect(last_response.status).to eq(200)
744
+ expect(last_response.body).to eq('Array')
745
+ end
746
+ end
747
+
571
748
  it 'uses parse where available' do
572
749
  subject.params do
573
750
  requires :ints, type: Array, coerce_with: JSON do
@@ -616,13 +793,52 @@ describe Grape::Validations::CoerceValidator do
616
793
  expect(last_response.body).to eq('3')
617
794
  end
618
795
 
796
+ context 'Integer type and coerce_with should' do
797
+ before do
798
+ subject.params do
799
+ optional :int, type: Integer, coerce_with: (lambda do |val|
800
+ if val.nil?
801
+ 0
802
+ else
803
+ val.to_i
804
+ end
805
+ end)
806
+ end
807
+ subject.get '/' do
808
+ params[:int].class.to_s
809
+ end
810
+ end
811
+
812
+ it 'coerce nil value to integer' do
813
+ get '/', int: nil
814
+
815
+ expect(last_response.status).to eq(200)
816
+ expect(last_response.body).to eq('Integer')
817
+ end
818
+
819
+ it 'not coerce missing field' do
820
+ get '/'
821
+
822
+ expect(last_response.status).to eq(200)
823
+ expect(last_response.body).to eq('NilClass')
824
+ end
825
+
826
+ it 'coerce integer as integer' do
827
+ get '/', int: 1
828
+
829
+ expect(last_response.status).to eq(200)
830
+ expect(last_response.body).to eq('Integer')
831
+ end
832
+ end
833
+
619
834
  context 'Integer type and coerce_with potentially returning nil' do
620
835
  before do
621
836
  subject.params do
622
837
  requires :int, type: Integer, coerce_with: (lambda do |val|
623
- if val == '0'
838
+ case val
839
+ when '0'
624
840
  nil
625
- elsif val.match?(/^-?\d+$/)
841
+ when /^-?\d+$/
626
842
  val.to_i
627
843
  else
628
844
  val
@@ -802,11 +1018,9 @@ describe Grape::Validations::CoerceValidator do
802
1018
  end
803
1019
 
804
1020
  context 'multiple types' do
805
- Boolean = Grape::API::Boolean
806
-
807
1021
  it 'coerces to first possible type' do
808
1022
  subject.params do
809
- requires :a, types: [Boolean, Integer, String]
1023
+ requires :a, types: [Grape::API::Boolean, Integer, String]
810
1024
  end
811
1025
  subject.get '/' do
812
1026
  params[:a].class.to_s
@@ -827,7 +1041,7 @@ describe Grape::Validations::CoerceValidator do
827
1041
 
828
1042
  it 'fails when no coercion is possible' do
829
1043
  subject.params do
830
- requires :a, types: [Boolean, Integer]
1044
+ requires :a, types: [Grape::API::Boolean, Integer]
831
1045
  end
832
1046
  subject.get '/' do
833
1047
  params[:a].class.to_s
@@ -986,10 +1200,11 @@ describe Grape::Validations::CoerceValidator do
986
1200
  context 'custom coercion rules' do
987
1201
  before do
988
1202
  subject.params do
989
- requires :a, types: [Boolean, String], coerce_with: (lambda do |val|
990
- if val == 'yup'
1203
+ requires :a, types: [Grape::API::Boolean, String], coerce_with: (lambda do |val|
1204
+ case val
1205
+ when 'yup'
991
1206
  true
992
- elsif val == 'false'
1207
+ when 'false'
993
1208
  0
994
1209
  else
995
1210
  val
@@ -2,104 +2,98 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Grape::Validations::DefaultValidator do
6
- module ValidationsSpec
7
- module DefaultValidatorSpec
8
- class API < Grape::API
9
- default_format :json
10
-
11
- params do
12
- optional :id
13
- optional :type, default: 'default-type'
14
- end
15
- get '/' do
16
- { id: params[:id], type: params[:type] }
17
- end
5
+ describe Grape::Validations::Validators::DefaultValidator do
6
+ let_it_be(:app) do
7
+ Class.new(Grape::API) do
8
+ default_format :json
9
+
10
+ params do
11
+ optional :id
12
+ optional :type, default: 'default-type'
13
+ end
14
+ get '/' do
15
+ { id: params[:id], type: params[:type] }
16
+ end
18
17
 
19
- params do
20
- optional :type1, default: 'default-type1'
21
- optional :type2, default: 'default-type2'
22
- end
23
- get '/user' do
24
- { type1: params[:type1], type2: params[:type2] }
25
- end
18
+ params do
19
+ optional :type1, default: 'default-type1'
20
+ optional :type2, default: 'default-type2'
21
+ end
22
+ get '/user' do
23
+ { type1: params[:type1], type2: params[:type2] }
24
+ end
26
25
 
27
- params do
28
- requires :id
29
- optional :type1, default: 'default-type1'
30
- optional :type2, default: 'default-type2'
31
- end
26
+ params do
27
+ requires :id
28
+ optional :type1, default: 'default-type1'
29
+ optional :type2, default: 'default-type2'
30
+ end
32
31
 
33
- get '/message' do
34
- { id: params[:id], type1: params[:type1], type2: params[:type2] }
35
- end
32
+ get '/message' do
33
+ { id: params[:id], type1: params[:type1], type2: params[:type2] }
34
+ end
36
35
 
37
- params do
38
- optional :random, default: -> { Random.rand }
39
- optional :not_random, default: Random.rand
40
- end
41
- get '/numbers' do
42
- { random_number: params[:random], non_random_number: params[:non_random_number] }
43
- end
36
+ params do
37
+ optional :random, default: -> { Random.rand }
38
+ optional :not_random, default: Random.rand
39
+ end
40
+ get '/numbers' do
41
+ { random_number: params[:random], non_random_number: params[:non_random_number] }
42
+ end
44
43
 
45
- params do
46
- optional :array, type: Array do
47
- requires :name
48
- optional :with_default, default: 'default'
49
- end
50
- end
51
- get '/array' do
52
- { array: params[:array] }
44
+ params do
45
+ optional :array, type: Array do
46
+ requires :name
47
+ optional :with_default, default: 'default'
53
48
  end
49
+ end
50
+ get '/array' do
51
+ { array: params[:array] }
52
+ end
54
53
 
55
- params do
56
- requires :thing1
57
- optional :more_things, type: Array do
58
- requires :nested_thing
59
- requires :other_thing, default: 1
60
- end
61
- end
62
- get '/optional_array' do
63
- { thing1: params[:thing1] }
54
+ params do
55
+ requires :thing1
56
+ optional :more_things, type: Array do
57
+ requires :nested_thing
58
+ requires :other_thing, default: 1
64
59
  end
60
+ end
61
+ get '/optional_array' do
62
+ { thing1: params[:thing1] }
63
+ end
65
64
 
66
- params do
67
- requires :root, type: Hash do
68
- optional :some_things, type: Array do
69
- requires :foo
70
- optional :options, type: Array do
71
- requires :name, type: String
72
- requires :value, type: String
73
- end
65
+ params do
66
+ requires :root, type: Hash do
67
+ optional :some_things, type: Array do
68
+ requires :foo
69
+ optional :options, type: Array do
70
+ requires :name, type: String
71
+ requires :value, type: String
74
72
  end
75
73
  end
76
74
  end
77
- get '/nested_optional_array' do
78
- { root: params[:root] }
79
- end
75
+ end
76
+ get '/nested_optional_array' do
77
+ { root: params[:root] }
78
+ end
80
79
 
81
- params do
82
- requires :root, type: Hash do
83
- optional :some_things, type: Array do
84
- requires :foo
85
- optional :options, type: Array do
86
- optional :name, type: String
87
- optional :value, type: String
88
- end
80
+ params do
81
+ requires :root, type: Hash do
82
+ optional :some_things, type: Array do
83
+ requires :foo
84
+ optional :options, type: Array do
85
+ optional :name, type: String
86
+ optional :value, type: String
89
87
  end
90
88
  end
91
89
  end
92
- get '/another_nested_optional_array' do
93
- { root: params[:root] }
94
- end
90
+ end
91
+ get '/another_nested_optional_array' do
92
+ { root: params[:root] }
95
93
  end
96
94
  end
97
95
  end
98
96
 
99
- def app
100
- ValidationsSpec::DefaultValidatorSpec::API
101
- end
102
-
103
97
  it 'lets you leave required values nested inside an optional blank' do
104
98
  get '/optional_array', thing1: 'stuff'
105
99
  expect(last_response.status).to eq(200)
@@ -419,4 +413,53 @@ describe Grape::Validations::DefaultValidator do
419
413
  end
420
414
  end
421
415
  end
416
+
417
+ context 'array with default values and given conditions' do
418
+ subject do
419
+ Class.new(Grape::API) do
420
+ default_format :json
421
+ end
422
+ end
423
+
424
+ def app
425
+ subject
426
+ end
427
+
428
+ it 'applies the default values only if the conditions are met' do
429
+ subject.params do
430
+ requires :ary, type: Array do
431
+ requires :has_value, type: Grape::API::Boolean
432
+ given has_value: ->(has_value) { has_value } do
433
+ optional :type, type: String, values: %w[str int], default: 'str'
434
+ given type: ->(type) { type == 'str' } do
435
+ optional :str, type: String, default: 'a'
436
+ end
437
+ given type: ->(type) { type == 'int' } do
438
+ optional :int, type: Integer, default: 1
439
+ end
440
+ end
441
+ end
442
+ end
443
+ subject.post('/nested_given_and_default') { declared(self.params) }
444
+
445
+ params = {
446
+ ary: [
447
+ { has_value: false },
448
+ { has_value: true, type: 'int', int: 123 },
449
+ { has_value: true, type: 'str', str: 'b' }
450
+ ]
451
+ }
452
+ expected = {
453
+ 'ary' => [
454
+ { 'has_value' => false, 'type' => nil, 'int' => nil, 'str' => nil },
455
+ { 'has_value' => true, 'type' => 'int', 'int' => 123, 'str' => nil },
456
+ { 'has_value' => true, 'type' => 'str', 'int' => nil, 'str' => 'b' }
457
+ ]
458
+ }
459
+
460
+ post '/nested_given_and_default', params
461
+ expect(last_response.status).to eq(201)
462
+ expect(JSON.parse(last_response.body)).to eq(expected)
463
+ end
464
+ end
422
465
  end