grape 1.7.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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