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
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class ContractScopeValidator
7
+ extend Grape::Util::FreezeOnNew
8
+
9
+ attr_reader :schema
10
+
11
+ def initialize(schema:)
12
+ @schema = schema
13
+ end
14
+
15
+ # Validates a given request.
16
+ # @param request [Grape::Request] the request currently being handled
17
+ # @raise [Grape::Exceptions::ValidationArrayErrors] if validation failed
18
+ # @return [void]
19
+ def validate(request)
20
+ res = schema.call(request.params)
21
+
22
+ if res.success?
23
+ request.params.deep_merge!(res.to_h)
24
+ return
25
+ end
26
+
27
+ raise Grape::Exceptions::ValidationArrayErrors.new(build_errors_from_messages(res.errors.messages))
28
+ end
29
+
30
+ def fail_fast?
31
+ false
32
+ end
33
+
34
+ private
35
+
36
+ def build_errors_from_messages(messages)
37
+ messages.map do |message|
38
+ full_name = message.path.first.to_s
39
+ full_name << "[#{message.path[1..].join('][')}]" if message.path.size > 1
40
+ Grape::Exceptions::Validation.new(params: full_name, message: message.text)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -4,27 +4,24 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class DefaultValidator < Base
7
- def initialize(attrs, options, required, scope, **opts)
8
- @default = options
7
+ def initialize(attrs, options, required, scope, opts)
9
8
  super
10
- end
11
-
12
- def validate_param!(attr_name, params)
13
- params[attr_name] = if @default.is_a? Proc
14
- @default.call
15
- elsif @default.frozen? || !@default.duplicable?
16
- @default
17
- else
18
- @default.dup
19
- end
9
+ # !important, lazy call at runtime
10
+ @default_call =
11
+ if options.is_a?(Proc)
12
+ options.arity.zero? ? proc { options.call } : options
13
+ elsif options.duplicable?
14
+ proc { options.dup }
15
+ else
16
+ proc { options }
17
+ end
20
18
  end
21
19
 
22
20
  def validate!(params)
23
- attrs = SingleAttributeIterator.new(self, @scope, params)
24
- attrs.each do |resource_params, attr_name|
25
- next unless @scope.meets_dependency?(resource_params, params)
21
+ @iterator.each(params) do |resource_params, attr_name|
22
+ next unless scope.meets_dependency?(resource_params, params)
26
23
 
27
- validate_param!(attr_name, resource_params) if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
24
+ resource_params[attr_name] = @default_call.call(resource_params) if hash_like?(resource_params) && resource_params[attr_name].nil?
28
25
  end
29
26
  end
30
27
  end
@@ -4,12 +4,19 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class ExactlyOneOfValidator < MultipleParamsBase
7
+ def initialize(attrs, options, required, scope, opts)
8
+ super
9
+ @exactly_one_exception_message = message(:exactly_one)
10
+ @mutual_exclusion_exception_message = message(:mutual_exclusion)
11
+ end
12
+
7
13
  def validate_params!(params)
8
- keys = keys_in_common(params)
14
+ known_keys = all_keys
15
+ keys = keys_in_common(params, known_keys)
9
16
  return if keys.length == 1
10
- raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.length.zero?
11
17
 
12
- raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
18
+ validation_error!(known_keys, @exactly_one_exception_message) if keys.empty?
19
+ validation_error!(keys, @mutual_exclusion_exception_message)
13
20
  end
14
21
  end
15
22
  end
@@ -4,19 +4,29 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class ExceptValuesValidator < Base
7
- def initialize(attrs, options, required, scope, **opts)
8
- @except = options.is_a?(Hash) ? options[:value] : options
7
+ default_message_key :except_values
8
+
9
+ def initialize(attrs, options, required, scope, opts)
9
10
  super
11
+ except = option_value
12
+ except_proc = except.is_a?(Proc)
13
+ raise ArgumentError, 'except_values Proc must have arity of zero (use values: with a one-arity predicate for per-element checks)' if except_proc && !except.arity.zero?
14
+
15
+ # Zero-arity procs (e.g. -> { User.pluck(:role) }) must be called per-request,
16
+ # not at definition time, so they are wrapped in a lambda to defer execution.
17
+ @excepts_call = except_proc ? except : -> { except }
10
18
  end
11
19
 
12
20
  def validate_param!(attr_name, params)
13
- return unless params.respond_to?(:key?) && params.key?(attr_name)
21
+ return unless hash_like?(params) && params.key?(attr_name)
14
22
 
15
- excepts = @except.is_a?(Proc) ? @except.call : @except
23
+ excepts = @excepts_call.call
16
24
  return if excepts.nil?
17
25
 
18
26
  param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
19
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:except_values)) if param_array.any? { |param| excepts.include?(param) }
27
+ return if param_array.none? { |param| excepts.include?(param) }
28
+
29
+ validation_error!(attr_name)
20
30
  end
21
31
  end
22
32
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ class LengthValidator < Base
7
+ def initialize(attrs, options, required, scope, opts)
8
+ super
9
+
10
+ @min, @max, @is = options.values_at(:min, :max, :is)
11
+ validate_boundary!(:min, @min)
12
+ validate_boundary!(:max, @max)
13
+ raise ArgumentError, "min `#{@min}` cannot be greater than max `#{@max}`" if @min && @max && @min > @max
14
+
15
+ return if @is.nil?
16
+ raise ArgumentError, 'is must be an integer greater than zero' unless @is.is_a?(Integer) && @is.positive?
17
+ raise ArgumentError, 'is cannot be combined with min or max' unless @min.nil? && @max.nil?
18
+ end
19
+
20
+ def validate_param!(attr_name, params)
21
+ return unless hash_like?(params)
22
+
23
+ param = params[attr_name]
24
+
25
+ return unless param.respond_to?(:length)
26
+
27
+ return unless (!@min.nil? && param.length < @min) || (!@max.nil? && param.length > @max) || (!@is.nil? && param.length != @is)
28
+
29
+ validation_error!(attr_name, message do
30
+ if @min && @max
31
+ translate(:length, min: @min, max: @max)
32
+ elsif @min
33
+ translate(:length_min, min: @min)
34
+ elsif @max
35
+ translate(:length_max, max: @max)
36
+ else
37
+ translate(:length_is, is: @is)
38
+ end
39
+ end)
40
+ end
41
+
42
+ private
43
+
44
+ def validate_boundary!(name, val)
45
+ raise ArgumentError, "`#{name}` must be an integer greater than or equal to zero" if !val.nil? && (!val.is_a?(Integer) || val.negative?)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -5,28 +5,31 @@ module Grape
5
5
  module Validators
6
6
  class MultipleParamsBase < Base
7
7
  def validate!(params)
8
- attributes = MultipleAttributesIterator.new(self, @scope, params)
9
- array_errors = []
8
+ array_errors = nil
10
9
 
11
- attributes.each do |resource_params|
10
+ @iterator.each(params) do |resource_params|
12
11
  validate_params!(resource_params)
13
12
  rescue Grape::Exceptions::Validation => e
14
- array_errors << e
13
+ (array_errors ||= []) << e
15
14
  end
16
15
 
17
- raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
16
+ raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors
18
17
  end
19
18
 
20
19
  private
21
20
 
22
- def keys_in_common(resource_params)
23
- return [] unless resource_params.is_a?(Hash)
21
+ def iterator_class
22
+ MultipleAttributesIterator
23
+ end
24
+
25
+ def keys_in_common(resource_params, known_keys = all_keys)
26
+ return [] unless hash_like?(resource_params)
24
27
 
25
- all_keys & resource_params.keys.map! { |attr| @scope.full_name(attr) }
28
+ known_keys & resource_params.keys.map! { |attr| scope.full_name(attr) }
26
29
  end
27
30
 
28
31
  def all_keys
29
- attrs.map { |attr| @scope.full_name(attr) }
32
+ attrs.map { |attr| scope.full_name(attr) }
30
33
  end
31
34
  end
32
35
  end
@@ -3,12 +3,14 @@
3
3
  module Grape
4
4
  module Validations
5
5
  module Validators
6
- class MutualExclusionValidator < MultipleParamsBase
6
+ class MutuallyExclusiveValidator < MultipleParamsBase
7
+ default_message_key :mutual_exclusion
8
+
7
9
  def validate_params!(params)
8
10
  keys = keys_in_common(params)
9
11
  return if keys.length <= 1
10
12
 
11
- raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
13
+ validation_error!(keys)
12
14
  end
13
15
  end
14
16
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Validations
5
+ module Validators
6
+ # Validates that a Hash parameter matches at least one of a set of
7
+ # variant schemas. Each variant is a list of pre-built validators
8
+ # captured by evaluating the variant's block in a {Grape::Validations::OneofCollector}-backed
9
+ # {ParamsScope}. At request time we try each variant in order against
10
+ # a deep-dup of the value; the first variant that produces no errors
11
+ # wins and its (possibly coerced) hash replaces the original.
12
+ class OneofValidator < Base
13
+ default_message_key :oneof
14
+
15
+ def initialize(attrs, options, required, scope, opts)
16
+ super
17
+ @variants = Array(options)
18
+ end
19
+
20
+ def validate_param!(attr_name, params)
21
+ return unless hash_like?(params)
22
+
23
+ value = params[attr_name]
24
+ return if value.nil? && !required?
25
+
26
+ winning_candidate = nil
27
+ @variants.each do |variant_validators|
28
+ candidate = value.deep_dup
29
+ if variant_matches?(variant_validators, candidate)
30
+ winning_candidate = candidate
31
+ break
32
+ end
33
+ end
34
+
35
+ return params[attr_name] = winning_candidate if winning_candidate
36
+
37
+ validation_error!(attr_name)
38
+ end
39
+
40
+ private
41
+
42
+ def variant_matches?(variant_validators, candidate)
43
+ variant_validators.each { |v| v.validate!(candidate) }
44
+ true
45
+ rescue Grape::Exceptions::Validation, Grape::Exceptions::ValidationArrayErrors
46
+ false
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -4,10 +4,12 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class PresenceValidator < Base
7
+ default_message_key :presence
8
+
7
9
  def validate_param!(attr_name, params)
8
- return if params.respond_to?(:key?) && params.key?(attr_name)
10
+ return if hash_like?(params) && params.key?(attr_name)
9
11
 
10
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
12
+ validation_error!(attr_name)
11
13
  end
12
14
  end
13
15
  end
@@ -4,11 +4,19 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class RegexpValidator < Base
7
+ default_message_key :regexp
8
+
9
+ def initialize(attrs, options, required, scope, opts)
10
+ super
11
+ @value = option_value
12
+ end
13
+
7
14
  def validate_param!(attr_name, params)
8
- return unless params.respond_to?(:key?) && params.key?(attr_name)
9
- return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.match?((options_key?(:value) ? @option[:value] : @option)) }
15
+ return unless hash_like?(params) && params.key?(attr_name)
16
+
17
+ return if Array.wrap(params[attr_name]).all? { |param| param.nil? || scrub(param.to_s).match?(@value) }
10
18
 
11
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
19
+ validation_error!(attr_name)
12
20
  end
13
21
  end
14
22
  end
@@ -4,24 +4,16 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class SameAsValidator < Base
7
- def validate_param!(attr_name, params)
8
- confirmation = options_key?(:value) ? @option[:value] : @option
9
- return if params[attr_name] == params[confirmation]
10
-
11
- raise Grape::Exceptions::Validation.new(
12
- params: [@scope.full_name(attr_name)],
13
- message: build_message
14
- )
7
+ def initialize(attrs, options, required, scope, opts)
8
+ super
9
+ @value = option_value
15
10
  end
16
11
 
17
- private
12
+ def validate_param!(attr_name, params)
13
+ return unless hash_like?(params)
14
+ return if params[attr_name] == params[@value]
18
15
 
19
- def build_message
20
- if options_key?(:message)
21
- @option[:message]
22
- else
23
- format I18n.t(:same_as, scope: 'grape.errors.messages'), parameter: @option
24
- end
16
+ validation_error!(attr_name, message { translate(:same_as, parameter: @value) })
25
17
  end
26
18
  end
27
19
  end
@@ -4,92 +4,63 @@ module Grape
4
4
  module Validations
5
5
  module Validators
6
6
  class ValuesValidator < Base
7
- def initialize(attrs, options, required, scope, **opts)
8
- if options.is_a?(Hash)
9
- @excepts = options[:except]
10
- @values = options[:value]
11
- @proc = options[:proc]
7
+ default_message_key :values
12
8
 
13
- ActiveSupport::Deprecation.warn('The values validator except option is deprecated. Use the except validator instead.') if @excepts
14
-
15
- raise ArgumentError, 'proc must be a Proc' if @proc && !@proc.is_a?(Proc)
16
-
17
- ActiveSupport::Deprecation.warn('The values validator proc option is deprecated. The lambda expression can now be assigned directly to values.') if @proc
9
+ def initialize(attrs, options, required, scope, opts)
10
+ super
11
+ values = option_value
12
+
13
+ # Zero-arity procs return a collection per-request (e.g. DB-backed lists).
14
+ # Non-zero-arity procs are per-element predicates, called directly at validation time.
15
+ # Non-Proc values are wrapped in a zero-arity lambda for a uniform call interface.
16
+ if values.is_a?(Proc)
17
+ @values_call = values
18
+ @values_is_predicate = !values.arity.zero?
18
19
  else
19
- @excepts = nil
20
- @values = nil
21
- @proc = nil
22
- @values = options
20
+ @values_call = -> { values }
21
+ @values_is_predicate = false
23
22
  end
24
- super
25
23
  end
26
24
 
27
25
  def validate_param!(attr_name, params)
28
- return unless params.is_a?(Hash)
26
+ return unless hash_like?(params)
29
27
 
30
- val = params[attr_name]
28
+ val = scrub(params[attr_name])
31
29
 
32
30
  return if val.nil? && !required_for_root_scope?
31
+ return if val != false && val.blank? && allow_blank?
32
+ return if check_values?(val, attr_name)
33
33
 
34
- # don't forget that +false.blank?+ is true
35
- return if val != false && val.blank? && @allow_blank
36
-
37
- param_array = val.nil? ? [nil] : Array.wrap(val)
38
-
39
- raise validation_exception(attr_name, except_message) \
40
- unless check_excepts(param_array)
41
-
42
- raise validation_exception(attr_name, message(:values)) \
43
- unless check_values(param_array, attr_name)
44
-
45
- raise validation_exception(attr_name, message(:values)) \
46
- if @proc && !validate_proc(@proc, param_array)
34
+ validation_error!(attr_name)
47
35
  end
48
36
 
49
37
  private
50
38
 
51
- def check_values(param_array, attr_name)
52
- values = @values.is_a?(Proc) && @values.arity.zero? ? @values.call : @values
53
- return true if values.nil?
54
-
55
- begin
56
- return param_array.all? { |param| values.call(param) } if values.is_a? Proc
57
- rescue StandardError => e
58
- warn "Error '#{e}' raised while validating attribute '#{attr_name}'"
59
- return false
60
- end
61
- param_array.all? { |param| values.include?(param) }
62
- end
63
-
64
- def check_excepts(param_array)
65
- excepts = @excepts.is_a?(Proc) ? @excepts.call : @excepts
66
- return true if excepts.nil?
67
-
68
- param_array.none? { |param| excepts.include?(param) }
69
- end
39
+ def check_values?(val, attr_name)
40
+ param_array = val.nil? ? [nil] : Array.wrap(val)
70
41
 
71
- def validate_proc(proc, param_array)
72
- case proc.arity
73
- when 0
74
- param_array.all? { |_param| proc.call }
75
- when 1
76
- param_array.all? { |param| proc.call(param) }
42
+ if @values_is_predicate
43
+ begin
44
+ param_array.all? { |param| @values_call.call(param) }
45
+ rescue StandardError => e
46
+ warn "Error '#{e}' raised while validating attribute '#{attr_name}'"
47
+ false
48
+ end
77
49
  else
78
- raise ArgumentError, 'proc arity must be 0 or 1'
79
- end
80
- end
50
+ values = @values_call.call
51
+ return true if values.nil?
81
52
 
82
- def except_message
83
- options = instance_variable_get(:@option)
84
- options_key?(:except_message) ? options[:except_message] : message(:except_values)
53
+ param_array.all? { |param| values.include?(param) }
54
+ end
85
55
  end
86
56
 
87
57
  def required_for_root_scope?
88
- @required && @scope.root?
89
- end
58
+ return false unless required?
59
+
60
+ current_scope = scope
61
+ current_scope = current_scope.parent while current_scope.lateral?
90
62
 
91
- def validation_exception(attr_name, message)
92
- Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message)
63
+ current_scope.root?
93
64
  end
94
65
  end
95
66
  end
@@ -1,34 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grape
4
- # Registry to store and locate known Validators.
5
4
  module Validations
5
+ extend Grape::Util::Registry
6
+
6
7
  module_function
7
8
 
8
- def validators
9
- @validators ||= {}
10
- end
9
+ def require_validator(short_name)
10
+ raise Grape::Exceptions::UnknownValidator, short_name unless registry.key?(short_name)
11
11
 
12
- # Register a new validator, so it can be used to validate parameters.
13
- # @param short_name [String] all lower-case, no spaces
14
- # @param klass [Class] the validator class. Should inherit from
15
- # Validations::Base.
16
- def register_validator(short_name, klass)
17
- validators[short_name] = klass
12
+ registry[short_name]
18
13
  end
19
14
 
20
- def deregister_validator(short_name)
21
- validators.delete(short_name)
22
- end
15
+ def build_short_name(klass)
16
+ return if klass.name.blank?
23
17
 
24
- # Find a validator and if not found will try to load it
25
- def require_validator(short_name)
26
- str_name = short_name.to_s
27
- validators.fetch(str_name) do
28
- Grape::Validations::Validators.const_get("#{str_name.camelize}Validator")
29
- end
30
- rescue NameError
31
- raise Grape::Exceptions::UnknownValidator.new(short_name)
18
+ klass.name.demodulize.underscore.delete_suffix('_validator')
32
19
  end
33
20
  end
34
21
  end
data/lib/grape/version.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Grape
4
- # The current version of Grape.
5
- VERSION = '1.8.0'
4
+ VERSION = '4.0.1'
6
5
  end
data/lib/grape/xml.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ # Since multi_xml 0.9.0 the canonical constant is MultiXML; MultiXml is a
5
+ # deprecated alias (removed in v1.0) that warns on use. Prefer MultiXML so
6
+ # Grape::Xml.parse doesn't trip the deprecation, falling back to the legacy
7
+ # constant and then ActiveSupport::XmlMini.
8
+ # https://github.com/sferik/multi_xml/blob/v0.9.1/CHANGELOG.md
9
+ if defined?(::MultiXML)
10
+ Xml = ::MultiXML
11
+ elsif defined?(::MultiXml)
12
+ Xml = ::MultiXml
13
+ else
14
+ Xml = ::ActiveSupport::XmlMini
15
+ Xml::ParseError = StandardError
16
+ end
17
+ end