grape 1.8.0 → 3.3.5

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 (312) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +391 -1
  3. data/CONTRIBUTING.md +3 -11
  4. data/README.md +648 -552
  5. data/UPGRADING.md +803 -7
  6. data/grape.gemspec +11 -14
  7. data/lib/grape/api/instance.rb +68 -158
  8. data/lib/grape/api.rb +71 -107
  9. data/lib/grape/content_types.rb +23 -11
  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 +14 -58
  14. data/lib/grape/dsl/declared.rb +35 -0
  15. data/lib/grape/dsl/desc.rb +15 -63
  16. data/lib/grape/dsl/entity.rb +88 -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 +56 -295
  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 +50 -82
  23. data/lib/grape/dsl/request_response.rb +144 -146
  24. data/lib/grape/dsl/rescue_options.rb +24 -0
  25. data/lib/grape/dsl/routing.rb +261 -186
  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 +19 -0
  30. data/lib/grape/endpoint.rb +277 -250
  31. data/lib/grape/{util/env.rb → env.rb} +1 -8
  32. data/lib/grape/error_formatter/base.rb +56 -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 +5 -25
  38. data/lib/grape/exceptions/base.rb +22 -58
  39. data/lib/grape/exceptions/error_response.rb +45 -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_parameter.rb +1 -1
  53. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  54. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  55. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  56. data/lib/grape/exceptions/validation.rb +25 -9
  57. data/lib/grape/exceptions/validation_array_errors.rb +5 -0
  58. data/lib/grape/exceptions/validation_errors.rb +22 -26
  59. data/lib/grape/formatter/base.rb +16 -0
  60. data/lib/grape/formatter/json.rb +4 -6
  61. data/lib/grape/formatter/serializable_hash.rb +6 -10
  62. data/lib/grape/formatter/txt.rb +3 -5
  63. data/lib/grape/formatter/xml.rb +4 -6
  64. data/lib/grape/formatter.rb +7 -25
  65. data/lib/grape/json.rb +46 -0
  66. data/lib/grape/locale/en.yml +43 -42
  67. data/lib/grape/middleware/auth/base.rb +11 -33
  68. data/lib/grape/middleware/auth/dsl.rb +37 -31
  69. data/lib/grape/middleware/auth/strategies.rb +1 -2
  70. data/lib/grape/middleware/base.rb +56 -33
  71. data/lib/grape/middleware/deprecated_options_hash_access.rb +19 -0
  72. data/lib/grape/middleware/error.rb +183 -91
  73. data/lib/grape/middleware/formatter.rb +94 -105
  74. data/lib/grape/middleware/precomputed_content_types.rb +46 -0
  75. data/lib/grape/middleware/stack.rb +27 -38
  76. data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
  77. data/lib/grape/middleware/versioner/base.rb +72 -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 +13 -32
  81. data/lib/grape/middleware/versioner.rb +7 -14
  82. data/lib/grape/mountable.rb +22 -0
  83. data/lib/grape/namespace.rb +18 -12
  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 +38 -63
  94. data/lib/grape/railtie.rb +9 -0
  95. data/lib/grape/request.rb +168 -26
  96. data/lib/grape/router/base_route.rb +48 -0
  97. data/lib/grape/router/greedy_route.rb +26 -0
  98. data/lib/grape/router/mustermann_pattern.rb +44 -0
  99. data/lib/grape/router/pattern.rb +30 -34
  100. data/lib/grape/router/route.rb +28 -61
  101. data/lib/grape/router.rb +71 -86
  102. data/lib/grape/serve_stream/file_body.rb +1 -0
  103. data/lib/grape/serve_stream/sendfile_response.rb +3 -5
  104. data/lib/grape/serve_stream/stream_response.rb +1 -0
  105. data/lib/grape/testing.rb +33 -0
  106. data/lib/grape/util/api_description.rb +58 -0
  107. data/lib/grape/util/base_inheritable.rb +14 -14
  108. data/lib/grape/util/cache.rb +1 -3
  109. data/lib/grape/util/deep_freeze.rb +35 -0
  110. data/lib/grape/util/endpoint_configuration.rb +1 -1
  111. data/lib/grape/util/header.rb +13 -0
  112. data/lib/grape/util/inheritable_setting.rb +51 -27
  113. data/lib/grape/util/inheritable_values.rb +7 -5
  114. data/lib/grape/util/lazy/base.rb +16 -0
  115. data/lib/grape/util/lazy/block.rb +22 -0
  116. data/lib/grape/util/lazy/value.rb +31 -0
  117. data/lib/grape/util/lazy/value_array.rb +21 -0
  118. data/lib/grape/util/lazy/value_enumerable.rb +31 -0
  119. data/lib/grape/util/lazy/value_hash.rb +21 -0
  120. data/lib/grape/util/media_type.rb +67 -0
  121. data/lib/grape/util/path_normalizer.rb +34 -0
  122. data/lib/grape/util/registry.rb +27 -0
  123. data/lib/grape/util/reverse_stackable_values.rb +1 -6
  124. data/lib/grape/util/stackable_values.rb +12 -14
  125. data/lib/grape/util/translation.rb +42 -0
  126. data/lib/grape/validations/attributes_iterator.rb +43 -27
  127. data/lib/grape/validations/coerce_options.rb +21 -0
  128. data/lib/grape/validations/contract_scope.rb +28 -0
  129. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  130. data/lib/grape/validations/oneof_collector.rb +39 -0
  131. data/lib/grape/validations/param_scope_tracker.rb +62 -0
  132. data/lib/grape/validations/params_documentation.rb +52 -0
  133. data/lib/grape/validations/params_scope.rb +180 -303
  134. data/lib/grape/validations/shared_options.rb +19 -0
  135. data/lib/grape/validations/single_attribute_iterator.rb +2 -2
  136. data/lib/grape/validations/types/array_coercer.rb +4 -7
  137. data/lib/grape/validations/types/custom_type_coercer.rb +41 -85
  138. data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
  139. data/lib/grape/validations/types/dry_type_coercer.rb +15 -28
  140. data/lib/grape/validations/types/json.rb +0 -2
  141. data/lib/grape/validations/types/primitive_coercer.rb +11 -35
  142. data/lib/grape/validations/types/set_coercer.rb +1 -4
  143. data/lib/grape/validations/types/variant_collection_coercer.rb +11 -0
  144. data/lib/grape/validations/types.rb +29 -54
  145. data/lib/grape/validations/validations_spec.rb +164 -0
  146. data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
  147. data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
  148. data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
  149. data/lib/grape/validations/validators/base.rb +120 -36
  150. data/lib/grape/validations/validators/coerce_validator.rb +18 -36
  151. data/lib/grape/validations/validators/contract_scope_validator.rb +45 -0
  152. data/lib/grape/validations/validators/default_validator.rb +13 -16
  153. data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
  154. data/lib/grape/validations/validators/except_values_validator.rb +15 -5
  155. data/lib/grape/validations/validators/length_validator.rb +48 -0
  156. data/lib/grape/validations/validators/multiple_params_base.rb +12 -9
  157. data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
  158. data/lib/grape/validations/validators/oneof_validator.rb +49 -0
  159. data/lib/grape/validations/validators/presence_validator.rb +4 -2
  160. data/lib/grape/validations/validators/regexp_validator.rb +11 -3
  161. data/lib/grape/validations/validators/same_as_validator.rb +6 -15
  162. data/lib/grape/validations/validators/values_validator.rb +36 -65
  163. data/lib/grape/validations.rb +8 -21
  164. data/lib/grape/version.rb +1 -2
  165. data/lib/grape/xml.rb +17 -0
  166. data/lib/grape.rb +75 -288
  167. metadata +79 -291
  168. data/lib/grape/api/helpers.rb +0 -9
  169. data/lib/grape/dsl/api.rb +0 -19
  170. data/lib/grape/dsl/configuration.rb +0 -15
  171. data/lib/grape/eager_load.rb +0 -20
  172. data/lib/grape/exceptions/empty_message_body.rb +0 -11
  173. data/lib/grape/exceptions/missing_option.rb +0 -11
  174. data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
  175. data/lib/grape/exceptions/unknown_options.rb +0 -11
  176. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -27
  177. data/lib/grape/extensions/hash.rb +0 -22
  178. data/lib/grape/extensions/hashie/mash.rb +0 -26
  179. data/lib/grape/http/headers.rb +0 -61
  180. data/lib/grape/middleware/globals.rb +0 -16
  181. data/lib/grape/middleware/helpers.rb +0 -12
  182. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
  183. data/lib/grape/router/attribute_translator.rb +0 -63
  184. data/lib/grape/types/invalid_value.rb +0 -8
  185. data/lib/grape/util/json.rb +0 -12
  186. data/lib/grape/util/lazy_block.rb +0 -27
  187. data/lib/grape/util/lazy_object.rb +0 -43
  188. data/lib/grape/util/lazy_value.rb +0 -91
  189. data/lib/grape/util/registrable.rb +0 -15
  190. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  191. data/lib/grape/util/xml.rb +0 -10
  192. data/lib/grape/validations/attributes_doc.rb +0 -58
  193. data/lib/grape/validations/types/build_coercer.rb +0 -94
  194. data/lib/grape/validations/validator_factory.rb +0 -15
  195. data/spec/grape/api/custom_validations_spec.rb +0 -213
  196. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  197. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  198. data/spec/grape/api/documentation_spec.rb +0 -59
  199. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  200. data/spec/grape/api/instance_spec.rb +0 -103
  201. data/spec/grape/api/invalid_format_spec.rb +0 -45
  202. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  203. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  204. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  205. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  206. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  207. data/spec/grape/api/recognize_path_spec.rb +0 -21
  208. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  209. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  210. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  211. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  212. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  213. data/spec/grape/api_remount_spec.rb +0 -509
  214. data/spec/grape/api_spec.rb +0 -4356
  215. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  216. data/spec/grape/dsl/desc_spec.rb +0 -98
  217. data/spec/grape/dsl/headers_spec.rb +0 -62
  218. data/spec/grape/dsl/helpers_spec.rb +0 -100
  219. data/spec/grape/dsl/inside_route_spec.rb +0 -531
  220. data/spec/grape/dsl/logger_spec.rb +0 -24
  221. data/spec/grape/dsl/middleware_spec.rb +0 -60
  222. data/spec/grape/dsl/parameters_spec.rb +0 -180
  223. data/spec/grape/dsl/request_response_spec.rb +0 -225
  224. data/spec/grape/dsl/routing_spec.rb +0 -275
  225. data/spec/grape/dsl/settings_spec.rb +0 -261
  226. data/spec/grape/dsl/validations_spec.rb +0 -55
  227. data/spec/grape/endpoint/declared_spec.rb +0 -846
  228. data/spec/grape/endpoint_spec.rb +0 -1085
  229. data/spec/grape/entity_spec.rb +0 -336
  230. data/spec/grape/exceptions/base_spec.rb +0 -81
  231. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
  232. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  233. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  234. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  235. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  236. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
  237. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  238. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  239. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  240. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  241. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
  242. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  243. data/spec/grape/exceptions/validation_spec.rb +0 -19
  244. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  245. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  246. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  247. data/spec/grape/grape_spec.rb +0 -9
  248. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  249. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  250. data/spec/grape/integration/rack_spec.rb +0 -51
  251. data/spec/grape/loading_spec.rb +0 -44
  252. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  253. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  254. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  255. data/spec/grape/middleware/base_spec.rb +0 -221
  256. data/spec/grape/middleware/error_spec.rb +0 -85
  257. data/spec/grape/middleware/exception_spec.rb +0 -294
  258. data/spec/grape/middleware/formatter_spec.rb +0 -461
  259. data/spec/grape/middleware/globals_spec.rb +0 -30
  260. data/spec/grape/middleware/stack_spec.rb +0 -155
  261. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  262. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  263. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  264. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  265. data/spec/grape/middleware/versioner_spec.rb +0 -21
  266. data/spec/grape/named_api_spec.rb +0 -19
  267. data/spec/grape/parser_spec.rb +0 -86
  268. data/spec/grape/path_spec.rb +0 -252
  269. data/spec/grape/presenters/presenter_spec.rb +0 -71
  270. data/spec/grape/request_spec.rb +0 -126
  271. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  272. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  273. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  274. data/spec/grape/util/stackable_values_spec.rb +0 -128
  275. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  276. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  277. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  278. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
  279. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  280. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
  281. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  282. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  283. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  284. data/spec/grape/validations/types_spec.rb +0 -111
  285. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  286. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  287. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  288. data/spec/grape/validations/validators/base_spec.rb +0 -38
  289. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  290. data/spec/grape/validations/validators/default_spec.rb +0 -463
  291. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  292. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  293. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  294. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  295. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  296. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  297. data/spec/grape/validations/validators/values_spec.rb +0 -733
  298. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  299. data/spec/grape/validations_spec.rb +0 -2030
  300. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  301. data/spec/integration/multi_json/json_spec.rb +0 -7
  302. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  303. data/spec/shared/deprecated_class_examples.rb +0 -16
  304. data/spec/shared/versioning_examples.rb +0 -215
  305. data/spec/spec_helper.rb +0 -52
  306. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  307. data/spec/support/chunks.rb +0 -14
  308. data/spec/support/content_type_helpers.rb +0 -15
  309. data/spec/support/endpoint_faker.rb +0 -25
  310. data/spec/support/file_streamer.rb +0 -13
  311. data/spec/support/integer_helpers.rb +0 -13
  312. data/spec/support/versioned_helpers.rb +0 -55
@@ -1,22 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'attributes_doc'
4
-
5
3
  module Grape
6
4
  module Validations
7
5
  class ParamsScope
8
- attr_accessor :element, :parent, :index
9
- attr_reader :type
6
+ attr_reader :parent, :type, :nearest_array_ancestor, :full_path
7
+
8
+ def qualifying_params
9
+ ParamScopeTracker.current&.qualifying_params(self)
10
+ end
10
11
 
11
12
  include Grape::DSL::Parameters
13
+ include Grape::Validations::ParamsDocumentation
12
14
 
13
15
  # There are a number of documentation options on entities that don't have
14
16
  # corresponding validators. Since there is nowhere that enumerates them all,
15
17
  # we maintain a list of them here and skip looking up validators for them.
16
18
  RESERVED_DOCUMENTATION_KEYWORDS = %i[as required param_type is_array format example].freeze
17
19
 
20
+ SPECIAL_JSON = [JSON, Array[JSON]].freeze
21
+
18
22
  class Attr
19
- attr_accessor :key, :scope
23
+ attr_reader :key, :scope
20
24
 
21
25
  # Open up a new ParamsScope::Attr
22
26
  # @param key [Hash, Symbol] key of attr
@@ -34,9 +38,10 @@ module Grape
34
38
  end
35
39
 
36
40
  def self.attr_key(declared_param_attr)
37
- return attr_key(declared_param_attr.key) if declared_param_attr.is_a?(self)
38
-
39
- if declared_param_attr.is_a?(Hash)
41
+ case declared_param_attr
42
+ when self
43
+ attr_key(declared_param_attr.key)
44
+ when Hash
40
45
  declared_param_attr.transform_values { |value| attrs_keys(value) }
41
46
  else
42
47
  declared_param_attr
@@ -46,39 +51,44 @@ module Grape
46
51
 
47
52
  # Open up a new ParamsScope, allowing parameter definitions per
48
53
  # Grape::DSL::Params.
49
- # @param opts [Hash] options for this scope
50
- # @option opts :element [Symbol] the element that contains this scope; for
51
- # this to be relevant, @parent must be set
52
- # @option opts :element_renamed [Symbol, nil] whenever this scope should
54
+ # @param api [API] the API endpoint to modify
55
+ # @param element [Symbol] the element that contains this scope; for
56
+ # this to be relevant, parent must be set
57
+ # @param element_renamed [Symbol, nil] whenever this scope should
53
58
  # be renamed and to what, given +nil+ no renaming is done
54
- # @option opts :parent [ParamsScope] the scope containing this scope
55
- # @option opts :api [API] the API endpoint to modify
56
- # @option opts :optional [Boolean] whether or not this scope needs to have
59
+ # @param parent [ParamsScope] the scope containing this scope
60
+ # @param optional [Boolean] whether or not this scope needs to have
57
61
  # any parameters set or not
58
- # @option opts :type [Class] a type meant to govern this scope (deprecated)
59
- # @option opts :type [Hash] group options for this scope
60
- # @option opts :dependent_on [Symbol] if present, this scope should only
62
+ # @param type [Class] a type meant to govern this scope (deprecated)
63
+ # @param type [Hash] group options for this scope
64
+ # @param dependent_on [Symbol] if present, this scope should only
61
65
  # validate if this param is present in the parent scope
62
66
  # @yield the instance context, open for parameter definitions
63
- def initialize(opts, &block)
64
- @element = opts[:element]
65
- @element_renamed = opts[:element_renamed]
66
- @parent = opts[:parent]
67
- @api = opts[:api]
68
- @optional = opts[:optional] || false
69
- @type = opts[:type]
70
- @group = opts[:group]
71
- @dependent_on = opts[:dependent_on]
67
+ def initialize(api:, element: nil, element_renamed: nil, parent: nil, optional: false, type: nil, group: nil, dependent_on: nil, &block)
68
+ @element = element
69
+ @element_renamed = element_renamed
70
+ @parent = parent
71
+ @api = api
72
+ @optional = optional
73
+ @type = type
74
+ @group = group
75
+ @dependent_on = dependent_on
76
+ # Must be an ivar: push_declared_params is dispatched on self during
77
+ # instance_eval, so local variables from initialize are unreachable.
78
+ # configure_declared_params consumes it and clears @declared_params to nil.
72
79
  @declared_params = []
73
- @index = nil
80
+ @full_path = build_full_path
74
81
 
75
82
  instance_eval(&block) if block
76
83
 
77
84
  configure_declared_params
85
+ @nearest_array_ancestor = find_nearest_array_ancestor
86
+ freeze
78
87
  end
79
88
 
80
89
  def configuration
81
- @api.configuration.respond_to?(:evaluate) ? @api.configuration.evaluate : @api.configuration
90
+ config = @api.configuration
91
+ config.is_a?(Grape::Util::Lazy::Base) ? config.evaluate : config
82
92
  end
83
93
 
84
94
  # @return [Boolean] whether or not this entire scope needs to be
@@ -88,24 +98,26 @@ module Grape
88
98
 
89
99
  return false if @optional && (scoped_params.blank? || all_element_blank?(scoped_params))
90
100
  return false unless meets_dependency?(scoped_params, parameters)
91
- return true if parent.nil?
101
+ return true if @parent.nil?
92
102
 
93
- parent.should_validate?(parameters)
103
+ @parent.should_validate?(parameters)
94
104
  end
95
105
 
96
106
  def meets_dependency?(params, request_params)
97
107
  return true unless @dependent_on
98
-
99
108
  return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
100
109
 
101
- return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array)
110
+ if params.is_a?(Array)
111
+ filtered = params.flatten.filter { |param| meets_dependency?(param, request_params) }
112
+ ParamScopeTracker.current&.store_qualifying_params(self, filtered)
113
+ return filtered.present?
114
+ end
102
115
 
103
116
  meets_hash_dependency?(params)
104
117
  end
105
118
 
106
119
  def attr_meets_dependency?(params)
107
120
  return true unless @dependent_on
108
-
109
121
  return false if @parent.present? && !@parent.attr_meets_dependency?(params)
110
122
 
111
123
  meets_hash_dependency?(params)
@@ -115,28 +127,27 @@ module Grape
115
127
  # params might be anything what looks like a hash, so it must implement a `key?` method
116
128
  return false unless params.respond_to?(:key?)
117
129
 
118
- @dependent_on.each do |dependency|
130
+ @dependent_on.all? do |dependency|
119
131
  if dependency.is_a?(Hash)
120
- dependency_key = dependency.keys[0]
121
- proc = dependency.values[0]
122
- return false unless proc.call(params.try(:[], dependency_key))
123
- elsif params.respond_to?(:key?) && params.try(:[], dependency).blank?
124
- return false
132
+ key, callable = dependency.first
133
+ callable.call(params[key])
134
+ else
135
+ params[dependency].present?
125
136
  end
126
137
  end
127
-
128
- true
129
138
  end
130
139
 
131
140
  # @return [String] the proper attribute name, with nesting considered.
132
141
  def full_name(name, index: nil)
142
+ tracker = ParamScopeTracker.current
133
143
  if nested?
134
144
  # Find our containing element's name, and append ours.
135
- "#{@parent.full_name(@element)}#{brackets(@index || index)}#{brackets(name)}"
145
+ resolved_index = index || tracker&.index_for(self)
146
+ "#{@parent.full_name(@element)}#{brackets(resolved_index)}#{brackets(name)}"
136
147
  elsif lateral?
137
148
  # Find the name of the element as if it was at the same nesting level
138
149
  # as our parent. We need to forward our index upward to achieve this.
139
- @parent.full_name(name, index: @index)
150
+ @parent.full_name(name, index: tracker&.index_for(self))
140
151
  else
141
152
  # We must be the root scope, so no prefix needed.
142
153
  name.to_s
@@ -176,26 +187,22 @@ module Grape
176
187
  # Adds a parameter declaration to our list of validations.
177
188
  # @param attrs [Array] (see Grape::DSL::Parameters#requires)
178
189
  def push_declared_params(attrs, **opts)
179
- opts = opts.merge(declared_params_scope: self) unless opts.key?(:declared_params_scope)
180
- if lateral?
181
- @parent.push_declared_params(attrs, **opts)
182
- else
183
- push_renamed_param(full_path + [attrs.first], opts[:as]) \
184
- if opts && opts[:as]
190
+ opts[:declared_params_scope] = self unless opts.key?(:declared_params_scope)
191
+ return @parent.push_declared_params(attrs, **opts) if lateral?
185
192
 
186
- @declared_params.concat(attrs.map { |attr| ::Grape::Validations::ParamsScope::Attr.new(attr, opts[:declared_params_scope]) })
187
- end
188
- end
189
-
190
- # Get the full path of the parameter scope in the hierarchy.
191
- #
192
- # @return [Array<Symbol>] the nesting/path of the current parameter scope
193
- def full_path
194
- nested? ? @parent.full_path + [@element] : []
193
+ push_renamed_param(full_path + [attrs.first], opts[:as]) if opts[:as]
194
+ @declared_params.concat(attrs.map { |attr| ::Grape::Validations::ParamsScope::Attr.new(attr, opts[:declared_params_scope]) })
195
195
  end
196
196
 
197
197
  private
198
198
 
199
+ def build_full_path
200
+ return @parent.full_path + [@element] if nested?
201
+ return @parent.full_path if lateral?
202
+
203
+ []
204
+ end
205
+
199
206
  # Add a new parameter which should be renamed when using the +#declared+
200
207
  # method.
201
208
  #
@@ -204,331 +211,201 @@ module Grape
204
211
  # @param new_name [String, Symbol] the new name of the parameter (the
205
212
  # renamed name, with the +as: ...+ semantic)
206
213
  def push_renamed_param(path, new_name)
207
- base = @api.route_setting(:renamed_params) || {}
214
+ api_route_setting = @api.inheritable_setting.route
215
+ base = api_route_setting[:renamed_params] || {}
208
216
  base[Array(path).map(&:to_s)] = new_name.to_s
209
- @api.route_setting(:renamed_params, base)
217
+ api_route_setting[:renamed_params] = base
210
218
  end
211
219
 
212
- def require_required_and_optional_fields(context, opts)
220
+ def require_required_and_optional_fields(context, using:, except: nil)
221
+ except_fields = Array.wrap(except)
222
+ using_fields = using.keys.delete_if { |f| except_fields.include?(f) }
223
+
213
224
  if context == :all
214
- optional_fields = Array(opts[:except])
215
- required_fields = opts[:using].keys - optional_fields
225
+ optional_fields = except_fields
226
+ required_fields = using_fields
216
227
  else # context == :none
217
- required_fields = Array(opts[:except])
218
- optional_fields = opts[:using].keys - required_fields
228
+ required_fields = except_fields
229
+ optional_fields = using_fields
219
230
  end
220
231
  required_fields.each do |field|
221
- field_opts = opts[:using][field]
232
+ field_opts = using[field]
222
233
  raise ArgumentError, "required field not exist: #{field}" unless field_opts
223
234
 
224
- requires(field, field_opts)
235
+ requires(field, **field_opts)
225
236
  end
226
237
  optional_fields.each do |field|
227
- field_opts = opts[:using][field]
228
- optional(field, field_opts) if field_opts
238
+ field_opts = using[field]
239
+ optional(field, **field_opts) if field_opts
229
240
  end
230
241
  end
231
242
 
232
- def require_optional_fields(context, opts)
233
- optional_fields = opts[:using].keys
234
- optional_fields -= Array(opts[:except]) unless context == :all
243
+ def require_optional_fields(context, using:, except: nil)
244
+ optional_fields = using.keys
245
+ unless context == :all
246
+ except_fields = Array.wrap(except)
247
+ optional_fields.delete_if { |f| except_fields.include?(f) }
248
+ end
235
249
  optional_fields.each do |field|
236
- field_opts = opts[:using][field]
237
- optional(field, field_opts) if field_opts
250
+ field_opts = using[field]
251
+ optional(field, **field_opts) if field_opts
238
252
  end
239
253
  end
240
254
 
241
- def validate_attributes(attrs, opts, &block)
242
- validations = opts.clone
243
- validations[:type] ||= Array if block
244
- validates(attrs, validations)
255
+ def validate_attributes(attrs, **opts, &block)
256
+ opts[:type] ||= Array if block
257
+ validates(attrs, opts)
245
258
  end
246
259
 
247
260
  # Returns a new parameter scope, subordinate to the current one and nested
248
- # under the parameter corresponding to `attrs.first`.
249
- # @param attrs [Array] the attributes passed to the `requires` or
250
- # `optional` invocation that opened this scope.
251
- # @param optional [Boolean] whether the parameter this are nested under
261
+ # under the given element.
262
+ # @param element [Symbol] the parameter name under which this scope is nested
263
+ # @param type [Class] the type governing this scope
264
+ # @param as [Symbol, nil] optional renamed name for the element
265
+ # @param optional [Boolean] whether the parameter this scope is nested under
252
266
  # is optional or not (and hence, whether this block's params will be).
253
267
  # @yield parameter scope
254
- def new_scope(attrs, optional = false, &block)
268
+ def new_scope(element, type:, as:, optional: false, &)
255
269
  # if required params are grouped and no type or unsupported type is provided, raise an error
256
- type = attrs[1] ? attrs[1][:type] : nil
257
- if attrs.first && !optional
270
+ if element && !optional
258
271
  raise Grape::Exceptions::MissingGroupType if type.nil?
259
272
  raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
260
273
  end
261
274
 
262
275
  self.class.new(
263
276
  api: @api,
264
- element: attrs.first,
265
- element_renamed: attrs[1][:as],
277
+ element:,
278
+ element_renamed: as,
266
279
  parent: self,
267
- optional: optional,
280
+ optional:,
268
281
  type: type || Array,
269
- &block
282
+ group: @group,
283
+ &
270
284
  )
271
285
  end
272
286
 
273
287
  # Returns a new parameter scope, not nested under any current-level param
274
288
  # but instead at the same level as the current scope.
275
- # @param options [Hash] options to control how this new scope behaves
276
- # @option options :dependent_on [Symbol] if given, specifies that this
277
- # scope should only validate if this parameter from the above scope is
278
- # present
289
+ # @param dependent_on [Symbol] if given, specifies that this scope should
290
+ # only validate if this parameter from the above scope is present
279
291
  # @yield parameter scope
280
- def new_lateral_scope(options, &block)
292
+ def new_lateral_scope(dependent_on:, &)
281
293
  self.class.new(
282
294
  api: @api,
283
- element: nil,
284
295
  parent: self,
285
- options: @optional,
296
+ optional: @optional,
286
297
  type: type == Array ? Array : Hash,
287
- dependent_on: options[:dependent_on],
288
- &block
298
+ dependent_on:,
299
+ &
289
300
  )
290
301
  end
291
302
 
292
- # Returns a new parameter scope, subordinate to the current one and nested
293
- # under the parameter corresponding to `attrs.first`.
294
- # @param attrs [Array] the attributes passed to the `requires` or
295
- # `optional` invocation that opened this scope.
303
+ # Returns a new parameter scope, subordinate to the current one, sharing
304
+ # the given group options with all parameters defined within.
305
+ # @param group [Hash] common options to merge into each parameter in the scope
296
306
  # @yield parameter scope
297
- def new_group_scope(attrs, &block)
298
- self.class.new(
299
- api: @api,
300
- parent: self,
301
- group: attrs.first,
302
- &block
303
- )
307
+ def new_group_scope(group, &)
308
+ self.class.new(api: @api, parent: self, group:, &)
304
309
  end
305
310
 
306
- # Pushes declared params to parent or settings
311
+ # Pushes declared params to parent or settings, then clears @declared_params.
312
+ # Clearing here (rather than in initialize) keeps the lifecycle ownership in
313
+ # one place: this method both consumes and invalidates the ivar so that
314
+ # push_declared_params cannot be called on the frozen scope later.
307
315
  def configure_declared_params
308
316
  push_renamed_param(full_path, @element_renamed) if @element_renamed
317
+ return @parent.push_declared_params [{ @element => @declared_params }] if nested?
309
318
 
310
- if nested?
311
- @parent.push_declared_params [element => @declared_params]
312
- else
313
- @api.namespace_stackable(:declared_params, @declared_params)
314
- end
315
-
316
- # params were stored in settings, it can be cleaned from the params scope
319
+ @api.inheritable_setting.namespace_stackable[:declared_params] = @declared_params
320
+ ensure
317
321
  @declared_params = nil
318
322
  end
319
323
 
320
- def validates(attrs, validations)
321
- doc = AttributesDoc.new @api, self
322
- doc.extract_details validations
323
-
324
- coerce_type = infer_coercion(validations)
325
-
326
- doc.type = coerce_type
327
-
328
- default = validations[:default]
329
-
330
- if (values_hash = validations[:values]).is_a? Hash
331
- values = values_hash[:value]
332
- # NB: excepts is deprecated
333
- excepts = values_hash[:except]
334
- else
335
- values = validations[:values]
336
- end
337
-
338
- doc.values = values
339
-
340
- except_values = options_key?(:except_values, :value, validations) ? validations[:except_values][:value] : validations[:except_values]
341
-
342
- # NB. values and excepts should be nil, Proc, Array, or Range.
343
- # Specifically, values should NOT be a Hash
344
-
345
- # use values or excepts to guess coerce type when stated type is Array
346
- coerce_type = guess_coerce_type(coerce_type, values, except_values, excepts)
347
-
348
- # default value should be present in values array, if both exist and are not procs
349
- check_incompatible_option_values(default, values, except_values, excepts)
350
-
351
- # type should be compatible with values array, if both exist
352
- validate_value_coercion(coerce_type, values, except_values, excepts)
353
-
354
- doc.document attrs
324
+ def find_nearest_array_ancestor
325
+ scope = @parent
326
+ scope = scope.parent while scope && scope.type != Array
327
+ scope
328
+ end
355
329
 
356
- opts = derive_validator_options(validations)
330
+ def validates(attrs, validations)
331
+ process_oneof!(validations) if validations.key?(:oneof)
332
+ spec = ValidationsSpec.from(validations)
357
333
 
358
- # Validate for presence before any other validators
359
- validates_presence(validations, attrs, doc, opts)
334
+ document_params(attrs, spec)
360
335
 
361
- # Before we run the rest of the validators, let's handle
362
- # whatever coercion so that we are working with correctly
363
- # type casted values
364
- coerce_type validations, attrs, doc, opts
336
+ # Presence runs first — `required` is forwarded to every subsequent
337
+ # validator (some short-circuit on it).
338
+ validate_presence(spec, attrs)
365
339
 
366
- validations.each do |type, options|
367
- # Don't try to look up validators for documentation params that don't have one.
368
- next if RESERVED_DOCUMENTATION_KEYWORDS.include?(type)
340
+ # Coerce runs second — later validators see the typed value.
341
+ validate_coerce(spec, attrs)
369
342
 
370
- validate(type, options, attrs, doc, opts)
343
+ spec.validator_entries.each do |type, options|
344
+ validate(type, options, attrs, spec.required?, spec.shared_opts)
371
345
  end
372
346
  end
373
347
 
374
- # Validate and comprehend the +:type+, +:types+, and +:coerce_with+
375
- # options that have been supplied to the parameter declaration.
376
- # The +:type+ and +:types+ options will be removed from the
377
- # validations list, replaced appropriately with +:coerce+ and
378
- # +:coerce_with+ options that will later be passed to
379
- # {Validators::CoerceValidator}. The type that is returned may be
380
- # used for documentation and further validation of parameter
381
- # options.
382
- #
383
- # @param validations [Hash] list of validations supplied to the
384
- # parameter declaration
385
- # @return [class-like] type to which the parameter will be coerced
386
- # @raise [ArgumentError] if the given type options are invalid
387
- def infer_coercion(validations)
388
- raise ArgumentError, ':type may not be supplied with :types' if validations.key?(:type) && validations.key?(:types)
389
-
390
- validations[:coerce] = (options_key?(:type, :value, validations) ? validations[:type][:value] : validations[:type]) if validations.key?(:type)
391
- validations[:coerce_message] = (options_key?(:type, :message, validations) ? validations[:type][:message] : nil) if validations.key?(:type)
392
- validations[:coerce] = (options_key?(:types, :value, validations) ? validations[:types][:value] : validations[:types]) if validations.key?(:types)
393
- validations[:coerce_message] = (options_key?(:types, :message, validations) ? validations[:types][:message] : nil) if validations.key?(:types)
394
-
395
- validations.delete(:types) if validations.key?(:types)
396
-
397
- coerce_type = validations[:coerce]
398
-
399
- # Special case - when the argument is a single type that is a
400
- # variant-type collection.
401
- if Types.multiple?(coerce_type) && validations.key?(:type)
402
- validations[:coerce] = Types::VariantCollectionCoercer.new(
403
- coerce_type,
404
- validations.delete(:coerce_with)
405
- )
406
- end
407
- validations.delete(:type)
408
-
409
- coerce_type
410
- end
411
-
412
- # Enforce correct usage of :coerce_with parameter.
413
- # We do not allow coercion without a type, nor with
414
- # +JSON+ as a type since this defines its own coercion
415
- # method.
416
- def check_coerce_with(validations)
417
- return unless validations.key?(:coerce_with)
418
- # type must be supplied for coerce_with..
419
- raise ArgumentError, 'must supply type for coerce_with' unless validations.key?(:coerce)
420
-
421
- # but not special JSON types, which
422
- # already imply coercion method
423
- return if [JSON, Array[JSON]].exclude? validations[:coerce]
348
+ # Enforce correct usage of :coerce_with on a CoerceOptions.
349
+ # We do not allow coercion without a type, nor with +JSON+ as a type
350
+ # since that defines its own coercion method.
351
+ def check_coerce_with(coerce_options)
352
+ return unless coerce_options.coerce_method
353
+ raise ArgumentError, 'must supply type for coerce_with' unless coerce_options.type
354
+ return unless SPECIAL_JSON.include?(coerce_options.type)
424
355
 
425
356
  raise ArgumentError, 'coerce_with disallowed for type: JSON'
426
357
  end
427
358
 
428
- # Add type coercion validation to this scope,
429
- # if any has been specified.
430
- # This validation has special handling since it is
431
- # composited from more than one +requires+/+optional+
432
- # parameter, and needs to be run before most other
433
- # validations.
434
- def coerce_type(validations, attrs, doc, opts)
435
- check_coerce_with(validations)
359
+ def validate_presence(spec, attrs)
360
+ return unless spec.required?
436
361
 
437
- return unless validations.key?(:coerce)
438
-
439
- coerce_options = {
440
- type: validations[:coerce],
441
- method: validations[:coerce_with],
442
- message: validations[:coerce_message]
443
- }
444
- validate('coerce', coerce_options, attrs, doc, opts)
445
- validations.delete(:coerce_with)
446
- validations.delete(:coerce)
447
- validations.delete(:coerce_message)
362
+ validate('presence', spec.presence_options, attrs, true, spec.shared_opts)
448
363
  end
449
364
 
450
- def guess_coerce_type(coerce_type, *values_list)
451
- return coerce_type unless coerce_type == Array
365
+ def validate_coerce(spec, attrs)
366
+ coerce_options = spec.coerce_options
367
+ check_coerce_with(coerce_options)
368
+ # Falsy check is intentional: when a remountable API is first evaluated
369
+ # on its base instance (no configuration supplied yet),
370
+ # configuration[:some_type] evaluates to nil. Skipping instantiation
371
+ # here is correct — the real mounted instance will replay this step
372
+ # with the actual type value.
373
+ return unless coerce_options.type
452
374
 
453
- values_list.each do |values|
454
- next if !values || values.is_a?(Proc)
455
- return values.first.class if values.is_a?(Range) || !values.empty?
456
- end
457
- coerce_type
375
+ validate('coerce', coerce_options, attrs, spec.required?, spec.shared_opts)
458
376
  end
459
377
 
460
- def check_incompatible_option_values(default, values, except_values, excepts)
461
- return unless default && !default.is_a?(Proc)
378
+ # Translate a `oneof: [proc, proc, ...]` declaration into a list of
379
+ # captured validator arrays — one array per variant. Each variant's
380
+ # block is evaluated in its own +ParamsScope+ backed by an
381
+ # {OneofCollector} so the full params DSL is available inside variants
382
+ # and the resulting validators are kept out of the real API's
383
+ # registration list.
384
+ def process_oneof!(validations)
385
+ raise ArgumentError, 'oneof: requires type: Hash' unless validations[:type] == Hash
462
386
 
463
- raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) if values && !values.is_a?(Proc) && !Array(default).all? { |def_val| values.include?(def_val) }
387
+ variants = validations[:oneof]
388
+ raise ArgumentError, 'oneof: must be a non-empty Array of blocks' unless variants.is_a?(Array) && variants.any?
389
+ raise ArgumentError, 'oneof: each variant must be a Proc' unless variants.all?(Proc)
464
390
 
465
- if except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }
466
- raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values) \
467
-
468
- end
469
-
470
- return unless excepts && !excepts.is_a?(Proc)
471
- raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, excepts) \
472
- unless Array(default).none? { |def_val| excepts.include?(def_val) }
473
- end
474
-
475
- def validate(type, options, attrs, doc, opts)
476
- validator_options = {
477
- attributes: attrs,
478
- options: options,
479
- required: doc.required,
480
- params_scope: self,
481
- opts: opts,
482
- validator_class: Validations.require_validator(type)
483
- }
484
- @api.namespace_stackable(:validations, validator_options)
485
- end
486
-
487
- def validate_value_coercion(coerce_type, *values_list)
488
- return unless coerce_type
489
-
490
- coerce_type = coerce_type.first if coerce_type.is_a?(Array)
491
- values_list.each do |values|
492
- next if !values || values.is_a?(Proc)
493
-
494
- value_types = values.is_a?(Range) ? [values.begin, values.end].compact : values
495
- value_types = value_types.map { |type| Grape::API::Boolean.build(type) } if coerce_type == Grape::API::Boolean
496
- raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values) unless value_types.all?(coerce_type)
497
- end
498
- end
499
-
500
- def extract_message_option(attrs)
501
- return nil unless attrs.is_a?(Array)
502
-
503
- opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
504
- opts.key?(:message) && !opts[:message].nil? ? opts.delete(:message) : nil
391
+ validations[:oneof] = variants.map { |block| OneofCollector.collect(block) }
505
392
  end
506
393
 
507
- def options_key?(type, key, validations)
508
- validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
394
+ def validate(type, options, attrs, required, opts)
395
+ validator_class = Validations.require_validator(type)
396
+ validator_instance = validator_class.new(
397
+ attrs,
398
+ options,
399
+ required,
400
+ self,
401
+ opts
402
+ )
403
+ @api.inheritable_setting.namespace_stackable[:validations] = validator_instance
509
404
  end
510
405
 
511
406
  def all_element_blank?(scoped_params)
512
407
  scoped_params.respond_to?(:all?) && scoped_params.all?(&:blank?)
513
408
  end
514
-
515
- # Validators don't have access to each other and they don't need, however,
516
- # some validators might influence others, so their options should be shared
517
- def derive_validator_options(validations)
518
- allow_blank = validations[:allow_blank]
519
-
520
- {
521
- allow_blank: allow_blank.is_a?(Hash) ? allow_blank[:value] : allow_blank,
522
- fail_fast: validations.delete(:fail_fast) || false
523
- }
524
- end
525
-
526
- def validates_presence(validations, attrs, doc, opts)
527
- return unless validations.key?(:presence) && validations[:presence]
528
-
529
- validate(:presence, validations.delete(:presence), attrs, doc, opts)
530
- validations.delete(:message) if validations.key?(:message)
531
- end
532
409
  end
533
410
  end
534
411
  end