grape 1.2.5 → 1.3.1

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 (258) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +7 -6
  4. data/UPGRADING.md +43 -0
  5. data/grape.gemspec +10 -1
  6. data/lib/grape/api/helpers.rb +2 -0
  7. data/lib/grape/api/instance.rb +8 -6
  8. data/lib/grape/api.rb +4 -2
  9. data/lib/grape/config.rb +2 -0
  10. data/lib/grape/content_types.rb +34 -0
  11. data/lib/grape/cookies.rb +2 -0
  12. data/lib/grape/dsl/api.rb +2 -0
  13. data/lib/grape/dsl/callbacks.rb +2 -0
  14. data/lib/grape/dsl/configuration.rb +2 -0
  15. data/lib/grape/dsl/desc.rb +2 -0
  16. data/lib/grape/dsl/headers.rb +2 -0
  17. data/lib/grape/dsl/helpers.rb +4 -2
  18. data/lib/grape/dsl/inside_route.rb +15 -11
  19. data/lib/grape/dsl/logger.rb +2 -0
  20. data/lib/grape/dsl/middleware.rb +2 -0
  21. data/lib/grape/dsl/parameters.rb +8 -6
  22. data/lib/grape/dsl/request_response.rb +4 -2
  23. data/lib/grape/dsl/routing.rb +9 -5
  24. data/lib/grape/dsl/settings.rb +7 -1
  25. data/lib/grape/dsl/validations.rb +2 -0
  26. data/lib/grape/eager_load.rb +2 -0
  27. data/lib/grape/endpoint.rb +13 -7
  28. data/lib/grape/error_formatter/base.rb +2 -0
  29. data/lib/grape/error_formatter/json.rb +2 -0
  30. data/lib/grape/error_formatter/txt.rb +2 -0
  31. data/lib/grape/error_formatter/xml.rb +2 -0
  32. data/lib/grape/error_formatter.rb +3 -1
  33. data/lib/grape/exceptions/base.rb +11 -13
  34. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  35. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  36. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  37. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  38. data/lib/grape/exceptions/invalid_response.rb +2 -0
  39. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  40. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  41. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  42. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  43. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  44. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  45. data/lib/grape/exceptions/missing_option.rb +2 -0
  46. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  47. data/lib/grape/exceptions/unknown_options.rb +2 -0
  48. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  49. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  50. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  51. data/lib/grape/exceptions/validation.rb +3 -1
  52. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  53. data/lib/grape/exceptions/validation_errors.rb +13 -12
  54. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  55. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  56. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  57. data/lib/grape/extensions/hash.rb +2 -0
  58. data/lib/grape/extensions/hashie/mash.rb +2 -0
  59. data/lib/grape/formatter/json.rb +2 -0
  60. data/lib/grape/formatter/serializable_hash.rb +2 -0
  61. data/lib/grape/formatter/txt.rb +2 -0
  62. data/lib/grape/formatter/xml.rb +2 -0
  63. data/lib/grape/formatter.rb +5 -3
  64. data/lib/grape/http/headers.rb +49 -18
  65. data/lib/grape/middleware/auth/base.rb +2 -0
  66. data/lib/grape/middleware/auth/dsl.rb +2 -0
  67. data/lib/grape/middleware/auth/strategies.rb +2 -0
  68. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  69. data/lib/grape/middleware/base.rb +5 -5
  70. data/lib/grape/middleware/error.rb +3 -1
  71. data/lib/grape/middleware/filter.rb +2 -0
  72. data/lib/grape/middleware/formatter.rb +5 -3
  73. data/lib/grape/middleware/globals.rb +2 -0
  74. data/lib/grape/middleware/helpers.rb +2 -0
  75. data/lib/grape/middleware/stack.rb +4 -1
  76. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  77. data/lib/grape/middleware/versioner/header.rb +5 -3
  78. data/lib/grape/middleware/versioner/param.rb +3 -1
  79. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -0
  80. data/lib/grape/middleware/versioner/path.rb +3 -1
  81. data/lib/grape/middleware/versioner.rb +2 -0
  82. data/lib/grape/namespace.rb +14 -2
  83. data/lib/grape/parser/json.rb +2 -0
  84. data/lib/grape/parser/xml.rb +2 -0
  85. data/lib/grape/parser.rb +3 -1
  86. data/lib/grape/path.rb +13 -1
  87. data/lib/grape/presenters/presenter.rb +2 -0
  88. data/lib/grape/request.rb +15 -8
  89. data/lib/grape/router/attribute_translator.rb +18 -8
  90. data/lib/grape/router/pattern.rb +20 -16
  91. data/lib/grape/router/route.rb +9 -4
  92. data/lib/grape/router.rb +26 -12
  93. data/lib/grape/serve_file/file_body.rb +2 -0
  94. data/lib/grape/serve_file/file_response.rb +2 -0
  95. data/lib/grape/serve_file/sendfile_response.rb +2 -0
  96. data/lib/grape/util/base_inheritable.rb +6 -0
  97. data/lib/grape/util/cache.rb +20 -0
  98. data/lib/grape/util/endpoint_configuration.rb +2 -0
  99. data/lib/grape/util/env.rb +19 -17
  100. data/lib/grape/util/inheritable_setting.rb +2 -0
  101. data/lib/grape/util/inheritable_values.rb +2 -0
  102. data/lib/grape/util/json.rb +2 -0
  103. data/lib/grape/util/lazy_block.rb +2 -0
  104. data/lib/grape/util/lazy_object.rb +43 -0
  105. data/lib/grape/util/lazy_value.rb +2 -0
  106. data/lib/grape/util/registrable.rb +2 -0
  107. data/lib/grape/util/reverse_stackable_values.rb +3 -1
  108. data/lib/grape/util/stackable_values.rb +9 -21
  109. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  110. data/lib/grape/util/xml.rb +2 -0
  111. data/lib/grape/validations/attributes_iterator.rb +3 -3
  112. data/lib/grape/validations/multiple_attributes_iterator.rb +2 -0
  113. data/lib/grape/validations/params_scope.rb +24 -11
  114. data/lib/grape/validations/single_attribute_iterator.rb +13 -2
  115. data/lib/grape/validations/types/array_coercer.rb +56 -0
  116. data/lib/grape/validations/types/build_coercer.rb +49 -48
  117. data/lib/grape/validations/types/custom_type_coercer.rb +15 -49
  118. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  119. data/lib/grape/validations/types/dry_type_coercer.rb +41 -0
  120. data/lib/grape/validations/types/file.rb +11 -9
  121. data/lib/grape/validations/types/json.rb +11 -8
  122. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  123. data/lib/grape/validations/types/primitive_coercer.rb +61 -0
  124. data/lib/grape/validations/types/set_coercer.rb +38 -0
  125. data/lib/grape/validations/types/variant_collection_coercer.rb +4 -12
  126. data/lib/grape/validations/types.rb +7 -30
  127. data/lib/grape/validations/validator_factory.rb +2 -0
  128. data/lib/grape/validations/validators/all_or_none.rb +3 -1
  129. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  130. data/lib/grape/validations/validators/as.rb +2 -0
  131. data/lib/grape/validations/validators/at_least_one_of.rb +3 -1
  132. data/lib/grape/validations/validators/base.rb +8 -5
  133. data/lib/grape/validations/validators/coerce.rb +44 -27
  134. data/lib/grape/validations/validators/default.rb +2 -0
  135. data/lib/grape/validations/validators/exactly_one_of.rb +6 -2
  136. data/lib/grape/validations/validators/except_values.rb +3 -1
  137. data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
  138. data/lib/grape/validations/validators/mutual_exclusion.rb +3 -1
  139. data/lib/grape/validations/validators/presence.rb +3 -1
  140. data/lib/grape/validations/validators/regexp.rb +3 -1
  141. data/lib/grape/validations/validators/same_as.rb +6 -3
  142. data/lib/grape/validations/validators/values.rb +17 -5
  143. data/lib/grape/validations.rb +2 -0
  144. data/lib/grape/version.rb +3 -1
  145. data/lib/grape.rb +4 -5
  146. data/spec/grape/api/custom_validations_spec.rb +5 -3
  147. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  148. data/spec/grape/api/defines_boolean_in_params_spec.rb +5 -3
  149. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  150. data/spec/grape/api/instance_spec.rb +54 -0
  151. data/spec/grape/api/invalid_format_spec.rb +2 -0
  152. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  153. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  154. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  155. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  156. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  157. data/spec/grape/api/recognize_path_spec.rb +2 -0
  158. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  160. data/spec/grape/api/routes_with_requirements_spec.rb +2 -0
  161. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  162. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  163. data/spec/grape/api_remount_spec.rb +2 -0
  164. data/spec/grape/api_spec.rb +34 -11
  165. data/spec/grape/config_spec.rb +2 -0
  166. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  167. data/spec/grape/dsl/configuration_spec.rb +2 -0
  168. data/spec/grape/dsl/desc_spec.rb +2 -0
  169. data/spec/grape/dsl/headers_spec.rb +2 -0
  170. data/spec/grape/dsl/helpers_spec.rb +4 -2
  171. data/spec/grape/dsl/inside_route_spec.rb +2 -0
  172. data/spec/grape/dsl/logger_spec.rb +2 -0
  173. data/spec/grape/dsl/middleware_spec.rb +2 -0
  174. data/spec/grape/dsl/parameters_spec.rb +2 -0
  175. data/spec/grape/dsl/request_response_spec.rb +2 -0
  176. data/spec/grape/dsl/routing_spec.rb +2 -0
  177. data/spec/grape/dsl/settings_spec.rb +2 -0
  178. data/spec/grape/dsl/validations_spec.rb +2 -0
  179. data/spec/grape/endpoint_spec.rb +3 -1
  180. data/spec/grape/entity_spec.rb +2 -0
  181. data/spec/grape/exceptions/base_spec.rb +3 -1
  182. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  183. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  184. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  185. data/spec/grape/exceptions/invalid_response_spec.rb +2 -0
  186. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  187. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  188. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  189. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  190. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  191. data/spec/grape/exceptions/validation_errors_spec.rb +4 -2
  192. data/spec/grape/exceptions/validation_spec.rb +3 -1
  193. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  194. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  195. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  196. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  197. data/spec/grape/integration/rack_sendfile_spec.rb +2 -0
  198. data/spec/grape/integration/rack_spec.rb +3 -1
  199. data/spec/grape/loading_spec.rb +2 -0
  200. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  201. data/spec/grape/middleware/auth/dsl_spec.rb +2 -0
  202. data/spec/grape/middleware/auth/strategies_spec.rb +2 -0
  203. data/spec/grape/middleware/base_spec.rb +2 -0
  204. data/spec/grape/middleware/error_spec.rb +2 -0
  205. data/spec/grape/middleware/exception_spec.rb +3 -1
  206. data/spec/grape/middleware/formatter_spec.rb +19 -12
  207. data/spec/grape/middleware/globals_spec.rb +2 -0
  208. data/spec/grape/middleware/stack_spec.rb +11 -0
  209. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  210. data/spec/grape/middleware/versioner/header_spec.rb +3 -1
  211. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  212. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  213. data/spec/grape/middleware/versioner_spec.rb +2 -0
  214. data/spec/grape/named_api_spec.rb +2 -0
  215. data/spec/grape/parser_spec.rb +7 -5
  216. data/spec/grape/path_spec.rb +2 -0
  217. data/spec/grape/presenters/presenter_spec.rb +2 -0
  218. data/spec/grape/request_spec.rb +2 -0
  219. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  220. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  221. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  222. data/spec/grape/util/stackable_values_spec.rb +3 -1
  223. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  224. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  225. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  226. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +2 -0
  227. data/spec/grape/validations/params_scope_spec.rb +3 -1
  228. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -4
  229. data/spec/grape/validations/types/primitive_coercer_spec.rb +75 -0
  230. data/spec/grape/validations/types_spec.rb +8 -35
  231. data/spec/grape/validations/validators/all_or_none_spec.rb +2 -0
  232. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  233. data/spec/grape/validations/validators/at_least_one_of_spec.rb +2 -0
  234. data/spec/grape/validations/validators/coerce_spec.rb +51 -110
  235. data/spec/grape/validations/validators/default_spec.rb +2 -0
  236. data/spec/grape/validations/validators/exactly_one_of_spec.rb +14 -12
  237. data/spec/grape/validations/validators/except_values_spec.rb +3 -1
  238. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +2 -0
  239. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  240. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  241. data/spec/grape/validations/validators/same_as_spec.rb +2 -0
  242. data/spec/grape/validations/validators/values_spec.rb +29 -4
  243. data/spec/grape/validations_spec.rb +69 -15
  244. data/spec/integration/multi_json/json_spec.rb +2 -0
  245. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  246. data/spec/shared/versioning_examples.rb +2 -0
  247. data/spec/spec_helper.rb +18 -0
  248. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  249. data/spec/support/content_type_helpers.rb +2 -0
  250. data/spec/support/eager_load.rb +19 -0
  251. data/spec/support/endpoint_faker.rb +2 -0
  252. data/spec/support/file_streamer.rb +2 -0
  253. data/spec/support/integer_helpers.rb +2 -0
  254. data/spec/support/versioned_helpers.rb +4 -2
  255. metadata +126 -112
  256. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  257. data/lib/grape/util/content_types.rb +0 -26
  258. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::CoerceValidator do
@@ -10,11 +12,13 @@ describe Grape::Validations::CoerceValidator do
10
12
  end
11
13
 
12
14
  describe 'coerce' do
13
- module CoerceValidatorSpec
14
- class User
15
- include Virtus.model
16
- attribute :id, Integer
17
- attribute :name, String
15
+ class SecureURIOnly
16
+ def self.parse(value)
17
+ URI.parse(value)
18
+ end
19
+
20
+ def self.parsed?(value)
21
+ value.is_a? URI::HTTPS
18
22
  end
19
23
  end
20
24
 
@@ -96,6 +100,7 @@ describe Grape::Validations::CoerceValidator do
96
100
 
97
101
  it 'respects :coerce_with' do
98
102
  get '/', a: 'yup'
103
+
99
104
  expect(last_response.status).to eq(200)
100
105
  expect(last_response.body).to eq('TrueClass')
101
106
  end
@@ -148,26 +153,20 @@ describe Grape::Validations::CoerceValidator do
148
153
  expect(last_response.body).to eq('array int works')
149
154
  end
150
155
 
151
- context 'complex objects' do
152
- it 'error on malformed input for complex objects' do
156
+ context 'coerces' do
157
+ it 'BigDecimal' do
153
158
  subject.params do
154
- requires :user, type: CoerceValidatorSpec::User
159
+ requires :bigdecimal, coerce: BigDecimal
155
160
  end
156
- subject.get '/user' do
157
- 'complex works'
161
+ subject.get '/bigdecimal' do
162
+ params[:bigdecimal].class
158
163
  end
159
164
 
160
- get '/user', user: '32'
161
- expect(last_response.status).to eq(400)
162
- expect(last_response.body).to eq('user is invalid')
163
-
164
- get '/user', user: { id: 32, name: 'Bob' }
165
+ get '/bigdecimal', bigdecimal: '45'
165
166
  expect(last_response.status).to eq(200)
166
- expect(last_response.body).to eq('complex works')
167
+ expect(last_response.body).to eq('BigDecimal')
167
168
  end
168
- end
169
169
 
170
- context 'coerces' do
171
170
  it 'Integer' do
172
171
  subject.params do
173
172
  requires :int, coerce: Integer
@@ -181,6 +180,25 @@ describe Grape::Validations::CoerceValidator do
181
180
  expect(last_response.body).to eq(integer_class_name)
182
181
  end
183
182
 
183
+ it 'is a custom type' do
184
+ subject.params do
185
+ requires :uri, coerce: SecureURIOnly
186
+ end
187
+ subject.get '/secure_uri' do
188
+ params[:uri].class
189
+ end
190
+
191
+ get 'secure_uri', uri: 'https://www.example.com'
192
+
193
+ expect(last_response.status).to eq(200)
194
+ expect(last_response.body).to eq('URI::HTTPS')
195
+
196
+ get 'secure_uri', uri: 'http://www.example.com'
197
+
198
+ expect(last_response.status).to eq(400)
199
+ expect(last_response.body).to eq('uri is invalid')
200
+ end
201
+
184
202
  context 'Array' do
185
203
  it 'Array of Integers' do
186
204
  subject.params do
@@ -197,7 +215,7 @@ describe Grape::Validations::CoerceValidator do
197
215
 
198
216
  it 'Array of Bools' do
199
217
  subject.params do
200
- requires :arry, coerce: Array[Virtus::Attribute::Boolean]
218
+ requires :arry, coerce: Array[Grape::API::Boolean]
201
219
  end
202
220
  subject.get '/array' do
203
221
  params[:arry][0].class
@@ -208,27 +226,6 @@ describe Grape::Validations::CoerceValidator do
208
226
  expect(last_response.body).to eq('TrueClass')
209
227
  end
210
228
 
211
- it 'Array of Complex' do
212
- subject.params do
213
- requires :arry, coerce: Array[CoerceValidatorSpec::User]
214
- end
215
- subject.get '/array' do
216
- params[:arry].size
217
- end
218
-
219
- get 'array', arry: [31]
220
- expect(last_response.status).to eq(400)
221
- expect(last_response.body).to eq('arry is invalid')
222
-
223
- get 'array', arry: { id: 31, name: 'Alice' }
224
- expect(last_response.status).to eq(400)
225
- expect(last_response.body).to eq('arry is invalid')
226
-
227
- get 'array', arry: [{ id: 31, name: 'Alice' }]
228
- expect(last_response.status).to eq(200)
229
- expect(last_response.body).to eq('1')
230
- end
231
-
232
229
  it 'Array of type implementing parse' do
233
230
  subject.params do
234
231
  requires :uri, type: Array[URI]
@@ -253,17 +250,7 @@ describe Grape::Validations::CoerceValidator do
253
250
  expect(last_response.body).to eq('Set,URI::HTTP,1')
254
251
  end
255
252
 
256
- it 'Array of class implementing parse and parsed?' do
257
- class SecureURIOnly
258
- def self.parse(value)
259
- URI.parse(value)
260
- end
261
-
262
- def self.parsed?(value)
263
- value.is_a? URI::HTTPS
264
- end
265
- end
266
-
253
+ it 'Array of a custom type' do
267
254
  subject.params do
268
255
  requires :uri, type: Array[SecureURIOnly]
269
256
  end
@@ -295,7 +282,7 @@ describe Grape::Validations::CoerceValidator do
295
282
 
296
283
  it 'Set of Bools' do
297
284
  subject.params do
298
- requires :set, coerce: Set[Virtus::Attribute::Boolean]
285
+ requires :set, coerce: Set[Grape::API::Boolean]
299
286
  end
300
287
  subject.get '/set' do
301
288
  params[:set].first.class
@@ -307,66 +294,17 @@ describe Grape::Validations::CoerceValidator do
307
294
  end
308
295
  end
309
296
 
310
- it 'Bool' do
311
- subject.params do
312
- requires :bool, coerce: Virtus::Attribute::Boolean
313
- end
314
- subject.get '/bool' do
315
- params[:bool].class
316
- end
317
-
318
- get '/bool', bool: 1
319
- expect(last_response.status).to eq(200)
320
- expect(last_response.body).to eq('TrueClass')
321
-
322
- get '/bool', bool: 0
323
- expect(last_response.status).to eq(200)
324
- expect(last_response.body).to eq('FalseClass')
325
-
326
- get '/bool', bool: 'false'
327
- expect(last_response.status).to eq(200)
328
- expect(last_response.body).to eq('FalseClass')
329
-
330
- get '/bool', bool: 'true'
331
- expect(last_response.status).to eq(200)
332
- expect(last_response.body).to eq('TrueClass')
333
- end
334
-
335
297
  it 'Boolean' do
336
298
  subject.params do
337
- optional :boolean, type: Boolean, default: true
299
+ requires :boolean, type: Boolean
338
300
  end
339
301
  subject.get '/boolean' do
340
302
  params[:boolean].class
341
303
  end
342
304
 
343
- get '/boolean'
305
+ get '/boolean', boolean: 1
344
306
  expect(last_response.status).to eq(200)
345
307
  expect(last_response.body).to eq('TrueClass')
346
-
347
- get '/boolean', boolean: true
348
- expect(last_response.status).to eq(200)
349
- expect(last_response.body).to eq('TrueClass')
350
-
351
- get '/boolean', boolean: false
352
- expect(last_response.status).to eq(200)
353
- expect(last_response.body).to eq('FalseClass')
354
-
355
- get '/boolean', boolean: 'true'
356
- expect(last_response.status).to eq(200)
357
- expect(last_response.body).to eq('TrueClass')
358
-
359
- get '/boolean', boolean: 'false'
360
- expect(last_response.status).to eq(200)
361
- expect(last_response.body).to eq('FalseClass')
362
-
363
- get '/boolean', boolean: 123
364
- expect(last_response.status).to eq(400)
365
- expect(last_response.body).to eq('boolean is invalid')
366
-
367
- get '/boolean', boolean: '123'
368
- expect(last_response.status).to eq(400)
369
- expect(last_response.body).to eq('boolean is invalid')
370
308
  end
371
309
 
372
310
  it 'Rack::Multipart::UploadedFile' do
@@ -443,19 +381,19 @@ describe Grape::Validations::CoerceValidator do
443
381
 
444
382
  it 'parses parameters with Array[String] type' do
445
383
  subject.params do
446
- requires :values, type: Array[String], coerce_with: ->(val) { val.split(/\s+/).map(&:to_i) }
384
+ requires :values, type: Array[String], coerce_with: ->(val) { val.split(/\s+/) }
447
385
  end
448
- subject.get '/ints' do
386
+ subject.get '/strings' do
449
387
  params[:values]
450
388
  end
451
389
 
452
- get '/ints', values: '1 2 3 4'
390
+ get '/strings', values: '1 2 3 4'
453
391
  expect(last_response.status).to eq(200)
454
392
  expect(JSON.parse(last_response.body)).to eq(%w[1 2 3 4])
455
393
 
456
- get '/ints', values: 'a b c d'
394
+ get '/strings', values: 'a b c d'
457
395
  expect(last_response.status).to eq(200)
458
- expect(JSON.parse(last_response.body)).to eq(%w[0 0 0 0])
396
+ expect(JSON.parse(last_response.body)).to eq(%w[a b c d])
459
397
  end
460
398
 
461
399
  it 'parses parameters with Array[Integer] type' do
@@ -914,14 +852,17 @@ describe Grape::Validations::CoerceValidator do
914
852
  end
915
853
 
916
854
  context 'converter' do
917
- it 'does not build Virtus::Attribute multiple times' do
855
+ it 'does not build a coercer multiple times' do
918
856
  subject.params do
919
857
  requires :something, type: Array[String]
920
858
  end
921
859
  subject.get do
922
860
  end
923
861
 
924
- expect(Virtus::Attribute).to receive(:build).at_most(2).times.and_call_original
862
+ expect(Grape::Validations::Types::ArrayCoercer).to(
863
+ receive(:new).at_most(:once).and_call_original
864
+ )
865
+
925
866
  10.times { get '/' }
926
867
  end
927
868
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::DefaultValidator do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::ExactlyOneOfValidator do
@@ -98,7 +100,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
98
100
  validate
99
101
  expect(last_response.status).to eq 400
100
102
  expect(JSON.parse(last_response.body)).to eq(
101
- 'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
103
+ 'beer,wine,grapefruit' => ['are mutually exclusive']
102
104
  )
103
105
  end
104
106
 
@@ -110,7 +112,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
110
112
  validate
111
113
  expect(last_response.status).to eq 400
112
114
  expect(JSON.parse(last_response.body)).to eq(
113
- 'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
115
+ 'beer,wine,grapefruit' => ['are mutually exclusive']
114
116
  )
115
117
  end
116
118
  end
@@ -124,7 +126,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
124
126
  validate
125
127
  expect(last_response.status).to eq 400
126
128
  expect(JSON.parse(last_response.body)).to eq(
127
- 'beer,wine,grapefruit' => ['are missing, exactly one parameter must be provided']
129
+ 'beer,grapefruit' => ['are mutually exclusive']
128
130
  )
129
131
  end
130
132
  end
@@ -137,7 +139,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
137
139
  validate
138
140
  expect(last_response.status).to eq 400
139
141
  expect(JSON.parse(last_response.body)).to eq(
140
- 'beer,wine,grapefruit' => ['you should choose one']
142
+ 'beer,wine' => ['you should choose one']
141
143
  )
142
144
  end
143
145
  end
@@ -173,7 +175,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
173
175
  validate
174
176
  expect(last_response.status).to eq 400
175
177
  expect(JSON.parse(last_response.body)).to eq(
176
- 'item[beer],item[wine],item[grapefruit]' => ['are missing, exactly one parameter must be provided']
178
+ 'item[beer],item[wine]' => ['are mutually exclusive']
177
179
  )
178
180
  end
179
181
  end
@@ -188,7 +190,7 @@ describe Grape::Validations::ExactlyOneOfValidator do
188
190
  validate
189
191
  expect(last_response.status).to eq 400
190
192
  expect(JSON.parse(last_response.body)).to eq(
191
- 'item[beer],item[wine],item[grapefruit]' => ['are missing, exactly one parameter must be provided']
193
+ 'item[beer],item[wine]' => ['are mutually exclusive']
192
194
  )
193
195
  end
194
196
  end
@@ -211,11 +213,11 @@ describe Grape::Validations::ExactlyOneOfValidator do
211
213
  validate
212
214
  expect(last_response.status).to eq 400
213
215
  expect(JSON.parse(last_response.body)).to eq(
214
- 'items[0][beer],items[0][wine],items[0][grapefruit]' => [
215
- 'are missing, exactly one parameter must be provided'
216
+ 'items[0][beer],items[0][wine]' => [
217
+ 'are mutually exclusive'
216
218
  ],
217
- 'items[1][beer],items[1][wine],items[1][grapefruit]' => [
218
- 'are missing, exactly one parameter must be provided'
219
+ 'items[1][wine],items[1][grapefruit]' => [
220
+ 'are mutually exclusive'
219
221
  ]
220
222
  )
221
223
  end
@@ -229,8 +231,8 @@ describe Grape::Validations::ExactlyOneOfValidator do
229
231
  validate
230
232
  expect(last_response.status).to eq 400
231
233
  expect(JSON.parse(last_response.body)).to eq(
232
- 'items[0][nested_items][0][beer],items[0][nested_items][0][wine],items[0][nested_items][0][grapefruit]' => [
233
- 'are missing, exactly one parameter must be provided'
234
+ 'items[0][nested_items][0][beer],items[0][nested_items][0][wine]' => [
235
+ 'are mutually exclusive'
234
236
  ]
235
237
  )
236
238
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::ExceptValuesValidator do
@@ -110,7 +112,7 @@ describe Grape::Validations::ExceptValuesValidator do
110
112
  optional: { type: Array[Integer], except_values: [10, 11], default: 12 },
111
113
  tests: [
112
114
  { value: 'invalid-type1', rc: 400, body: { error: 'type is invalid' }.to_json },
113
- { value: 10, rc: 400, body: { error: 'type has a value not allowed' }.to_json },
115
+ { value: 10, rc: 400, body: { error: 'type is invalid' }.to_json },
114
116
  { value: [10], rc: 400, body: { error: 'type has a value not allowed' }.to_json },
115
117
  { value: ['3'], rc: 200, body: { type: [3] }.to_json },
116
118
  { value: [3], rc: 200, body: { type: [3] }.to_json },
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::MutualExclusionValidator do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::PresenceValidator do
@@ -269,4 +271,32 @@ describe Grape::Validations::PresenceValidator do
269
271
  expect(last_response.body).to eq('Hello optional'.to_json)
270
272
  end
271
273
  end
274
+
275
+ context 'with a custom type' do
276
+ it 'does not validate their type when it is missing' do
277
+ class CustomType
278
+ def self.parse(value)
279
+ return if value.blank?
280
+
281
+ new
282
+ end
283
+ end
284
+
285
+ subject.params do
286
+ requires :custom, type: CustomType
287
+ end
288
+ subject.get '/custom' do
289
+ 'custom'
290
+ end
291
+
292
+ get 'custom'
293
+
294
+ expect(last_response.status).to eq(400)
295
+ expect(last_response.body).to eq('{"error":"custom is missing"}')
296
+
297
+ get 'custom', custom: 'filled'
298
+
299
+ expect(last_response.status).to eq(200)
300
+ end
301
+ end
272
302
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::RegexpValidator do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::SameAsValidator do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::ValuesValidator do
@@ -222,6 +224,11 @@ describe Grape::Validations::ValuesValidator do
222
224
  requires :type, values: { proc: ->(v) { ValuesModel.values.include? v }, message: 'failed check' }
223
225
  end
224
226
  get '/proc/message'
227
+
228
+ params do
229
+ optional :name, type: String, values: %w[a b], allow_blank: true
230
+ end
231
+ get '/allow_blank'
225
232
  end
226
233
  end
227
234
  end
@@ -431,11 +438,21 @@ describe Grape::Validations::ValuesValidator do
431
438
  end.to raise_error Grape::Exceptions::IncompatibleOptionValues
432
439
  end
433
440
 
434
- it 'allows values to be true or false when setting the type to boolean' do
435
- get('/values/optional_boolean', type: true)
436
- expect(last_response.status).to eq 200
437
- expect(last_response.body).to eq({ type: true }.to_json)
441
+ context 'boolean values' do
442
+ it 'allows a value from the list' do
443
+ get('/values/optional_boolean', type: true)
444
+
445
+ expect(last_response.status).to eq 200
446
+ expect(last_response.body).to eq({ type: true }.to_json)
447
+ end
448
+
449
+ it 'rejects a value which is not in the list' do
450
+ get('/values/optional_boolean', type: false)
451
+
452
+ expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
453
+ end
438
454
  end
455
+
439
456
  it 'allows values to be a kind of the coerced type not just an instance of it' do
440
457
  get('/values/coercion', type: 10)
441
458
  expect(last_response.status).to eq 200
@@ -462,6 +479,14 @@ describe Grape::Validations::ValuesValidator do
462
479
  end.to raise_error Grape::Exceptions::IncompatibleOptionValues
463
480
  end
464
481
 
482
+ it 'allows a blank value when the allow_blank option is true' do
483
+ get 'allow_blank', name: nil
484
+ expect(last_response.status).to eq(200)
485
+
486
+ get 'allow_blank', name: ''
487
+ expect(last_response.status).to eq(200)
488
+ end
489
+
465
490
  context 'with a lambda values' do
466
491
  subject do
467
492
  Class.new(Grape::API) do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations do
@@ -9,14 +11,17 @@ describe Grape::Validations do
9
11
 
10
12
  describe 'params' do
11
13
  context 'optional' do
12
- it 'validates when params is present' do
14
+ before do
13
15
  subject.params do
14
16
  optional :a_number, regexp: /^[0-9]+$/
17
+ optional :attachment, type: File
15
18
  end
16
19
  subject.get '/optional' do
17
20
  'optional works!'
18
21
  end
22
+ end
19
23
 
24
+ it 'validates when params is present' do
20
25
  get '/optional', a_number: 'string'
21
26
  expect(last_response.status).to eq(400)
22
27
  expect(last_response.body).to eq('a_number is invalid')
@@ -27,14 +32,7 @@ describe Grape::Validations do
27
32
  end
28
33
 
29
34
  it "doesn't validate when param not present" do
30
- subject.params do
31
- optional :a_number, regexp: /^[0-9]+$/
32
- end
33
- subject.get '/optional' do
34
- 'optional works!'
35
- end
36
-
37
- get '/optional'
35
+ get '/optional', a_number: nil, attachment: nil
38
36
  expect(last_response.status).to eq(200)
39
37
  expect(last_response.body).to eq('optional works!')
40
38
  end
@@ -120,6 +118,62 @@ describe Grape::Validations do
120
118
  end
121
119
  end
122
120
 
121
+ context 'requires with nested params' do
122
+ before do
123
+ subject.params do
124
+ requires :first_level, type: Hash do
125
+ optional :second_level, type: Array do
126
+ requires :value, type: Integer
127
+ optional :name, type: String
128
+ optional :third_level, type: Array do
129
+ requires :value, type: Integer
130
+ optional :name, type: String
131
+ optional :fourth_level, type: Array do
132
+ requires :value, type: Integer
133
+ optional :name, type: String
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ subject.put('/required') { 'required works' }
140
+ end
141
+
142
+ let(:request_params) do
143
+ {
144
+ first_level: {
145
+ second_level: [
146
+ { value: 1, name: 'Lisa' },
147
+ {
148
+ value: 2,
149
+ name: 'James',
150
+ third_level: [
151
+ { value: 'three', name: 'Sophie' },
152
+ {
153
+ value: 4,
154
+ name: 'Jenny',
155
+ fourth_level: [
156
+ { name: 'Samuel' }, { value: 6, name: 'Jane' }
157
+ ]
158
+ }
159
+ ]
160
+ }
161
+ ]
162
+ }
163
+ }
164
+ end
165
+
166
+ it 'validates correctly in deep nested params' do
167
+ put '/required', request_params.to_json, 'CONTENT_TYPE' => 'application/json'
168
+
169
+ expect(last_response.status).to eq(400)
170
+ expect(last_response.body).to eq(
171
+ 'first_level[second_level][1][third_level][0][value] is invalid, ' \
172
+ 'first_level[second_level][1][third_level][1][fourth_level][0][value] is missing'
173
+ )
174
+ end
175
+ end
176
+
123
177
  context 'requires :all using Grape::Entity documentation' do
124
178
  def define_requires_all
125
179
  documentation = {
@@ -436,7 +490,7 @@ describe Grape::Validations do
436
490
  class DateRangeValidator < Grape::Validations::Base
437
491
  def validate_param!(attr_name, params)
438
492
  return if params[attr_name][:from] <= params[attr_name][:to]
439
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'"
493
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'")
440
494
  end
441
495
  end
442
496
  end
@@ -851,7 +905,7 @@ describe Grape::Validations do
851
905
  class Customvalidator < Grape::Validations::Base
852
906
  def validate_param!(attr_name, params)
853
907
  return if params[attr_name] == 'im custom'
854
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: 'is not custom!'
908
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: 'is not custom!')
855
909
  end
856
910
  end
857
911
  end
@@ -999,7 +1053,7 @@ describe Grape::Validations do
999
1053
  class CustomvalidatorWithOptions < Grape::Validations::Base
1000
1054
  def validate_param!(attr_name, params)
1001
1055
  return if params[attr_name] == @option[:text]
1002
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message
1056
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
1003
1057
  end
1004
1058
  end
1005
1059
  end
@@ -1364,7 +1418,7 @@ describe Grape::Validations do
1364
1418
  it 'errors when two or more are present' do
1365
1419
  get '/custom_message/exactly_one_of', beer: 'string', wine: 'anotherstring'
1366
1420
  expect(last_response.status).to eq(400)
1367
- expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter is required'
1421
+ expect(last_response.body).to eq 'beer, wine are missing, exactly one parameter is required'
1368
1422
  end
1369
1423
  end
1370
1424
 
@@ -1383,7 +1437,7 @@ describe Grape::Validations do
1383
1437
  it 'errors when two or more are present' do
1384
1438
  get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
1385
1439
  expect(last_response.status).to eq(400)
1386
- expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter must be provided'
1440
+ expect(last_response.body).to eq 'beer, wine are mutually exclusive'
1387
1441
  end
1388
1442
  end
1389
1443
 
@@ -1423,7 +1477,7 @@ describe Grape::Validations do
1423
1477
  it 'errors when two or more are present' do
1424
1478
  get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
1425
1479
  expect(last_response.status).to eq(400)
1426
- expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2], nested2[0][juice_nested2] are missing, exactly one parameter must be provided'
1480
+ expect(last_response.body).to eq 'nested2[0][beer_nested2], nested2[0][wine_nested2] are mutually exclusive'
1427
1481
  end
1428
1482
  end
1429
1483
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Json do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Xml do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  shared_examples_for 'versioning' do
2
4
  it 'sets the API version' do
3
5
  subject.format :txt