grape 1.7.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -1
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +30 -25
  5. data/UPGRADING.md +35 -0
  6. data/grape.gemspec +3 -6
  7. data/lib/grape/api.rb +2 -2
  8. data/lib/grape/content_types.rb +2 -8
  9. data/lib/grape/dsl/desc.rb +1 -1
  10. data/lib/grape/dsl/inside_route.rb +11 -11
  11. data/lib/grape/dsl/request_response.rb +2 -1
  12. data/lib/grape/dsl/settings.rb +2 -6
  13. data/lib/grape/endpoint.rb +28 -18
  14. data/lib/grape/error_formatter/base.rb +1 -1
  15. data/lib/grape/exceptions/base.rb +2 -2
  16. data/lib/grape/exceptions/missing_group_type.rb +1 -6
  17. data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
  18. data/lib/grape/exceptions/validation_errors.rb +1 -6
  19. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
  20. data/lib/grape/extensions/hash.rb +4 -7
  21. data/lib/grape/extensions/hashie/mash.rb +3 -3
  22. data/lib/grape/formatter/serializable_hash.rb +7 -7
  23. data/lib/grape/http/headers.rb +12 -2
  24. data/lib/grape/middleware/auth/base.rb +1 -1
  25. data/lib/grape/middleware/auth/strategies.rb +1 -2
  26. data/lib/grape/middleware/error.rb +5 -5
  27. data/lib/grape/middleware/formatter.rb +6 -6
  28. data/lib/grape/middleware/versioner/header.rb +11 -19
  29. data/lib/grape/railtie.rb +9 -0
  30. data/lib/grape/request.rb +8 -2
  31. data/lib/grape/router/route.rb +1 -3
  32. data/lib/grape/util/lazy_value.rb +3 -11
  33. data/lib/grape/util/strict_hash_configuration.rb +3 -4
  34. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  35. data/lib/grape/validations/params_scope.rb +8 -2
  36. data/lib/grape/validations/single_attribute_iterator.rb +3 -1
  37. data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
  38. data/lib/grape/validations/validators/base.rb +9 -20
  39. data/lib/grape/validations/validators/default_validator.rb +2 -20
  40. data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
  41. data/lib/grape/validations/validators/values_validator.rb +14 -5
  42. data/lib/grape/version.rb +1 -1
  43. data/lib/grape.rb +26 -5
  44. metadata +13 -253
  45. data/lib/grape/config.rb +0 -34
  46. data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
  47. data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
  48. data/spec/grape/api/custom_validations_spec.rb +0 -256
  49. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  50. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  51. data/spec/grape/api/documentation_spec.rb +0 -59
  52. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  53. data/spec/grape/api/instance_spec.rb +0 -103
  54. data/spec/grape/api/invalid_format_spec.rb +0 -45
  55. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  56. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  57. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  58. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  59. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  60. data/spec/grape/api/recognize_path_spec.rb +0 -21
  61. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  62. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  63. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  64. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  65. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  66. data/spec/grape/api_remount_spec.rb +0 -473
  67. data/spec/grape/api_spec.rb +0 -4347
  68. data/spec/grape/config_spec.rb +0 -17
  69. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  70. data/spec/grape/dsl/desc_spec.rb +0 -101
  71. data/spec/grape/dsl/headers_spec.rb +0 -62
  72. data/spec/grape/dsl/helpers_spec.rb +0 -100
  73. data/spec/grape/dsl/inside_route_spec.rb +0 -535
  74. data/spec/grape/dsl/logger_spec.rb +0 -24
  75. data/spec/grape/dsl/middleware_spec.rb +0 -60
  76. data/spec/grape/dsl/parameters_spec.rb +0 -180
  77. data/spec/grape/dsl/request_response_spec.rb +0 -206
  78. data/spec/grape/dsl/routing_spec.rb +0 -275
  79. data/spec/grape/dsl/settings_spec.rb +0 -261
  80. data/spec/grape/dsl/validations_spec.rb +0 -55
  81. data/spec/grape/endpoint/declared_spec.rb +0 -846
  82. data/spec/grape/endpoint_spec.rb +0 -1085
  83. data/spec/grape/entity_spec.rb +0 -336
  84. data/spec/grape/exceptions/base_spec.rb +0 -81
  85. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -145
  86. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  87. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  88. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  89. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  90. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -21
  91. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  92. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  93. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  94. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  95. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -23
  96. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  97. data/spec/grape/exceptions/validation_spec.rb +0 -19
  98. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  99. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  100. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  101. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  102. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  103. data/spec/grape/integration/rack_spec.rb +0 -51
  104. data/spec/grape/loading_spec.rb +0 -44
  105. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  106. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  107. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  108. data/spec/grape/middleware/base_spec.rb +0 -221
  109. data/spec/grape/middleware/error_spec.rb +0 -85
  110. data/spec/grape/middleware/exception_spec.rb +0 -294
  111. data/spec/grape/middleware/formatter_spec.rb +0 -461
  112. data/spec/grape/middleware/globals_spec.rb +0 -30
  113. data/spec/grape/middleware/stack_spec.rb +0 -155
  114. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  115. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  116. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  117. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  118. data/spec/grape/middleware/versioner_spec.rb +0 -21
  119. data/spec/grape/named_api_spec.rb +0 -19
  120. data/spec/grape/parser_spec.rb +0 -86
  121. data/spec/grape/path_spec.rb +0 -252
  122. data/spec/grape/presenters/presenter_spec.rb +0 -71
  123. data/spec/grape/request_spec.rb +0 -136
  124. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  125. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  126. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  127. data/spec/grape/util/stackable_values_spec.rb +0 -128
  128. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  129. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  130. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  131. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -40
  132. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  133. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -57
  134. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  135. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  136. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  137. data/spec/grape/validations/types_spec.rb +0 -111
  138. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  139. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  140. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  141. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  142. data/spec/grape/validations/validators/default_spec.rb +0 -463
  143. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  144. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  145. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  146. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  147. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  148. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  149. data/spec/grape/validations/validators/values_spec.rb +0 -696
  150. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  151. data/spec/grape/validations_spec.rb +0 -2029
  152. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  153. data/spec/integration/multi_json/json_spec.rb +0 -7
  154. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  155. data/spec/shared/versioning_examples.rb +0 -215
  156. data/spec/spec_helper.rb +0 -52
  157. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  158. data/spec/support/chunks.rb +0 -14
  159. data/spec/support/content_type_helpers.rb +0 -15
  160. data/spec/support/endpoint_faker.rb +0 -25
  161. data/spec/support/file_streamer.rb +0 -13
  162. data/spec/support/integer_helpers.rb +0 -13
  163. data/spec/support/versioned_helpers.rb +0 -55
@@ -1,1261 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- describe Grape::Validations::Validators::CoerceValidator do
4
- subject do
5
- Class.new(Grape::API)
6
- end
7
-
8
- def app
9
- subject
10
- end
11
-
12
- describe 'coerce' do
13
- class SecureURIOnly
14
- def self.parse(value)
15
- URI.parse(value)
16
- end
17
-
18
- def self.parsed?(value)
19
- value.is_a? URI::HTTPS
20
- end
21
- end
22
-
23
- context 'i18n' do
24
- after do
25
- I18n.available_locales = %i[en]
26
- I18n.locale = :en
27
- I18n.default_locale = :en
28
- end
29
-
30
- it 'i18n error on malformed input' do
31
- I18n.available_locales = %i[en zh-CN]
32
- I18n.load_path << File.expand_path('zh-CN.yml', __dir__)
33
- I18n.reload!
34
- I18n.locale = :'zh-CN'
35
- subject.params do
36
- requires :age, type: Integer
37
- end
38
- subject.get '/single' do
39
- 'int works'
40
- end
41
-
42
- get '/single', age: '43a'
43
- expect(last_response.status).to eq(400)
44
- expect(last_response.body).to eq('年龄格式不正确')
45
- end
46
-
47
- it 'gives an english fallback error when default locale message is blank' do
48
- I18n.available_locales = %i[en pt-BR]
49
- I18n.locale = :'pt-BR'
50
- subject.params do
51
- requires :age, type: Integer
52
- end
53
- subject.get '/single' do
54
- 'int works'
55
- end
56
-
57
- get '/single', age: '43a'
58
- expect(last_response.status).to eq(400)
59
- expect(last_response.body).to eq('age is invalid')
60
- end
61
- end
62
-
63
- context 'with a custom validation message' do
64
- it 'errors on malformed input' do
65
- subject.params do
66
- requires :int, type: { value: Integer, message: 'type cast is invalid' }
67
- end
68
- subject.get '/single' do
69
- 'int works'
70
- end
71
-
72
- get '/single', int: '43a'
73
- expect(last_response.status).to eq(400)
74
- expect(last_response.body).to eq('int type cast is invalid')
75
-
76
- get '/single', int: '43'
77
- expect(last_response.status).to eq(200)
78
- expect(last_response.body).to eq('int works')
79
- end
80
-
81
- context 'on custom coercion rules' do
82
- before do
83
- subject.params do
84
- requires :a, types: { value: [Grape::API::Boolean, String], message: 'type cast is invalid' }, coerce_with: (lambda do |val|
85
- case val
86
- when 'yup'
87
- true
88
- when 'false'
89
- 0
90
- else
91
- val
92
- end
93
- end)
94
- end
95
- subject.get '/' do
96
- params[:a].class.to_s
97
- end
98
- end
99
-
100
- it 'respects :coerce_with' do
101
- get '/', a: 'yup'
102
-
103
- expect(last_response.status).to eq(200)
104
- expect(last_response.body).to eq('TrueClass')
105
- end
106
-
107
- it 'still validates type' do
108
- get '/', a: 'false'
109
- expect(last_response.status).to eq(400)
110
- expect(last_response.body).to eq('a type cast is invalid')
111
- end
112
-
113
- it 'performs no additional coercion' do
114
- get '/', a: 'true'
115
- expect(last_response.status).to eq(200)
116
- expect(last_response.body).to eq('String')
117
- end
118
- end
119
- end
120
-
121
- it 'error on malformed input' do
122
- subject.params do
123
- requires :int, type: Integer
124
- end
125
- subject.get '/single' do
126
- 'int works'
127
- end
128
-
129
- get '/single', int: '43a'
130
- expect(last_response.status).to eq(400)
131
- expect(last_response.body).to eq('int is invalid')
132
-
133
- get '/single', int: '43'
134
- expect(last_response.status).to eq(200)
135
- expect(last_response.body).to eq('int works')
136
- end
137
-
138
- it 'error on malformed input (Array)' do
139
- subject.params do
140
- requires :ids, type: Array[Integer]
141
- end
142
- subject.get '/array' do
143
- 'array int works'
144
- end
145
-
146
- get 'array', ids: %w[1 2 az]
147
- expect(last_response.status).to eq(400)
148
- expect(last_response.body).to eq('ids is invalid')
149
-
150
- get 'array', ids: %w[1 2 890]
151
- expect(last_response.status).to eq(200)
152
- expect(last_response.body).to eq('array int works')
153
- end
154
-
155
- context 'coerces' do
156
- context 'json' do
157
- let(:headers) { { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' } }
158
-
159
- it 'BigDecimal' do
160
- subject.params do
161
- requires :bigdecimal, type: BigDecimal
162
- end
163
- subject.post '/bigdecimal' do
164
- "#{params[:bigdecimal].class} #{params[:bigdecimal].to_f}"
165
- end
166
-
167
- post '/bigdecimal', { bigdecimal: 45.1 }.to_json, headers
168
- expect(last_response.status).to eq(201)
169
- expect(last_response.body).to eq('BigDecimal 45.1')
170
- end
171
-
172
- it 'Grape::API::Boolean' do
173
- subject.params do
174
- requires :boolean, type: Grape::API::Boolean
175
- end
176
- subject.post '/boolean' do
177
- params[:boolean]
178
- end
179
-
180
- post '/boolean', { boolean: 'true' }.to_json, headers
181
- expect(last_response.status).to eq(201)
182
- expect(last_response.body).to eq('true')
183
- end
184
- end
185
-
186
- it 'BigDecimal' do
187
- subject.params do
188
- requires :bigdecimal, coerce: BigDecimal
189
- end
190
- subject.get '/bigdecimal' do
191
- params[:bigdecimal].class
192
- end
193
-
194
- get '/bigdecimal', bigdecimal: '45'
195
- expect(last_response.status).to eq(200)
196
- expect(last_response.body).to eq('BigDecimal')
197
- end
198
-
199
- it 'Integer' do
200
- subject.params do
201
- requires :int, coerce: Integer
202
- end
203
- subject.get '/int' do
204
- params[:int].class
205
- end
206
-
207
- get '/int', int: '45'
208
- expect(last_response.status).to eq(200)
209
- expect(last_response.body).to eq(integer_class_name)
210
- end
211
-
212
- it 'String' do
213
- subject.params do
214
- requires :string, coerce: String
215
- end
216
- subject.get '/string' do
217
- params[:string].class
218
- end
219
-
220
- get '/string', string: 45
221
- expect(last_response.status).to eq(200)
222
- expect(last_response.body).to eq('String')
223
-
224
- get '/string', string: nil
225
- expect(last_response.status).to eq(200)
226
- expect(last_response.body).to eq('NilClass')
227
- end
228
-
229
- context 'a custom type' do
230
- it 'coerces the given value' do
231
- subject.params do
232
- requires :uri, coerce: SecureURIOnly
233
- end
234
- subject.get '/secure_uri' do
235
- params[:uri].class
236
- end
237
-
238
- get 'secure_uri', uri: 'https://www.example.com'
239
-
240
- expect(last_response.status).to eq(200)
241
- expect(last_response.body).to eq('URI::HTTPS')
242
-
243
- get 'secure_uri', uri: 'http://www.example.com'
244
-
245
- expect(last_response.status).to eq(400)
246
- expect(last_response.body).to eq('uri is invalid')
247
- end
248
-
249
- context 'returning the InvalidValue instance when invalid' do
250
- let(:custom_type) do
251
- Class.new do
252
- def self.parse(_val)
253
- Grape::Types::InvalidValue.new('must be unique')
254
- end
255
- end
256
- end
257
-
258
- it 'uses a custom message added to the invalid value' do
259
- type = custom_type
260
-
261
- subject.params do
262
- requires :name, type: type
263
- end
264
- subject.get '/whatever' do
265
- params[:name].class
266
- end
267
-
268
- get 'whatever', name: 'Bob'
269
-
270
- expect(last_response.status).to eq(400)
271
- expect(last_response.body).to eq('name must be unique')
272
- end
273
- end
274
- end
275
-
276
- context 'Array' do
277
- it 'Array of Integers' do
278
- subject.params do
279
- requires :arry, coerce: Array[Integer]
280
- end
281
- subject.get '/array' do
282
- params[:arry][0].class
283
- end
284
-
285
- get '/array', arry: %w[1 2 3]
286
- expect(last_response.status).to eq(200)
287
- expect(last_response.body).to eq(integer_class_name)
288
- end
289
-
290
- it 'Array of Bools' do
291
- subject.params do
292
- requires :arry, coerce: Array[Grape::API::Boolean]
293
- end
294
- subject.get '/array' do
295
- params[:arry][0].class
296
- end
297
-
298
- get 'array', arry: [1, 0]
299
- expect(last_response.status).to eq(200)
300
- expect(last_response.body).to eq('TrueClass')
301
- end
302
-
303
- it 'Array of type implementing parse' do
304
- subject.params do
305
- requires :uri, type: Array[URI]
306
- end
307
- subject.get '/uri_array' do
308
- params[:uri][0].class
309
- end
310
- get 'uri_array', uri: ['http://www.google.com']
311
- expect(last_response.status).to eq(200)
312
- expect(last_response.body).to eq('URI::HTTP')
313
- end
314
-
315
- it 'Set of type implementing parse' do
316
- subject.params do
317
- requires :uri, type: Set[URI]
318
- end
319
- subject.get '/uri_array' do
320
- "#{params[:uri].class},#{params[:uri].first.class},#{params[:uri].size}"
321
- end
322
- get 'uri_array', uri: Array.new(2) { 'http://www.example.com' }
323
- expect(last_response.status).to eq(200)
324
- expect(last_response.body).to eq('Set,URI::HTTP,1')
325
- end
326
-
327
- it 'Array of a custom type' do
328
- subject.params do
329
- requires :uri, type: Array[SecureURIOnly]
330
- end
331
- subject.get '/secure_uris' do
332
- params[:uri].first.class
333
- end
334
- get 'secure_uris', uri: ['https://www.example.com']
335
- expect(last_response.status).to eq(200)
336
- expect(last_response.body).to eq('URI::HTTPS')
337
- get 'secure_uris', uri: ['https://www.example.com', 'http://www.example.com']
338
- expect(last_response.status).to eq(400)
339
- expect(last_response.body).to eq('uri is invalid')
340
- end
341
- end
342
-
343
- context 'Set' do
344
- it 'Set of Integers' do
345
- subject.params do
346
- requires :set, coerce: Set[Integer]
347
- end
348
- subject.get '/set' do
349
- params[:set].first.class
350
- end
351
-
352
- get '/set', set: Set.new([1, 2, 3, 4]).to_a
353
- expect(last_response.status).to eq(200)
354
- expect(last_response.body).to eq(integer_class_name)
355
- end
356
-
357
- it 'Set of Bools' do
358
- subject.params do
359
- requires :set, coerce: Set[Grape::API::Boolean]
360
- end
361
- subject.get '/set' do
362
- params[:set].first.class
363
- end
364
-
365
- get '/set', set: Set.new([1, 0]).to_a
366
- expect(last_response.status).to eq(200)
367
- expect(last_response.body).to eq('TrueClass')
368
- end
369
- end
370
-
371
- it 'Grape::API::Boolean' do
372
- subject.params do
373
- requires :boolean, type: Grape::API::Boolean
374
- end
375
- subject.get '/boolean' do
376
- params[:boolean].class
377
- end
378
-
379
- get '/boolean', boolean: 1
380
- expect(last_response.status).to eq(200)
381
- expect(last_response.body).to eq('TrueClass')
382
- end
383
-
384
- context 'File' do
385
- let(:file) { Rack::Test::UploadedFile.new(__FILE__) }
386
- let(:filename) { File.basename(__FILE__).to_s }
387
-
388
- it 'Rack::Multipart::UploadedFile' do
389
- subject.params do
390
- requires :file, type: Rack::Multipart::UploadedFile
391
- end
392
- subject.post '/upload' do
393
- params[:file][:filename]
394
- end
395
-
396
- post '/upload', file: file
397
- expect(last_response.status).to eq(201)
398
- expect(last_response.body).to eq(filename)
399
-
400
- post '/upload', file: 'not a file'
401
- expect(last_response.status).to eq(400)
402
- expect(last_response.body).to eq('file is invalid')
403
- end
404
-
405
- it 'File' do
406
- subject.params do
407
- requires :file, coerce: File
408
- end
409
- subject.post '/upload' do
410
- params[:file][:filename]
411
- end
412
-
413
- post '/upload', file: file
414
- expect(last_response.status).to eq(201)
415
- expect(last_response.body).to eq(filename)
416
-
417
- post '/upload', file: 'not a file'
418
- expect(last_response.status).to eq(400)
419
- expect(last_response.body).to eq('file is invalid')
420
-
421
- post '/upload', file: { filename: 'fake file', tempfile: '/etc/passwd' }
422
- expect(last_response.status).to eq(400)
423
- expect(last_response.body).to eq('file is invalid')
424
- end
425
-
426
- it 'collection' do
427
- subject.params do
428
- requires :files, type: Array[File]
429
- end
430
- subject.post '/upload' do
431
- params[:files].first[:filename]
432
- end
433
-
434
- post '/upload', files: [file]
435
- expect(last_response.status).to eq(201)
436
- expect(last_response.body).to eq(filename)
437
- end
438
- end
439
-
440
- it 'Nests integers' do
441
- subject.params do
442
- requires :integers, type: Hash do
443
- requires :int, coerce: Integer
444
- end
445
- end
446
- subject.get '/int' do
447
- params[:integers][:int].class
448
- end
449
-
450
- get '/int', integers: { int: '45' }
451
- expect(last_response.status).to eq(200)
452
- expect(last_response.body).to eq(integer_class_name)
453
- end
454
-
455
- context 'nil values' do
456
- context 'primitive types' do
457
- Grape::Validations::Types::PRIMITIVES.each do |type|
458
- it 'respects the nil value' do
459
- subject.params do
460
- requires :param, type: type
461
- end
462
- subject.get '/nil_value' do
463
- params[:param].class
464
- end
465
-
466
- get '/nil_value', param: nil
467
- expect(last_response.status).to eq(200)
468
- expect(last_response.body).to eq('NilClass')
469
- end
470
- end
471
- end
472
-
473
- context 'structures types' do
474
- Grape::Validations::Types::STRUCTURES.each do |type|
475
- it 'respects the nil value' do
476
- subject.params do
477
- requires :param, type: type
478
- end
479
- subject.get '/nil_value' do
480
- params[:param].class
481
- end
482
-
483
- get '/nil_value', param: nil
484
- expect(last_response.status).to eq(200)
485
- expect(last_response.body).to eq('NilClass')
486
- end
487
- end
488
- end
489
-
490
- context 'special types' do
491
- Grape::Validations::Types::SPECIAL.each_key do |type|
492
- it 'respects the nil value' do
493
- subject.params do
494
- requires :param, type: type
495
- end
496
- subject.get '/nil_value' do
497
- params[:param].class
498
- end
499
-
500
- get '/nil_value', param: nil
501
- expect(last_response.status).to eq(200)
502
- expect(last_response.body).to eq('NilClass')
503
- end
504
- end
505
-
506
- context 'variant-member-type collections' do
507
- [
508
- Array[Integer, String],
509
- [Integer, String, Array[Integer, String]]
510
- ].each do |type|
511
- it 'respects the nil value' do
512
- subject.params do
513
- requires :param, type: type
514
- end
515
- subject.get '/nil_value' do
516
- params[:param].class
517
- end
518
-
519
- get '/nil_value', param: nil
520
- expect(last_response.status).to eq(200)
521
- expect(last_response.body).to eq('NilClass')
522
- end
523
- end
524
- end
525
- end
526
- end
527
-
528
- context 'empty string' do
529
- context 'primitive types' do
530
- (Grape::Validations::Types::PRIMITIVES - [String]).each do |type|
531
- it "is coerced to nil for type #{type}" do
532
- subject.params do
533
- requires :param, type: type
534
- end
535
- subject.get '/empty_string' do
536
- params[:param].class
537
- end
538
-
539
- get '/empty_string', param: ''
540
- expect(last_response.status).to eq(200)
541
- expect(last_response.body).to eq('NilClass')
542
- end
543
- end
544
-
545
- it 'is not coerced to nil for type String' do
546
- subject.params do
547
- requires :param, type: String
548
- end
549
- subject.get '/empty_string' do
550
- params[:param].class
551
- end
552
-
553
- get '/empty_string', param: ''
554
- expect(last_response.status).to eq(200)
555
- expect(last_response.body).to eq('String')
556
- end
557
- end
558
-
559
- context 'structures types' do
560
- (Grape::Validations::Types::STRUCTURES - [Hash]).each do |type|
561
- it "is coerced to nil for type #{type}" do
562
- subject.params do
563
- requires :param, type: type
564
- end
565
- subject.get '/empty_string' do
566
- params[:param].class
567
- end
568
-
569
- get '/empty_string', param: ''
570
- expect(last_response.status).to eq(200)
571
- expect(last_response.body).to eq('NilClass')
572
- end
573
- end
574
- end
575
-
576
- context 'special types' do
577
- (Grape::Validations::Types::SPECIAL.keys - [File, Rack::Multipart::UploadedFile]).each do |type|
578
- it "is coerced to nil for type #{type}" do
579
- subject.params do
580
- requires :param, type: type
581
- end
582
- subject.get '/empty_string' do
583
- params[:param].class
584
- end
585
-
586
- get '/empty_string', param: ''
587
- expect(last_response.status).to eq(200)
588
- expect(last_response.body).to eq('NilClass')
589
- end
590
- end
591
-
592
- context 'variant-member-type collections' do
593
- [
594
- Array[Integer, String],
595
- [Integer, String, Array[Integer, String]]
596
- ].each do |type|
597
- it "is coerced to nil for type #{type}" do
598
- subject.params do
599
- requires :param, type: type
600
- end
601
- subject.get '/empty_string' do
602
- params[:param].class
603
- end
604
-
605
- get '/empty_string', param: ''
606
- expect(last_response.status).to eq(200)
607
- expect(last_response.body).to eq('NilClass')
608
- end
609
- end
610
- end
611
- end
612
- end
613
- end
614
-
615
- context 'using coerce_with' do
616
- it 'parses parameters with Array type' do
617
- subject.params do
618
- requires :values, type: Array, coerce_with: ->(val) { val.split(/\s+/).map(&:to_i) }
619
- end
620
- subject.get '/ints' do
621
- params[:values]
622
- end
623
-
624
- get '/ints', values: '1 2 3 4'
625
- expect(last_response.status).to eq(200)
626
- expect(JSON.parse(last_response.body)).to eq([1, 2, 3, 4])
627
-
628
- get '/ints', values: 'a b c d'
629
- expect(last_response.status).to eq(200)
630
- expect(JSON.parse(last_response.body)).to eq([0, 0, 0, 0])
631
- end
632
-
633
- it 'parses parameters with Array[String] type' do
634
- subject.params do
635
- requires :values, type: Array[String], coerce_with: ->(val) { val.split(/\s+/) }
636
- end
637
- subject.get '/strings' do
638
- params[:values]
639
- end
640
-
641
- get '/strings', values: '1 2 3 4'
642
- expect(last_response.status).to eq(200)
643
- expect(JSON.parse(last_response.body)).to eq(%w[1 2 3 4])
644
-
645
- get '/strings', values: 'a b c d'
646
- expect(last_response.status).to eq(200)
647
- expect(JSON.parse(last_response.body)).to eq(%w[a b c d])
648
- end
649
-
650
- it 'parses parameters with Array[Array[String]] type and coerce_with' do
651
- subject.params do
652
- requires :values, type: Array[Array[String]], coerce_with: ->(val) { val.is_a?(String) ? [val.split(',').map(&:strip)] : val }
653
- end
654
- subject.post '/coerce_nested_strings' do
655
- params[:values]
656
- end
657
-
658
- post '/coerce_nested_strings', ::Grape::Json.dump(values: 'a,b,c,d'), 'CONTENT_TYPE' => 'application/json'
659
- expect(last_response.status).to eq(201)
660
- expect(JSON.parse(last_response.body)).to eq([%w[a b c d]])
661
-
662
- post '/coerce_nested_strings', ::Grape::Json.dump(values: [%w[a c], %w[b]]), 'CONTENT_TYPE' => 'application/json'
663
- expect(last_response.status).to eq(201)
664
- expect(JSON.parse(last_response.body)).to eq([%w[a c], %w[b]])
665
-
666
- post '/coerce_nested_strings', ::Grape::Json.dump(values: [[]]), 'CONTENT_TYPE' => 'application/json'
667
- expect(last_response.status).to eq(201)
668
- expect(JSON.parse(last_response.body)).to eq([[]])
669
-
670
- post '/coerce_nested_strings', ::Grape::Json.dump(values: [['a', { bar: 0 }], ['b']]), 'CONTENT_TYPE' => 'application/json'
671
- expect(last_response.status).to eq(400)
672
- end
673
-
674
- it 'parses parameters with Array[Integer] type' do
675
- subject.params do
676
- requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(/\s+/).map(&:to_i) }
677
- end
678
- subject.get '/ints' do
679
- params[:values]
680
- end
681
-
682
- get '/ints', values: '1 2 3 4'
683
- expect(last_response.status).to eq(200)
684
- expect(JSON.parse(last_response.body)).to eq([1, 2, 3, 4])
685
-
686
- get '/ints', values: 'a b c d'
687
- expect(last_response.status).to eq(200)
688
- expect(JSON.parse(last_response.body)).to eq([0, 0, 0, 0])
689
- end
690
-
691
- it 'parses parameters even if type is valid' do
692
- subject.params do
693
- requires :values, type: Array, coerce_with: ->(array) { array.map { |val| val.to_i + 1 } }
694
- end
695
- subject.get '/ints' do
696
- params[:values]
697
- end
698
-
699
- get '/ints', values: [1, 2, 3, 4]
700
- expect(last_response.status).to eq(200)
701
- expect(JSON.parse(last_response.body)).to eq([2, 3, 4, 5])
702
-
703
- get '/ints', values: %w[a b c d]
704
- expect(last_response.status).to eq(200)
705
- expect(JSON.parse(last_response.body)).to eq([1, 1, 1, 1])
706
- end
707
-
708
- context 'Array type and coerce_with should' do
709
- before do
710
- subject.params do
711
- optional :arr, type: Array, coerce_with: (lambda do |val|
712
- if val.nil?
713
- []
714
- else
715
- val
716
- end
717
- end)
718
- end
719
- subject.get '/' do
720
- params[:arr].class.to_s
721
- end
722
- end
723
-
724
- it 'coerce nil value to array' do
725
- get '/', arr: nil
726
-
727
- expect(last_response.status).to eq(200)
728
- expect(last_response.body).to eq('Array')
729
- end
730
-
731
- it 'not coerce missing field' do
732
- get '/'
733
-
734
- expect(last_response.status).to eq(200)
735
- expect(last_response.body).to eq('NilClass')
736
- end
737
-
738
- it 'coerce array as array' do
739
- get '/', arr: []
740
-
741
- expect(last_response.status).to eq(200)
742
- expect(last_response.body).to eq('Array')
743
- end
744
- end
745
-
746
- it 'uses parse where available' do
747
- subject.params do
748
- requires :ints, type: Array, coerce_with: JSON do
749
- requires :i, type: Integer
750
- requires :j
751
- end
752
- end
753
- subject.get '/ints' do
754
- ints = params[:ints].first
755
- 'coercion works' if ints[:i] == 1 && ints[:j] == '2'
756
- end
757
-
758
- get '/ints', ints: [{ i: 1, j: '2' }]
759
- expect(last_response.status).to eq(400)
760
- expect(last_response.body).to eq('ints is invalid')
761
-
762
- get '/ints', ints: '{"i":1,"j":"2"}'
763
- expect(last_response.status).to eq(400)
764
- expect(last_response.body).to eq('ints is invalid')
765
-
766
- get '/ints', ints: '[{"i":"1","j":"2"}]'
767
- expect(last_response.status).to eq(200)
768
- expect(last_response.body).to eq('coercion works')
769
- end
770
-
771
- it 'accepts any callable' do
772
- subject.params do
773
- requires :ints, type: Hash, coerce_with: JSON.method(:parse) do
774
- requires :int, type: Integer, coerce_with: ->(val) { val == 'three' ? 3 : val }
775
- end
776
- end
777
- subject.get '/ints' do
778
- params[:ints][:int]
779
- end
780
-
781
- get '/ints', ints: '{"int":"3"}'
782
- expect(last_response.status).to eq(400)
783
- expect(last_response.body).to eq('ints[int] is invalid')
784
-
785
- get '/ints', ints: '{"int":"three"}'
786
- expect(last_response.status).to eq(200)
787
- expect(last_response.body).to eq('3')
788
-
789
- get '/ints', ints: '{"int":3}'
790
- expect(last_response.status).to eq(200)
791
- expect(last_response.body).to eq('3')
792
- end
793
-
794
- context 'Integer type and coerce_with should' do
795
- before do
796
- subject.params do
797
- optional :int, type: Integer, coerce_with: (lambda do |val|
798
- if val.nil?
799
- 0
800
- else
801
- val.to_i
802
- end
803
- end)
804
- end
805
- subject.get '/' do
806
- params[:int].class.to_s
807
- end
808
- end
809
-
810
- it 'coerce nil value to integer' do
811
- get '/', int: nil
812
-
813
- expect(last_response.status).to eq(200)
814
- expect(last_response.body).to eq('Integer')
815
- end
816
-
817
- it 'not coerce missing field' do
818
- get '/'
819
-
820
- expect(last_response.status).to eq(200)
821
- expect(last_response.body).to eq('NilClass')
822
- end
823
-
824
- it 'coerce integer as integer' do
825
- get '/', int: 1
826
-
827
- expect(last_response.status).to eq(200)
828
- expect(last_response.body).to eq('Integer')
829
- end
830
- end
831
-
832
- context 'Integer type and coerce_with potentially returning nil' do
833
- before do
834
- subject.params do
835
- requires :int, type: Integer, coerce_with: (lambda do |val|
836
- case val
837
- when '0'
838
- nil
839
- when /^-?\d+$/
840
- val.to_i
841
- else
842
- val
843
- end
844
- end)
845
- end
846
- subject.get '/' do
847
- params[:int].class.to_s
848
- end
849
- end
850
-
851
- it 'accepts value that coerces to nil' do
852
- get '/', int: '0'
853
-
854
- expect(last_response.status).to eq(200)
855
- expect(last_response.body).to eq('NilClass')
856
- end
857
-
858
- it 'coerces to Integer' do
859
- get '/', int: '1'
860
-
861
- expect(last_response.status).to eq(200)
862
- expect(last_response.body).to eq('Integer')
863
- end
864
-
865
- it 'returns invalid value if coercion returns a wrong type' do
866
- get '/', int: 'lol'
867
-
868
- expect(last_response.status).to eq(400)
869
- expect(last_response.body).to eq('int is invalid')
870
- end
871
- end
872
-
873
- it 'must be supplied with :type or :coerce' do
874
- expect do
875
- subject.params do
876
- requires :ints, coerce_with: JSON
877
- end
878
- end.to raise_error(ArgumentError)
879
- end
880
- end
881
-
882
- context 'first-class JSON' do
883
- it 'parses objects, hashes, and arrays' do
884
- subject.params do
885
- requires :splines, type: JSON do
886
- requires :x, type: Integer, values: [1, 2, 3]
887
- optional :ints, type: Array[Integer]
888
- optional :obj, type: Hash do
889
- optional :y
890
- end
891
- end
892
- end
893
- subject.get '/' do
894
- if params[:splines].is_a? Hash
895
- params[:splines][:obj][:y]
896
- elsif params[:splines].any? { |s| s.key? :obj }
897
- 'arrays work'
898
- end
899
- end
900
-
901
- get '/', splines: '{"x":1,"ints":[1,2,3],"obj":{"y":"woof"}}'
902
- expect(last_response.status).to eq(200)
903
- expect(last_response.body).to eq('woof')
904
-
905
- get '/', splines: { x: 1, ints: [1, 2, 3], obj: { y: 'woof' } }
906
- expect(last_response.status).to eq(200)
907
- expect(last_response.body).to eq('woof')
908
-
909
- get '/', splines: '[{"x":2,"ints":[]},{"x":3,"ints":[4],"obj":{"y":"quack"}}]'
910
- expect(last_response.status).to eq(200)
911
- expect(last_response.body).to eq('arrays work')
912
-
913
- get '/', splines: [{ x: 2, ints: [5] }, { x: 3, ints: [4], obj: { y: 'quack' } }]
914
- expect(last_response.status).to eq(200)
915
- expect(last_response.body).to eq('arrays work')
916
-
917
- get '/', splines: '{"x":4,"ints":[2]}'
918
- expect(last_response.status).to eq(400)
919
- expect(last_response.body).to eq('splines[x] does not have a valid value')
920
-
921
- get '/', splines: { x: 4, ints: [2] }
922
- expect(last_response.status).to eq(400)
923
- expect(last_response.body).to eq('splines[x] does not have a valid value')
924
-
925
- get '/', splines: '[{"x":1,"ints":[]},{"x":4,"ints":[]}]'
926
- expect(last_response.status).to eq(400)
927
- expect(last_response.body).to eq('splines[x] does not have a valid value')
928
-
929
- get '/', splines: [{ x: 1, ints: [5] }, { x: 4, ints: [6] }]
930
- expect(last_response.status).to eq(400)
931
- expect(last_response.body).to eq('splines[x] does not have a valid value')
932
- end
933
-
934
- it 'works when declared optional' do
935
- subject.params do
936
- optional :splines, type: JSON do
937
- requires :x, type: Integer, values: [1, 2, 3]
938
- optional :ints, type: Array[Integer]
939
- optional :obj, type: Hash do
940
- optional :y
941
- end
942
- end
943
- end
944
- subject.get '/' do
945
- if params[:splines].is_a? Hash
946
- params[:splines][:obj][:y]
947
- elsif params[:splines].any? { |s| s.key? :obj }
948
- 'arrays work'
949
- end
950
- end
951
-
952
- get '/', splines: '{"x":1,"ints":[1,2,3],"obj":{"y":"woof"}}'
953
- expect(last_response.status).to eq(200)
954
- expect(last_response.body).to eq('woof')
955
-
956
- get '/', splines: '[{"x":2,"ints":[]},{"x":3,"ints":[4],"obj":{"y":"quack"}}]'
957
- expect(last_response.status).to eq(200)
958
- expect(last_response.body).to eq('arrays work')
959
-
960
- get '/', splines: '{"x":4,"ints":[2]}'
961
- expect(last_response.status).to eq(400)
962
- expect(last_response.body).to eq('splines[x] does not have a valid value')
963
-
964
- get '/', splines: '[{"x":1,"ints":[]},{"x":4,"ints":[]}]'
965
- expect(last_response.status).to eq(400)
966
- expect(last_response.body).to eq('splines[x] does not have a valid value')
967
- end
968
-
969
- it 'accepts Array[JSON] shorthand' do
970
- subject.params do
971
- requires :splines, type: Array[JSON] do
972
- requires :x, type: Integer, values: [1, 2, 3]
973
- requires :y
974
- end
975
- end
976
- subject.get '/' do
977
- params[:splines].first[:y].class.to_s
978
- spline = params[:splines].first
979
- "#{spline[:x].class}.#{spline[:y].class}"
980
- end
981
-
982
- get '/', splines: '{"x":"1","y":"woof"}'
983
- expect(last_response.status).to eq(200)
984
- expect(last_response.body).to eq("#{integer_class_name}.String")
985
-
986
- get '/', splines: '[{"x":1,"y":2},{"x":1,"y":"quack"}]'
987
- expect(last_response.status).to eq(200)
988
- expect(last_response.body).to eq("#{integer_class_name}.#{integer_class_name}")
989
-
990
- get '/', splines: '{"x":"4","y":"woof"}'
991
- expect(last_response.status).to eq(400)
992
- expect(last_response.body).to eq('splines[x] does not have a valid value')
993
-
994
- get '/', splines: '[{"x":"4","y":"woof"}]'
995
- expect(last_response.status).to eq(400)
996
- expect(last_response.body).to eq('splines[x] does not have a valid value')
997
- end
998
-
999
- it "doesn't make sense using coerce_with" do
1000
- expect do
1001
- subject.params do
1002
- requires :bad, type: JSON, coerce_with: JSON do
1003
- requires :x
1004
- end
1005
- end
1006
- end.to raise_error(ArgumentError)
1007
-
1008
- expect do
1009
- subject.params do
1010
- requires :bad, type: Array[JSON], coerce_with: JSON do
1011
- requires :x
1012
- end
1013
- end
1014
- end.to raise_error(ArgumentError)
1015
- end
1016
- end
1017
-
1018
- context 'multiple types' do
1019
- it 'coerces to first possible type' do
1020
- subject.params do
1021
- requires :a, types: [Grape::API::Boolean, Integer, String]
1022
- end
1023
- subject.get '/' do
1024
- params[:a].class.to_s
1025
- end
1026
-
1027
- get '/', a: 'true'
1028
- expect(last_response.status).to eq(200)
1029
- expect(last_response.body).to eq('TrueClass')
1030
-
1031
- get '/', a: '5'
1032
- expect(last_response.status).to eq(200)
1033
- expect(last_response.body).to eq(integer_class_name)
1034
-
1035
- get '/', a: 'anything else'
1036
- expect(last_response.status).to eq(200)
1037
- expect(last_response.body).to eq('String')
1038
- end
1039
-
1040
- it 'fails when no coercion is possible' do
1041
- subject.params do
1042
- requires :a, types: [Grape::API::Boolean, Integer]
1043
- end
1044
- subject.get '/' do
1045
- params[:a].class.to_s
1046
- end
1047
-
1048
- get '/', a: true
1049
- expect(last_response.status).to eq(200)
1050
- expect(last_response.body).to eq('TrueClass')
1051
-
1052
- get '/', a: 'not good'
1053
- expect(last_response.status).to eq(400)
1054
- expect(last_response.body).to eq('a is invalid')
1055
- end
1056
-
1057
- context 'for primitive collections' do
1058
- before do
1059
- subject.params do
1060
- optional :a, types: [String, Array[String]]
1061
- optional :b, types: [Array[Integer], Array[String]]
1062
- optional :c, type: Array[Integer, String]
1063
- optional :d, types: [Integer, String, Set[Integer, String]]
1064
- end
1065
- subject.get '/' do
1066
- (
1067
- params[:a] ||
1068
- params[:b] ||
1069
- params[:c] ||
1070
- params[:d]
1071
- ).inspect
1072
- end
1073
- end
1074
-
1075
- it 'allows singular form declaration' do
1076
- get '/', a: 'one way'
1077
- expect(last_response.status).to eq(200)
1078
- expect(last_response.body).to eq('"one way"')
1079
-
1080
- get '/', a: %w[the other]
1081
- expect(last_response.status).to eq(200)
1082
- expect(last_response.body).to eq('["the", "other"]')
1083
-
1084
- get '/', a: { a: 1, b: 2 }
1085
- expect(last_response.status).to eq(400)
1086
- expect(last_response.body).to eq('a is invalid')
1087
-
1088
- get '/', a: [1, 2, 3]
1089
- expect(last_response.status).to eq(200)
1090
- expect(last_response.body).to eq('["1", "2", "3"]')
1091
- end
1092
-
1093
- it 'allows multiple collection types' do
1094
- get '/', b: [1, 2, 3]
1095
- expect(last_response.status).to eq(200)
1096
- expect(last_response.body).to eq('[1, 2, 3]')
1097
-
1098
- get '/', b: %w[1 2 3]
1099
- expect(last_response.status).to eq(200)
1100
- expect(last_response.body).to eq('[1, 2, 3]')
1101
-
1102
- get '/', b: [1, true, 'three']
1103
- expect(last_response.status).to eq(200)
1104
- expect(last_response.body).to eq('["1", "true", "three"]')
1105
- end
1106
-
1107
- it 'allows collections with multiple types' do
1108
- get '/', c: [1, '2', true, 'three']
1109
- expect(last_response.status).to eq(200)
1110
- expect(last_response.body).to eq('[1, 2, "true", "three"]')
1111
-
1112
- get '/', d: '1'
1113
- expect(last_response.status).to eq(200)
1114
- expect(last_response.body).to eq('1')
1115
-
1116
- get '/', d: 'one'
1117
- expect(last_response.status).to eq(200)
1118
- expect(last_response.body).to eq('"one"')
1119
-
1120
- get '/', d: %w[1 two]
1121
- expect(last_response.status).to eq(200)
1122
- expect(last_response.body).to eq('#<Set: {1, "two"}>')
1123
- end
1124
- end
1125
-
1126
- context 'when params is Hashie::Mash' do
1127
- context 'for primitive collections' do
1128
- before do
1129
- subject.params do
1130
- build_with Grape::Extensions::Hashie::Mash::ParamBuilder
1131
- optional :a, types: [String, Array[String]]
1132
- optional :b, types: [Array[Integer], Array[String]]
1133
- optional :c, type: Array[Integer, String]
1134
- optional :d, types: [Integer, String, Set[Integer, String]]
1135
- end
1136
- subject.get '/' do
1137
- (
1138
- params.a ||
1139
- params.b ||
1140
- params.c ||
1141
- params.d
1142
- ).inspect
1143
- end
1144
- end
1145
-
1146
- it 'allows singular form declaration' do
1147
- get '/', a: 'one way'
1148
- expect(last_response.status).to eq(200)
1149
- expect(last_response.body).to eq('"one way"')
1150
-
1151
- get '/', a: %w[the other]
1152
- expect(last_response.status).to eq(200)
1153
- expect(last_response.body).to eq('#<Hashie::Array ["the", "other"]>')
1154
-
1155
- get '/', a: { a: 1, b: 2 }
1156
- expect(last_response.status).to eq(400)
1157
- expect(last_response.body).to eq('a is invalid')
1158
-
1159
- get '/', a: [1, 2, 3]
1160
- expect(last_response.status).to eq(200)
1161
- expect(last_response.body).to eq('#<Hashie::Array ["1", "2", "3"]>')
1162
- end
1163
-
1164
- it 'allows multiple collection types' do
1165
- get '/', b: [1, 2, 3]
1166
- expect(last_response.status).to eq(200)
1167
- expect(last_response.body).to eq('#<Hashie::Array [1, 2, 3]>')
1168
-
1169
- get '/', b: %w[1 2 3]
1170
- expect(last_response.status).to eq(200)
1171
- expect(last_response.body).to eq('#<Hashie::Array [1, 2, 3]>')
1172
-
1173
- get '/', b: [1, true, 'three']
1174
- expect(last_response.status).to eq(200)
1175
- expect(last_response.body).to eq('#<Hashie::Array ["1", "true", "three"]>')
1176
- end
1177
-
1178
- it 'allows collections with multiple types' do
1179
- get '/', c: [1, '2', true, 'three']
1180
- expect(last_response.status).to eq(200)
1181
- expect(last_response.body).to eq('#<Hashie::Array [1, 2, "true", "three"]>')
1182
-
1183
- get '/', d: '1'
1184
- expect(last_response.status).to eq(200)
1185
- expect(last_response.body).to eq('1')
1186
-
1187
- get '/', d: 'one'
1188
- expect(last_response.status).to eq(200)
1189
- expect(last_response.body).to eq('"one"')
1190
-
1191
- get '/', d: %w[1 two]
1192
- expect(last_response.status).to eq(200)
1193
- expect(last_response.body).to eq('#<Set: {1, "two"}>')
1194
- end
1195
- end
1196
- end
1197
-
1198
- context 'custom coercion rules' do
1199
- before do
1200
- subject.params do
1201
- requires :a, types: [Grape::API::Boolean, String], coerce_with: (lambda do |val|
1202
- case val
1203
- when 'yup'
1204
- true
1205
- when 'false'
1206
- 0
1207
- else
1208
- val
1209
- end
1210
- end)
1211
- end
1212
- subject.get '/' do
1213
- params[:a].class.to_s
1214
- end
1215
- end
1216
-
1217
- it 'respects :coerce_with' do
1218
- get '/', a: 'yup'
1219
- expect(last_response.status).to eq(200)
1220
- expect(last_response.body).to eq('TrueClass')
1221
- end
1222
-
1223
- it 'still validates type' do
1224
- get '/', a: 'false'
1225
- expect(last_response.status).to eq(400)
1226
- expect(last_response.body).to eq('a is invalid')
1227
- end
1228
-
1229
- it 'performs no additional coercion' do
1230
- get '/', a: 'true'
1231
- expect(last_response.status).to eq(200)
1232
- expect(last_response.body).to eq('String')
1233
- end
1234
- end
1235
-
1236
- it 'may not be supplied together with a single type' do
1237
- expect do
1238
- subject.params do
1239
- requires :a, type: Integer, types: [Integer, String]
1240
- end
1241
- end.to raise_exception ArgumentError
1242
- end
1243
- end
1244
-
1245
- context 'converter' do
1246
- it 'does not build a coercer multiple times' do
1247
- subject.params do
1248
- requires :something, type: Array[String]
1249
- end
1250
- subject.get do
1251
- end
1252
-
1253
- expect(Grape::Validations::Types::ArrayCoercer).to(
1254
- receive(:new).at_most(:once).and_call_original
1255
- )
1256
-
1257
- 10.times { get '/' }
1258
- end
1259
- end
1260
- end
1261
- end