grape 1.8.0 → 4.0.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 (317) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +517 -1
  3. data/CONTRIBUTING.md +19 -11
  4. data/README.md +841 -545
  5. data/UPGRADING.md +1553 -7
  6. data/grape.gemspec +11 -14
  7. data/lib/grape/api/instance.rb +77 -160
  8. data/lib/grape/api.rb +75 -107
  9. data/lib/grape/content_types.rb +67 -9
  10. data/lib/grape/cookies.rb +28 -25
  11. data/lib/grape/declared_params_handler.rb +116 -0
  12. data/lib/grape/dry_types.rb +48 -6
  13. data/lib/grape/dsl/callbacks.rb +8 -58
  14. data/lib/grape/dsl/declared.rb +35 -0
  15. data/lib/grape/dsl/desc.rb +25 -63
  16. data/lib/grape/dsl/entity.rb +106 -0
  17. data/lib/grape/dsl/headers.rb +2 -2
  18. data/lib/grape/dsl/helpers.rb +83 -64
  19. data/lib/grape/dsl/inside_route.rb +69 -296
  20. data/lib/grape/dsl/logger.rb +4 -9
  21. data/lib/grape/dsl/middleware.rb +22 -40
  22. data/lib/grape/dsl/parameters.rb +88 -94
  23. data/lib/grape/dsl/request_response.rb +143 -147
  24. data/lib/grape/dsl/rescue_options.rb +25 -0
  25. data/lib/grape/dsl/routing.rb +333 -191
  26. data/lib/grape/dsl/settings.rb +29 -135
  27. data/lib/grape/dsl/validations.rb +39 -32
  28. data/lib/grape/dsl/version_options.rb +23 -0
  29. data/lib/grape/endpoint/options.rb +25 -0
  30. data/lib/grape/endpoint.rb +319 -269
  31. data/lib/grape/{util/env.rb → env.rb} +8 -8
  32. data/lib/grape/error_formatter/base.rb +57 -21
  33. data/lib/grape/error_formatter/json.rb +13 -15
  34. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  35. data/lib/grape/error_formatter/txt.rb +12 -18
  36. data/lib/grape/error_formatter/xml.rb +3 -13
  37. data/lib/grape/error_formatter.rb +9 -25
  38. data/lib/grape/exceptions/base.rb +22 -58
  39. data/lib/grape/exceptions/error_response.rb +48 -0
  40. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  41. data/lib/grape/exceptions/internal_server_error.rb +16 -0
  42. data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
  43. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  44. data/lib/grape/exceptions/invalid_message_body.rb +1 -1
  45. data/lib/grape/exceptions/invalid_version_header.rb +1 -1
  46. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  47. data/lib/grape/exceptions/method_not_allowed.rb +1 -1
  48. data/lib/grape/exceptions/missing_group_type.rb +0 -2
  49. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  50. data/lib/grape/exceptions/request_error.rb +11 -0
  51. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  52. data/lib/grape/exceptions/unknown_error_formatter.rb +11 -0
  53. data/lib/grape/exceptions/unknown_parameter.rb +1 -1
  54. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  55. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  56. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  57. data/lib/grape/exceptions/validation.rb +28 -11
  58. data/lib/grape/exceptions/validation_array_errors.rb +5 -0
  59. data/lib/grape/exceptions/validation_errors.rb +22 -26
  60. data/lib/grape/formatter/base.rb +16 -0
  61. data/lib/grape/formatter/json.rb +5 -6
  62. data/lib/grape/formatter/serializable_hash.rb +7 -10
  63. data/lib/grape/formatter/txt.rb +3 -5
  64. data/lib/grape/formatter/xml.rb +4 -6
  65. data/lib/grape/formatter.rb +7 -25
  66. data/lib/grape/json.rb +46 -0
  67. data/lib/grape/locale/en.yml +44 -42
  68. data/lib/grape/middleware/auth/base.rb +11 -33
  69. data/lib/grape/middleware/auth/dsl.rb +12 -34
  70. data/lib/grape/middleware/auth/strategies.rb +1 -2
  71. data/lib/grape/middleware/base.rb +56 -33
  72. data/lib/grape/middleware/error.rb +290 -91
  73. data/lib/grape/middleware/formatter.rb +164 -106
  74. data/lib/grape/middleware/precomputed_content_types.rb +51 -0
  75. data/lib/grape/middleware/stack.rb +38 -40
  76. data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
  77. data/lib/grape/middleware/versioner/base.rb +66 -0
  78. data/lib/grape/middleware/versioner/header.rb +44 -129
  79. data/lib/grape/middleware/versioner/param.rb +4 -25
  80. data/lib/grape/middleware/versioner/path.rb +48 -25
  81. data/lib/grape/middleware/versioner.rb +7 -14
  82. data/lib/grape/mountable.rb +22 -0
  83. data/lib/grape/namespace.rb +21 -14
  84. data/lib/grape/params_builder/base.rb +20 -0
  85. data/lib/grape/params_builder/hash.rb +11 -0
  86. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  87. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  88. data/lib/grape/params_builder.rb +15 -0
  89. data/lib/grape/parser/base.rb +16 -0
  90. data/lib/grape/parser/json.rb +6 -8
  91. data/lib/grape/parser/xml.rb +6 -8
  92. data/lib/grape/parser.rb +5 -23
  93. data/lib/grape/path.rb +8 -94
  94. data/lib/grape/precompiled_json.rb +50 -0
  95. data/lib/grape/railtie.rb +9 -0
  96. data/lib/grape/request.rb +200 -26
  97. data/lib/grape/router/base_route.rb +85 -0
  98. data/lib/grape/router/greedy_route.rb +30 -0
  99. data/lib/grape/router/mustermann_pattern.rb +44 -0
  100. data/lib/grape/router/pattern/path.rb +78 -0
  101. data/lib/grape/router/pattern.rb +77 -35
  102. data/lib/grape/router/route.rb +79 -60
  103. data/lib/grape/router.rb +128 -104
  104. data/lib/grape/serve_stream/file_body.rb +7 -0
  105. data/lib/grape/serve_stream/sendfile_response.rb +3 -5
  106. data/lib/grape/serve_stream/stream_response.rb +7 -0
  107. data/lib/grape/testing.rb +33 -0
  108. data/lib/grape/util/api_description.rb +67 -0
  109. data/lib/grape/util/cache.rb +22 -5
  110. data/lib/grape/util/deep_freeze.rb +34 -0
  111. data/lib/grape/util/endpoint_configuration.rb +1 -1
  112. data/lib/grape/util/freeze_on_new.rb +20 -0
  113. data/lib/grape/util/header.rb +13 -0
  114. data/lib/grape/util/inheritable_setting.rb +777 -39
  115. data/lib/grape/util/lazy/base.rb +16 -0
  116. data/lib/grape/util/lazy/block.rb +22 -0
  117. data/lib/grape/util/lazy/value.rb +31 -0
  118. data/lib/grape/util/lazy/value_array.rb +21 -0
  119. data/lib/grape/util/lazy/value_enumerable.rb +31 -0
  120. data/lib/grape/util/lazy/value_hash.rb +21 -0
  121. data/lib/grape/util/media_type.rb +74 -0
  122. data/lib/grape/util/path_normalizer.rb +37 -0
  123. data/lib/grape/util/registry.rb +37 -0
  124. data/lib/grape/util/shadowed_rescue_handlers.rb +49 -0
  125. data/lib/grape/util/stackable_values.rb +40 -16
  126. data/lib/grape/util/translation.rb +42 -0
  127. data/lib/grape/validations/attributes_iterator.rb +61 -28
  128. data/lib/grape/validations/coerce_options.rb +21 -0
  129. data/lib/grape/validations/contract_scope.rb +29 -0
  130. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  131. data/lib/grape/validations/oneof_collector.rb +35 -0
  132. data/lib/grape/validations/param_scope_tracker.rb +62 -0
  133. data/lib/grape/validations/params_documentation.rb +52 -0
  134. data/lib/grape/validations/params_scope.rb +223 -306
  135. data/lib/grape/validations/shared_options.rb +19 -0
  136. data/lib/grape/validations/single_attribute_iterator.rb +6 -4
  137. data/lib/grape/validations/types/array_coercer.rb +7 -12
  138. data/lib/grape/validations/types/custom_type_coercer.rb +47 -85
  139. data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
  140. data/lib/grape/validations/types/dry_type_coercer.rb +17 -28
  141. data/lib/grape/validations/types/json.rb +1 -5
  142. data/lib/grape/validations/types/multiple_type_coercer.rb +5 -3
  143. data/lib/grape/validations/types/primitive_coercer.rb +14 -35
  144. data/lib/grape/validations/types/set_coercer.rb +1 -4
  145. data/lib/grape/validations/types/variant_collection_coercer.rb +16 -3
  146. data/lib/grape/validations/types.rb +29 -54
  147. data/lib/grape/validations/validations_spec.rb +164 -0
  148. data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
  149. data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
  150. data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
  151. data/lib/grape/validations/validators/base.rb +118 -37
  152. data/lib/grape/validations/validators/coerce_validator.rb +26 -38
  153. data/lib/grape/validations/validators/contract_scope_validator.rb +46 -0
  154. data/lib/grape/validations/validators/default_validator.rb +13 -16
  155. data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
  156. data/lib/grape/validations/validators/except_values_validator.rb +15 -5
  157. data/lib/grape/validations/validators/length_validator.rb +50 -0
  158. data/lib/grape/validations/validators/multiple_params_base.rb +12 -9
  159. data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
  160. data/lib/grape/validations/validators/oneof_validator.rb +51 -0
  161. data/lib/grape/validations/validators/presence_validator.rb +4 -2
  162. data/lib/grape/validations/validators/regexp_validator.rb +11 -3
  163. data/lib/grape/validations/validators/same_as_validator.rb +7 -15
  164. data/lib/grape/validations/validators/values_validator.rb +36 -65
  165. data/lib/grape/validations.rb +8 -21
  166. data/lib/grape/version.rb +1 -2
  167. data/lib/grape/xml.rb +17 -0
  168. data/lib/grape.rb +96 -288
  169. metadata +83 -294
  170. data/lib/grape/api/helpers.rb +0 -9
  171. data/lib/grape/dsl/api.rb +0 -19
  172. data/lib/grape/dsl/configuration.rb +0 -15
  173. data/lib/grape/eager_load.rb +0 -20
  174. data/lib/grape/exceptions/empty_message_body.rb +0 -11
  175. data/lib/grape/exceptions/missing_option.rb +0 -11
  176. data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
  177. data/lib/grape/exceptions/unknown_options.rb +0 -11
  178. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -27
  179. data/lib/grape/extensions/hash.rb +0 -22
  180. data/lib/grape/extensions/hashie/mash.rb +0 -26
  181. data/lib/grape/http/headers.rb +0 -61
  182. data/lib/grape/middleware/globals.rb +0 -16
  183. data/lib/grape/middleware/helpers.rb +0 -12
  184. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
  185. data/lib/grape/router/attribute_translator.rb +0 -63
  186. data/lib/grape/types/invalid_value.rb +0 -8
  187. data/lib/grape/util/base_inheritable.rb +0 -43
  188. data/lib/grape/util/inheritable_values.rb +0 -31
  189. data/lib/grape/util/json.rb +0 -12
  190. data/lib/grape/util/lazy_block.rb +0 -27
  191. data/lib/grape/util/lazy_object.rb +0 -43
  192. data/lib/grape/util/lazy_value.rb +0 -91
  193. data/lib/grape/util/registrable.rb +0 -15
  194. data/lib/grape/util/reverse_stackable_values.rb +0 -20
  195. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  196. data/lib/grape/util/xml.rb +0 -10
  197. data/lib/grape/validations/attributes_doc.rb +0 -58
  198. data/lib/grape/validations/types/build_coercer.rb +0 -94
  199. data/lib/grape/validations/validator_factory.rb +0 -15
  200. data/spec/grape/api/custom_validations_spec.rb +0 -213
  201. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  202. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  203. data/spec/grape/api/documentation_spec.rb +0 -59
  204. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  205. data/spec/grape/api/instance_spec.rb +0 -103
  206. data/spec/grape/api/invalid_format_spec.rb +0 -45
  207. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  208. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  209. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  210. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  211. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  212. data/spec/grape/api/recognize_path_spec.rb +0 -21
  213. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  214. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  215. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  216. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  217. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  218. data/spec/grape/api_remount_spec.rb +0 -509
  219. data/spec/grape/api_spec.rb +0 -4356
  220. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  221. data/spec/grape/dsl/desc_spec.rb +0 -98
  222. data/spec/grape/dsl/headers_spec.rb +0 -62
  223. data/spec/grape/dsl/helpers_spec.rb +0 -100
  224. data/spec/grape/dsl/inside_route_spec.rb +0 -531
  225. data/spec/grape/dsl/logger_spec.rb +0 -24
  226. data/spec/grape/dsl/middleware_spec.rb +0 -60
  227. data/spec/grape/dsl/parameters_spec.rb +0 -180
  228. data/spec/grape/dsl/request_response_spec.rb +0 -225
  229. data/spec/grape/dsl/routing_spec.rb +0 -275
  230. data/spec/grape/dsl/settings_spec.rb +0 -261
  231. data/spec/grape/dsl/validations_spec.rb +0 -55
  232. data/spec/grape/endpoint/declared_spec.rb +0 -846
  233. data/spec/grape/endpoint_spec.rb +0 -1085
  234. data/spec/grape/entity_spec.rb +0 -336
  235. data/spec/grape/exceptions/base_spec.rb +0 -81
  236. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
  237. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  238. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  239. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  240. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  241. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
  242. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  243. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  244. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  245. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  246. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
  247. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  248. data/spec/grape/exceptions/validation_spec.rb +0 -19
  249. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  250. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  251. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  252. data/spec/grape/grape_spec.rb +0 -9
  253. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  254. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  255. data/spec/grape/integration/rack_spec.rb +0 -51
  256. data/spec/grape/loading_spec.rb +0 -44
  257. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  258. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  259. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  260. data/spec/grape/middleware/base_spec.rb +0 -221
  261. data/spec/grape/middleware/error_spec.rb +0 -85
  262. data/spec/grape/middleware/exception_spec.rb +0 -294
  263. data/spec/grape/middleware/formatter_spec.rb +0 -461
  264. data/spec/grape/middleware/globals_spec.rb +0 -30
  265. data/spec/grape/middleware/stack_spec.rb +0 -155
  266. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  267. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  268. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  269. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  270. data/spec/grape/middleware/versioner_spec.rb +0 -21
  271. data/spec/grape/named_api_spec.rb +0 -19
  272. data/spec/grape/parser_spec.rb +0 -86
  273. data/spec/grape/path_spec.rb +0 -252
  274. data/spec/grape/presenters/presenter_spec.rb +0 -71
  275. data/spec/grape/request_spec.rb +0 -126
  276. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  277. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  278. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  279. data/spec/grape/util/stackable_values_spec.rb +0 -128
  280. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  281. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  282. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  283. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
  284. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  285. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
  286. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  287. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  288. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  289. data/spec/grape/validations/types_spec.rb +0 -111
  290. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  291. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  292. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  293. data/spec/grape/validations/validators/base_spec.rb +0 -38
  294. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  295. data/spec/grape/validations/validators/default_spec.rb +0 -463
  296. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  297. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  298. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  299. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  300. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  301. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  302. data/spec/grape/validations/validators/values_spec.rb +0 -733
  303. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  304. data/spec/grape/validations_spec.rb +0 -2030
  305. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  306. data/spec/integration/multi_json/json_spec.rb +0 -7
  307. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  308. data/spec/shared/deprecated_class_examples.rb +0 -16
  309. data/spec/shared/versioning_examples.rb +0 -215
  310. data/spec/spec_helper.rb +0 -52
  311. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  312. data/spec/support/chunks.rb +0 -14
  313. data/spec/support/content_type_helpers.rb +0 -15
  314. data/spec/support/endpoint_faker.rb +0 -25
  315. data/spec/support/file_streamer.rb +0 -13
  316. data/spec/support/integer_helpers.rb +0 -13
  317. data/spec/support/versioned_helpers.rb +0 -55
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/validations/types/json'
4
- require 'grape/validations/types/file'
5
-
6
3
  module Grape
7
4
  module Validations
8
5
  # Module for code related to grape's system for
@@ -78,7 +75,7 @@ module Grape
78
75
  # @return [Boolean] +true+ if the given value will be treated as
79
76
  # a list of types.
80
77
  def multiple?(type)
81
- (type.is_a?(Array) || type.is_a?(Set)) && type.size > 1
78
+ array_or_set?(type) && type.size > 1
82
79
  end
83
80
 
84
81
  # Does Grape provide special coercion and validation
@@ -101,17 +98,18 @@ module Grape
101
98
  GROUPS.include? type
102
99
  end
103
100
 
101
+ def array_or_set?(type)
102
+ type.is_a?(Array) || type.is_a?(Set)
103
+ end
104
+
104
105
  # A valid custom type must implement a class-level `parse` method, taking
105
106
  # one String argument and returning the parsed value in its correct type.
106
107
  #
107
108
  # @param type [Class] type to check
108
109
  # @return [Boolean] whether or not the type can be used as a custom type
109
110
  def custom?(type)
110
- !primitive?(type) &&
111
- !structure?(type) &&
112
- !multiple?(type) &&
113
- type.respond_to?(:parse) &&
114
- type.method(:parse).arity == 1
111
+ !(primitive?(type) || structure?(type) || multiple?(type)) &&
112
+ type.respond_to?(:parse) && type.method(:parse).arity == 1
115
113
  end
116
114
 
117
115
  # Is the declared type an +Array+ or +Set+ of a {#custom?} type?
@@ -120,9 +118,7 @@ module Grape
120
118
  # @return [Boolean] true if +type+ is a collection of a type that implements
121
119
  # its own +#parse+ method.
122
120
  def collection_of_custom?(type)
123
- (type.is_a?(Array) || type.is_a?(Set)) &&
124
- type.length == 1 &&
125
- (custom?(type.first) || special?(type.first))
121
+ array_or_set?(type) && type.length == 1 && (custom?(type.first) || special?(type.first))
126
122
  end
127
123
 
128
124
  def map_special(type)
@@ -158,59 +154,38 @@ module Grape
158
154
  # @return [Object] object to be used
159
155
  # for coercion and type validation
160
156
  def build_coercer(type, method: nil, strict: false)
161
- cache_instance(type, method, strict) do
162
- create_coercer_instance(type, method, strict)
163
- end
164
- end
157
+ # no cache since unique
158
+ return create_coercer_instance(type, method, strict) if method.respond_to?(:call)
165
159
 
166
- def create_coercer_instance(type, method, strict)
167
- # Maps a custom type provided by Grape, it doesn't map types wrapped by collections!!!
168
- type = Types.map_special(type)
169
-
170
- # Use a special coercer for multiply-typed parameters.
171
- if Types.multiple?(type)
172
- MultipleTypeCoercer.new(type, method)
173
-
174
- # Use a special coercer for custom types and coercion methods.
175
- elsif method || Types.custom?(type)
176
- CustomTypeCoercer.new(type, method)
177
-
178
- # Special coercer for collections of types that implement a parse method.
179
- # CustomTypeCoercer (above) already handles such types when an explicit coercion
180
- # method is supplied.
181
- elsif Types.collection_of_custom?(type)
182
- Types::CustomTypeCollectionCoercer.new(
183
- Types.map_special(type.first), type.is_a?(Set)
184
- )
185
- else
186
- DryTypeCoercer.coercer_instance_for(type, strict)
187
- end
160
+ CoercerCache[[type, method, strict]]
188
161
  end
189
162
 
190
- def cache_instance(type, method, strict, &_block)
191
- key = cache_key(type, method, strict)
163
+ def create_coercer_instance(type, method, strict)
164
+ # map_special doesn't recurse into collections — applied only to the top-level type here.
165
+ mapped = map_special(type)
192
166
 
193
- return @__cache[key] if @__cache.key?(key)
167
+ # Multiply-typed parameters, e.g. types: [Integer, String].
168
+ return MultipleTypeCoercer.new(mapped, method) if multiple?(mapped)
194
169
 
195
- instance = yield
170
+ # User-supplied coercion method, or a custom type with its own #parse.
171
+ return CustomTypeCoercer.new(mapped, method) if method || custom?(mapped)
196
172
 
197
- @__cache_write_lock.synchronize do
198
- @__cache[key] = instance
199
- end
173
+ # Array/Set of a custom type — CustomTypeCoercer already handles single
174
+ # custom types when an explicit coercion method is supplied.
175
+ return CustomTypeCollectionCoercer.new(map_special(mapped.first), set: mapped.is_a?(Set)) if collection_of_custom?(mapped)
200
176
 
201
- instance
177
+ # Fallback: let dry-types handle primitives, structures, and known specials.
178
+ DryTypeCoercer.coercer_instance_for(mapped, strict:)
202
179
  end
203
180
 
204
- def cache_key(type, method, strict)
205
- [type, method, strict].each_with_object(+'_') do |val, memo|
206
- next if val.nil?
207
-
208
- memo << '_' << val.to_s
181
+ class CoercerCache < Grape::Util::Cache
182
+ def initialize
183
+ super
184
+ @cache = Hash.new do |h, (type, method, strict)|
185
+ h[[type, method, strict]] = Grape::Validations::Types.create_coercer_instance(type, method, strict)
186
+ end
209
187
  end
210
188
  end
211
-
212
- instance_variable_set(:@__cache, {})
213
- instance_variable_set(:@__cache_write_lock, Mutex.new)
214
189
  end
215
190
  end
216
191
  end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ # Frozen value object holding everything {ParamsScope#validates} needs to
6
+ # know about a single +requires+/+optional+ declaration. Built once from
7
+ # the raw validations hash supplied by the DSL; the raw hash is never
8
+ # mutated.
9
+ #
10
+ # Splits the raw entries into three logical buckets:
11
+ #
12
+ # * Spec-consumed keys (type/types/coerce*, presence/message,
13
+ # default/fail_fast, doc keys) — exposed via named accessors and never
14
+ # handed to validator dispatch.
15
+ # * Shared opts (allow_blank, fail_fast) — read by every validator at
16
+ # construction time via {#shared_opts}.
17
+ # * Validator entries (everything else, e.g. +regexp+, +length+, +values+,
18
+ # +allow_blank+, custom validators) — exposed via {#validator_entries}
19
+ # for the dispatch loop.
20
+ #
21
+ # Same key can land in more than one bucket (e.g. +allow_blank+ is both a
22
+ # shared opt and a validator entry; +length+ is both a doc source and a
23
+ # validator entry).
24
+ class ValidationsSpec
25
+ # Keys consumed by the spec itself; must NOT be dispatched as validators
26
+ # by the caller. Documentation-only keys are filtered through a separate
27
+ # set so that dual-purpose keys (length, default, values, except_values)
28
+ # aren't accidentally swallowed.
29
+ SPEC_CONSUMED_KEYS = %i[
30
+ type types coerce coerce_with coerce_message
31
+ presence message
32
+ fail_fast
33
+ desc description documentation
34
+ ].freeze
35
+
36
+ attr_reader :raw, :coerce_type, :coerce_method, :coerce_message, :presence_options, :values, :except_values, :default, :allow_blank, :fail_fast, :shared_opts, :validator_entries
37
+
38
+ def self.from(validations)
39
+ new(validations)
40
+ end
41
+
42
+ def initialize(raw)
43
+ raise ArgumentError, ':type may not be supplied with :types' if raw.key?(:type) && raw.key?(:types)
44
+
45
+ @raw = raw
46
+ @coerce_type, @coerce_message, @coerce_method = parse_coerce(raw)
47
+ @values = resolve_value(raw[:values])
48
+ @except_values = resolve_value(raw[:except_values])
49
+ @default = raw[:default]
50
+ @presence_options = raw[:presence]
51
+ @allow_blank = resolve_value(raw[:allow_blank])
52
+ @fail_fast = raw[:fail_fast] || false
53
+
54
+ @guessed_coerce_type = guess_coerce_type(declared_coerce_type, @values, @except_values)
55
+ @shared_opts = { allow_blank: @allow_blank, fail_fast: @fail_fast }.freeze
56
+ @validator_entries = build_validator_entries(raw)
57
+
58
+ validate!
59
+
60
+ freeze
61
+ end
62
+
63
+ def required?
64
+ !@presence_options.nil? && @presence_options != false
65
+ end
66
+
67
+ def coerce_options
68
+ CoerceOptions.new(type: @coerce_type, coerce_method: @coerce_method, message: @coerce_message)
69
+ end
70
+
71
+ private
72
+
73
+ # Cross-field consistency checks on the parsed declaration. Run at
74
+ # construction so an incoherent spec (e.g. a +default+ outside +values+,
75
+ # or +values+ whose elements don't match +type+) can never exist —
76
+ # callers no longer have to remember to invoke these separately.
77
+ #
78
+ # NB. +@guessed_coerce_type+ is used only for this check, never for
79
+ # +coerce_options+ — a bare +type: Array+ combined with scalar
80
+ # +values:+ must still coerce to +Array+ at runtime (only the elements
81
+ # are scalar), so the real +@coerce_type+ is never overwritten by it.
82
+ def validate!
83
+ check_incompatible_option_values(@default, @values, @except_values)
84
+ validate_value_coercion(@guessed_coerce_type, @values, @except_values)
85
+ end
86
+
87
+ def check_incompatible_option_values(default, values, except_values)
88
+ return if default.nil? || default.is_a?(Proc)
89
+
90
+ raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) if values && !values.is_a?(Proc) && Array(default).any? { |def_val| !values.include?(def_val) }
91
+
92
+ return unless except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }
93
+
94
+ raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
95
+ end
96
+
97
+ def validate_value_coercion(coerce_type, *values_list)
98
+ return unless coerce_type
99
+
100
+ element_type = coerce_type.is_a?(Enumerable) ? coerce_type.first : coerce_type
101
+ values_list.each do |values|
102
+ next if !values || values.is_a?(Proc)
103
+
104
+ value_types = values.is_a?(Range) ? [values.begin, values.end].compact : values
105
+ value_types = value_types.map { |type| Grape::API::Boolean.build(type) } if element_type == Grape::API::Boolean
106
+ raise Grape::Exceptions::IncompatibleOptionValues.new(:type, element_type, :values, values) unless value_types.all?(element_type)
107
+ end
108
+ end
109
+
110
+ def build_validator_entries(raw)
111
+ raw.reject do |k, _|
112
+ SPEC_CONSUMED_KEYS.include?(k) || ParamsScope::RESERVED_DOCUMENTATION_KEYWORDS.include?(k)
113
+ end.freeze
114
+ end
115
+
116
+ def parse_coerce(raw)
117
+ if raw.key?(:type)
118
+ coerce, coerce_message = extract_value_and_message(raw[:type])
119
+ coerce_with = raw[:coerce_with]
120
+ return [Types::VariantCollectionCoercer.new(coerce, coerce_with), coerce_message, nil] if Types.multiple?(coerce)
121
+ elsif raw.key?(:types)
122
+ coerce, coerce_message = extract_value_and_message(raw[:types])
123
+ coerce_with = raw[:coerce_with]
124
+ else
125
+ coerce = raw[:coerce]
126
+ coerce_message = raw[:coerce_message]
127
+ coerce_with = raw[:coerce_with]
128
+ end
129
+
130
+ [coerce, coerce_message, coerce_with]
131
+ end
132
+
133
+ def extract_value_and_message(opt)
134
+ return [opt, nil] unless opt.is_a?(Hash)
135
+
136
+ [opt[:value], opt[:message]]
137
+ end
138
+
139
+ def resolve_value(opt)
140
+ opt.is_a?(Hash) ? opt[:value] : opt
141
+ end
142
+
143
+ # The type as written in the DSL. Identical to +@coerce_type+ except for
144
+ # a multiple-type declaration (+type: [Integer, String]+), where
145
+ # {#parse_coerce} wraps the declared list in a
146
+ # {Types::VariantCollectionCoercer} for runtime coercion — the
147
+ # definition-time coherence checks need the list back, since the wrapper
148
+ # matches nothing under +===+.
149
+ def declared_coerce_type
150
+ @coerce_type.is_a?(Types::VariantCollectionCoercer) ? @coerce_type.types : @coerce_type
151
+ end
152
+
153
+ def guess_coerce_type(coerce_type, *values_list)
154
+ return coerce_type unless coerce_type == Array
155
+
156
+ values_list.each do |values|
157
+ next if !values || values.is_a?(Proc)
158
+ return values.first.class if values.is_a?(Range) || !values.empty?
159
+ end
160
+ coerce_type
161
+ end
162
+ end
163
+ end
164
+ end
@@ -4,11 +4,14 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class AllOrNoneOfValidator < MultipleParamsBase
7
+ default_message_key :all_or_none
8
+
7
9
  def validate_params!(params)
8
- keys = keys_in_common(params)
9
- return if keys.empty? || keys.length == all_keys.length
10
+ known_keys = all_keys
11
+ keys = keys_in_common(params, known_keys)
12
+ return if keys.empty? || keys.length == attrs.length
10
13
 
11
- raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:all_or_none))
14
+ validation_error!(known_keys)
12
15
  end
13
16
  end
14
17
  end
@@ -4,15 +4,20 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class AllowBlankValidator < Base
7
- def validate_param!(attr_name, params)
8
- return if (options_key?(:value) ? @option[:value] : @option) || !params.is_a?(Hash)
7
+ default_message_key :blank
8
+
9
+ def initialize(attrs, options, required, scope, opts)
10
+ super
11
+ @value = option_value
12
+ end
9
13
 
10
- value = params[attr_name]
11
- value = value.strip if value.respond_to?(:strip)
14
+ def validate_param!(attr_name, params)
15
+ return if @value || !hash_like?(params)
12
16
 
17
+ value = scrub(params[attr_name])
13
18
  return if value == false || value.present?
14
19
 
15
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:blank))
20
+ validation_error!(attr_name)
16
21
  end
17
22
  end
18
23
  end
@@ -4,10 +4,13 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class AtLeastOneOfValidator < MultipleParamsBase
7
+ default_message_key :at_least_one
8
+
7
9
  def validate_params!(params)
8
- return unless keys_in_common(params).empty?
10
+ known_keys = all_keys
11
+ return if hash_like?(params) && known_keys.intersect?(params.keys.map { |attr| scope.full_name(attr) })
9
12
 
10
- raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:at_least_one))
13
+ validation_error!(known_keys)
11
14
  end
12
15
  end
13
16
  end
@@ -3,25 +3,72 @@
3
3
  module Grape
4
4
  module Validations
5
5
  module Validators
6
+ # Base class for all parameter validators.
7
+ #
8
+ # == Freeze contract
9
+ # Validator instances are shared across requests and are frozen after
10
+ # initialization (via +.new+). All inputs (+options+, +opts+, +attrs+)
11
+ # arrive pre-frozen from the DSL boundary, so subclass ivars derived
12
+ # from them are frozen by construction. Lazy ivar assignment
13
+ # (e.g. +memoize+, <tt>||=</tt>) will raise +FrozenError+ at request time.
6
14
  class Base
15
+ extend Forwardable
16
+ extend Grape::Util::FreezeOnNew
17
+ include Grape::Util::Translation
18
+
7
19
  attr_reader :attrs
8
20
 
21
+ # +allow_blank+ / +fail_fast+ are read straight off the internal
22
+ # {Grape::Validations::SharedOptions} value object; no per-validator
23
+ # ivars. The object is built from the +opts+ Hash in #initialize, so
24
+ # the public 5th-argument contract stays a plain Hash.
25
+ def_delegators :@opts, :allow_blank, :fail_fast
26
+
27
+ alias fail_fast? fail_fast
28
+ alias allow_blank? allow_blank
29
+
30
+ class << self
31
+ # Declares the default I18n message key used by +validation_error!+.
32
+ # Subclasses that only need a single fixed error message can declare it
33
+ # at the class level instead of overriding +initialize+:
34
+ #
35
+ # class MyValidator < Grape::Validations::Validators::Base
36
+ # default_message_key :my_error
37
+ # end
38
+ #
39
+ # The key is resolved through +message+, so a per-option +:message+
40
+ # override still takes precedence.
41
+ def default_message_key(key = nil)
42
+ if key
43
+ @default_message_key = key
44
+ else
45
+ @default_message_key || (superclass.respond_to?(:default_message_key) ? superclass.default_message_key : nil)
46
+ end
47
+ end
48
+
49
+ def inherited(klass)
50
+ super
51
+ Validations.register(klass)
52
+ end
53
+ end
54
+
9
55
  # Creates a new Validator from options specified
10
56
  # by a +requires+ or +optional+ directive during
11
57
  # parameter definition.
12
58
  # @param attrs [Array] names of attributes to which the Validator applies
13
- # @param options [Object] implementation-dependent Validator options
59
+ # @param options [Object] implementation-dependent Validator options; deep-frozen on assignment
14
60
  # @param required [Boolean] attribute(s) are required or optional
15
61
  # @param scope [ParamsScope] parent scope for this Validator
16
- # @param opts [Array] additional validation options
17
- def initialize(attrs, options, required, scope, *opts)
18
- @attrs = Array(attrs)
19
- @option = options
62
+ # @param opts [Hash] shared validator options; only +:allow_blank+ and
63
+ # +:fail_fast+ are consulted (other keys ignored, as before)
64
+ def initialize(attrs, options, required, scope, opts)
65
+ @attrs = Array(attrs).freeze
66
+ @options = Grape::Util::DeepFreeze.deep_freeze(options)
20
67
  @required = required
21
68
  @scope = scope
22
- opts = opts.any? ? opts.shift : {}
23
- @fail_fast = opts.fetch(:fail_fast, false)
24
- @allow_blank = opts.fetch(:allow_blank, false)
69
+ @opts = SharedOptions.new(**opts.slice(:allow_blank, :fail_fast))
70
+ @exception_message = message(self.class.default_message_key) if self.class.default_message_key
71
+ @iterator = iterator_class.new(@attrs, @scope).freeze
25
72
  end
26
73
 
27
74
  # Validates a given request.
@@ -30,62 +77,96 @@ module Grape
30
77
  # @raise [Grape::Exceptions::Validation] if validation failed
31
78
  # @return [void]
32
79
  def validate(request)
33
- return unless @scope.should_validate?(request.params)
80
+ params = request.params
81
+ return unless scope.should_validate?(params)
34
82
 
35
- validate!(request.params)
83
+ validate!(params)
36
84
  end
37
85
 
38
86
  # Validates a given parameter hash.
39
- # @note Override #validate if you need to access the entire request.
87
+ # @note Override #validate_param! for per-parameter validation,
88
+ # or #validate if you need access to the entire request.
40
89
  # @param params [Hash] parameters to validate
41
90
  # @raise [Grape::Exceptions::Validation] if validation failed
42
91
  # @return [void]
43
92
  def validate!(params)
44
- attributes = SingleAttributeIterator.new(self, @scope, params)
45
93
  # we collect errors inside array because
46
94
  # there may be more than one error per field
47
- array_errors = []
95
+ array_errors = nil
48
96
 
49
- attributes.each do |val, attr_name, empty_val|
50
- next if !@scope.required? && empty_val
51
- next unless @scope.meets_dependency?(val, params)
97
+ @iterator.each(params) do |val, attr_name, empty_val|
98
+ next if !scope.required? && empty_val
99
+ next unless scope.meets_dependency?(val, params)
52
100
 
53
- validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
101
+ validate_param!(attr_name, val) if required? || (hash_like?(val) && val.key?(attr_name))
54
102
  rescue Grape::Exceptions::Validation => e
55
- array_errors << e
103
+ (array_errors ||= []) << e
56
104
  end
57
105
 
58
- raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
106
+ raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors
107
+ end
108
+
109
+ protected
110
+
111
+ # Validates a single attribute. Override in subclasses.
112
+ # @param attr_name [Symbol, String] the attribute name
113
+ # @param params [Hash] the parameter hash containing the attribute
114
+ # @raise [Grape::Exceptions::Validation] if validation failed
115
+ # @return [void]
116
+ def validate_param!(attr_name, params)
117
+ raise NotImplementedError
118
+ end
119
+
120
+ private
121
+
122
+ attr_reader :options, :scope, :required, :exception_message
123
+
124
+ alias required? required
125
+
126
+ # The AttributesIterator subclass used to walk this validator's
127
+ # attributes. Built once in #initialize and reused across requests.
128
+ def iterator_class
129
+ SingleAttributeIterator
59
130
  end
60
131
 
61
- def self.inherited(klass)
62
- return if klass.name.blank?
132
+ def validation_error!(attr_name_or_params, message = exception_message)
133
+ params = attr_name_or_params.is_a?(Array) ? attr_name_or_params : scope.full_name(attr_name_or_params)
134
+ raise Grape::Exceptions::Validation.new(params:, message:)
135
+ end
63
136
 
64
- short_validator_name = klass.name.demodulize.underscore.delete_suffix('_validator')
65
- Validations.register_validator(short_validator_name, klass)
137
+ def hash_like?(obj)
138
+ obj.respond_to?(:key?)
66
139
  end
67
140
 
141
+ def options_key?(key, given_options = nil)
142
+ current_options = given_options || options
143
+ hash_like?(current_options) && current_options.key?(key) && !current_options[key].nil?
144
+ end
145
+
146
+ # Returns the effective message for a validation error.
147
+ # Prefers an explicit +:message+ option, then +default_key+.
148
+ # If both are nil, the block (if given) is called to compute a fallback —
149
+ # useful for validators that build a message Hash for deferred i18n interpolation.
150
+ # @example
151
+ # @exception_message = message(:presence) # symbol key or custom message
152
+ # @exception_message = message { build_hash_message } # computed fallback
68
153
  def message(default_key = nil)
69
- options = instance_variable_get(:@option)
70
- options_key?(:message) ? options[:message] : default_key
154
+ key = options_key?(:message) ? options[:message] : default_key
155
+ return key unless key.nil?
156
+
157
+ yield if block_given?
71
158
  end
72
159
 
73
- def options_key?(key, options = nil)
74
- options = instance_variable_get(:@option) if options.nil?
75
- options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
160
+ def option_value
161
+ options_key?(:value) ? options[:value] : options
76
162
  end
77
163
 
78
- def fail_fast?
79
- @fail_fast
164
+ def scrub(value)
165
+ return value if !value.respond_to?(:valid_encoding?) || value.valid_encoding?
166
+
167
+ value.scrub
80
168
  end
81
169
  end
82
170
  end
83
171
  end
84
172
  end
85
-
86
- Grape::Validations::Base = Class.new(Grape::Validations::Validators::Base) do
87
- def self.inherited(*)
88
- ActiveSupport::Deprecation.warn 'Grape::Validations::Base is deprecated! Use Grape::Validations::Validators::Base instead.'
89
- super
90
- end
91
- end
@@ -4,22 +4,35 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class CoerceValidator < Base
7
- def initialize(attrs, options, required, scope, **opts)
7
+ default_message_key :coerce
8
+
9
+ def initialize(attrs, options, required, scope, opts)
8
10
  super
9
11
 
10
- @converter = if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
11
- type
12
- else
13
- Types.build_coercer(type, method: @option[:method])
14
- end
12
+ @exception_message = options.message if options.message
13
+
14
+ raw_type = options.type
15
+ type = hash_like?(raw_type) ? raw_type[:value] : raw_type
16
+ @converter =
17
+ if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
18
+ type
19
+ else
20
+ Types.build_coercer(type, method: options.coerce_method)
21
+ end
15
22
  end
16
23
 
24
+ # The attribute is read once and held: +coerce_value+ only ever sees the
25
+ # value, never the Hash, so the two further reads the identity check
26
+ # used to make came back with the same object -- through a
27
+ # HashWithIndifferentAccess key conversion each time, for every coerced
28
+ # attribute of every request.
17
29
  def validate_param!(attr_name, params)
18
- raise validation_exception(attr_name) unless params.is_a? Hash
30
+ validation_error!(attr_name) unless hash_like?(params)
19
31
 
20
- new_value = coerce_value(params[attr_name])
32
+ value = params[attr_name]
33
+ new_value = coerce_value(value)
21
34
 
22
- raise validation_exception(attr_name, new_value.message) unless valid_type?(new_value)
35
+ validation_error!(attr_name, new_value.message || exception_message) if new_value.is_a?(Types::InvalidValue)
23
36
 
24
37
  # Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
25
38
  # which looses wrappers for hashes and arrays after reassigning values
@@ -30,45 +43,20 @@ module Grape
30
43
  # h[:list] = list
31
44
  # h
32
45
  # => #<Hashie::Mash list=[1, 2, 3, 4]>
33
- return if params[attr_name].instance_of?(new_value.class) && params[attr_name] == new_value
46
+ return if value.instance_of?(new_value.class) && value == new_value
34
47
 
35
48
  params[attr_name] = new_value
36
49
  end
37
50
 
38
51
  private
39
52
 
40
- # @!attribute [r] converter
41
- # Object that will be used for parameter coercion and type checking.
42
- #
43
- # See {Types.build_coercer}
44
- #
45
- # @return [Object]
46
- attr_reader :converter
47
-
48
- def valid_type?(val)
49
- !val.is_a?(Types::InvalidValue)
50
- end
51
-
53
+ # Calls the converter built at definition time.
54
+ # Custom coercers may raise; any StandardError is treated as an invalid value.
52
55
  def coerce_value(val)
53
- converter.call(val)
54
- # Some custom types might fail, so it should be treated as an invalid value
56
+ @converter.call(val)
55
57
  rescue StandardError
56
58
  Types::InvalidValue.new
57
59
  end
58
-
59
- # Type to which the parameter will be coerced.
60
- #
61
- # @return [Class]
62
- def type
63
- @option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
64
- end
65
-
66
- def validation_exception(attr_name, custom_msg = nil)
67
- Grape::Exceptions::Validation.new(
68
- params: [@scope.full_name(attr_name)],
69
- message: custom_msg || message(:coerce)
70
- )
71
- end
72
60
  end
73
61
  end
74
62
  end