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
@@ -3,51 +3,33 @@
3
3
  module Grape
4
4
  module DSL
5
5
  module Middleware
6
- extend ActiveSupport::Concern
7
-
8
- include Grape::DSL::Configuration
9
-
10
- module ClassMethods
11
- # Apply a custom middleware to the API. Applies
12
- # to the current namespace and any children, but
13
- # not parents.
14
- #
15
- # @param middleware_class [Class] The class of the middleware you'd like
16
- # to inject.
17
- def use(middleware_class, *args, &block)
18
- arr = [:use, middleware_class, *args]
19
- arr << block if block
20
-
21
- namespace_stackable(:middleware, arr)
22
- end
23
-
24
- def insert(*args, &block)
25
- arr = [:insert, *args]
26
- arr << block if block
27
-
28
- namespace_stackable(:middleware, arr)
29
- end
30
-
31
- def insert_before(*args, &block)
32
- arr = [:insert_before, *args]
33
- arr << block if block
34
-
35
- namespace_stackable(:middleware, arr)
36
- end
6
+ # Apply a custom middleware to the API. Applies
7
+ # to the current namespace and any children, but
8
+ # not parents.
9
+ #
10
+ # @param middleware_class [Class] The class of the middleware you'd like
11
+ # to inject.
12
+ def use(middleware_class, *args, &block)
13
+ arr = [:use, middleware_class, *args]
14
+ arr << block if block
15
+
16
+ inheritable_setting.add_middleware(arr)
17
+ end
37
18
 
38
- def insert_after(*args, &block)
39
- arr = [:insert_after, *args]
19
+ %i[insert insert_before insert_after].each do |method_name|
20
+ define_method method_name do |*args, &block|
21
+ arr = [method_name, *args]
40
22
  arr << block if block
41
23
 
42
- namespace_stackable(:middleware, arr)
24
+ inheritable_setting.add_middleware(arr)
43
25
  end
26
+ end
44
27
 
45
- # Retrieve an array of the middleware classes
46
- # and arguments that are currently applied to the
47
- # application.
48
- def middleware
49
- namespace_stackable(:middleware) || []
50
- end
28
+ # Retrieve an array of the middleware classes
29
+ # and arguments that are currently applied to the
30
+ # application.
31
+ def middleware
32
+ inheritable_setting.middleware
51
33
  end
52
34
  end
53
35
  end
@@ -6,8 +6,6 @@ module Grape
6
6
  # and describe the parameters accepted by an endpoint, or all endpoints
7
7
  # within a namespace.
8
8
  module Parameters
9
- extend ActiveSupport::Concern
10
-
11
9
  # Set the module used to build the request.params.
12
10
  #
13
11
  # @param build_with the ParamBuilder module to use when building request.params
@@ -23,15 +21,15 @@ module Grape
23
21
  # class API < Grape::API
24
22
  # desc "Get collection"
25
23
  # params do
26
- # build_with Grape::Extensions::Hashie::Mash::ParamBuilder
24
+ # build_with :hashie_mash
27
25
  # requires :user_id, type: Integer
28
26
  # end
29
27
  # get do
30
28
  # params['user_id']
31
29
  # end
32
30
  # end
33
- def build_with(build_with = nil)
34
- @api.namespace_inheritable(:build_params_with, build_with)
31
+ def build_with(build_with)
32
+ @api.inheritable_setting.build_params_with = build_with
35
33
  end
36
34
 
37
35
  # Include reusable params rules among current.
@@ -55,9 +53,10 @@ module Grape
55
53
  # Collection.page(params[:page]).per(params[:per_page])
56
54
  # end
57
55
  # end
58
- def use(*names)
59
- named_params = @api.namespace_stackable_with_hash(:named_params) || {}
60
- options = names.extract_options!
56
+ def use(*names, **options)
57
+ return redispatch_legacy_options(:use, names, options) if legacy_options?(names)
58
+
59
+ named_params = @api.inheritable_setting.named_params || {}
61
60
  names.each do |name|
62
61
  params_block = named_params.fetch(name) do
63
62
  raise "Params :#{name} not found!"
@@ -77,8 +76,8 @@ module Grape
77
76
  #
78
77
  # @param attrs list of parameters names, or, if :using is
79
78
  # passed as an option, which keys to include (:all or :none) from
80
- # the :using hash. The last key can be a hash, which specifies
81
- # options for the parameters
79
+ # the :using hash. Passing the options as a trailing positional Hash
80
+ # is deprecated; pass them as keyword arguments instead.
82
81
  # @option attrs :type [Class] the type to coerce this parameter to before
83
82
  # passing it to the endpoint. See {Grape::Validations::Types} for a list of
84
83
  # types that are supported automatically. Custom classes may be used
@@ -125,76 +124,34 @@ module Grape
125
124
  # requires :name, type: String
126
125
  # end
127
126
  # end
128
- def requires(*attrs, &block)
129
- orig_attrs = attrs.clone
127
+ def requires(*attrs, using: nil, except: nil, as: nil, **opts, &)
128
+ return redispatch_legacy_options(:requires, attrs, { using:, except:, as: }.compact.merge(opts), &) if legacy_options?(attrs)
130
129
 
131
- opts = attrs.extract_options!.clone
132
- opts[:presence] = { value: true, message: opts[:message] }
133
- opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
134
-
135
- if opts[:using]
136
- require_required_and_optional_fields(attrs.first, opts)
137
- else
138
- validate_attributes(attrs, opts, &block)
139
- block ? new_scope(orig_attrs, &block) : push_declared_params(attrs, **opts.slice(:as))
140
- end
130
+ declare(attrs, opts, required: true, using:, except:, as:, &)
141
131
  end
142
132
 
143
133
  # Allow, but don't require, one or more parameters for the current
144
134
  # endpoint.
145
135
  # @param (see #requires)
146
136
  # @option (see #requires)
147
- def optional(*attrs, &block)
148
- orig_attrs = attrs.clone
149
-
150
- opts = attrs.extract_options!.clone
151
- type = opts[:type]
152
- opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
137
+ def optional(*attrs, using: nil, except: nil, as: nil, **opts, &)
138
+ return redispatch_legacy_options(:optional, attrs, { using:, except:, as: }.compact.merge(opts), &) if legacy_options?(attrs)
153
139
 
154
- # check type for optional parameter group
155
- if attrs && block
156
- raise Grape::Exceptions::MissingGroupType if type.nil?
157
- raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
158
- end
159
-
160
- if opts[:using]
161
- require_optional_fields(attrs.first, opts)
162
- else
163
- validate_attributes(attrs, opts, &block)
164
-
165
- block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
166
- end
140
+ declare(attrs, opts, required: false, using:, except:, as:, &)
167
141
  end
168
142
 
169
143
  # Define common settings for one or more parameters
170
144
  # @param (see #requires)
171
145
  # @option (see #requires)
172
- def with(*attrs, &block)
173
- new_group_scope(attrs.clone, &block)
174
- end
175
-
176
- # Disallow the given parameters to be present in the same request.
177
- # @param attrs [*Symbol] parameters to validate
178
- def mutually_exclusive(*attrs)
179
- validates(attrs, mutual_exclusion: { value: true, message: extract_message_option(attrs) })
146
+ def with(**opts, &)
147
+ new_group_attrs = @group&.deep_merge(opts) || opts
148
+ new_group_scope(new_group_attrs, &)
180
149
  end
181
150
 
182
- # Require exactly one of the given parameters to be present.
183
- # @param (see #mutually_exclusive)
184
- def exactly_one_of(*attrs)
185
- validates(attrs, exactly_one_of: { value: true, message: extract_message_option(attrs) })
186
- end
187
-
188
- # Require at least one of the given parameters to be present.
189
- # @param (see #mutually_exclusive)
190
- def at_least_one_of(*attrs)
191
- validates(attrs, at_least_one_of: { value: true, message: extract_message_option(attrs) })
192
- end
193
-
194
- # Require that either all given params are present, or none are.
195
- # @param (see #mutually_exclusive)
196
- def all_or_none_of(*attrs)
197
- validates(attrs, all_or_none_of: { value: true, message: extract_message_option(attrs) })
151
+ %i[mutually_exclusive exactly_one_of at_least_one_of all_or_none_of].each do |validator|
152
+ define_method validator do |*attrs, message: nil|
153
+ validates(attrs, { validator => { value: true, message: } })
154
+ end
198
155
  end
199
156
 
200
157
  # Define a block of validations which should be applied if and only if
@@ -204,62 +161,99 @@ module Grape
204
161
  # @raise Grape::Exceptions::UnknownParameter if `attr` has not been
205
162
  # defined in this scope yet
206
163
  # @yield a parameter definition DSL
207
- def given(*attrs, &block)
164
+ def given(*attrs, &)
208
165
  attrs.each do |attr|
209
166
  proxy_attr = first_hash_key_or_param(attr)
210
167
  raise Grape::Exceptions::UnknownParameter.new(proxy_attr) unless declared_param?(proxy_attr)
211
168
  end
212
- new_lateral_scope(dependent_on: attrs, &block)
169
+ new_lateral_scope(dependent_on: attrs, &)
213
170
  end
214
171
 
215
172
  # Test for whether a certain parameter has been defined in this params
216
173
  # block yet.
217
174
  # @return [Boolean] whether the parameter has been defined
218
175
  def declared_param?(param)
219
- if lateral?
220
- # Elements of @declared_params of lateral scope are pushed in @parent. So check them in @parent.
221
- @parent.declared_param?(param)
222
- else
223
- # @declared_params also includes hashes of options and such, but those
224
- # won't be flattened out.
225
- @declared_params.flatten.any? do |declared_param_attr|
226
- first_hash_key_or_param(declared_param_attr.key) == param
227
- end
176
+ # Elements of @declared_params of lateral scope are pushed in @parent. So check them in @parent.
177
+ return @parent.declared_param?(param) if lateral?
178
+
179
+ # @declared_params also includes hashes of options and such, but those
180
+ # won't be flattened out.
181
+ @declared_params.flatten.any? do |declared_param_attr|
182
+ first_hash_key_or_param(declared_param_attr.key) == param
228
183
  end
229
184
  end
230
185
 
231
186
  alias group requires
232
187
 
233
- class EmptyOptionalValue; end
234
-
235
- def map_params(params, element, is_array = false)
236
- if params.is_a?(Array)
237
- params.map do |el|
238
- map_params(el, element, true)
239
- end
240
- elsif params.is_a?(Hash)
241
- params[element] || (@optional && is_array ? EmptyOptionalValue : {})
242
- elsif params == EmptyOptionalValue
243
- EmptyOptionalValue
244
- else
245
- {}
246
- end
247
- end
188
+ EmptyOptionalValue = Object.new.freeze
248
189
 
249
190
  # @param params [Hash] initial hash of parameters
250
191
  # @return hash of parameters relevant for the current scope
251
192
  # @api private
252
193
  def params(params)
253
- params = @parent.params(params) if instance_variable_defined?(:@parent) && @parent
254
- params = map_params(params, @element) if instance_variable_defined?(:@element) && @element
255
- params
194
+ scoped = @parent ? (@parent.qualifying_params.presence || @parent.params(params)) : params
195
+ @element ? map_params(scoped, @element) : scoped
256
196
  end
257
197
 
258
198
  private
259
199
 
200
+ # @deprecated A trailing positional options Hash is deprecated; pass keyword
201
+ # arguments instead. Before Ruby 3 keyword separation this Hash was pulled
202
+ # off the argument list by `extract_options!`; now it lands in the splat and
203
+ # would silently be treated as a parameter name.
204
+ # +requires+ and +optional+ differ only in whether what they declare is
205
+ # required, so the declaration itself lives here.
206
+ #
207
+ # The two +using:+ helpers stay separate. They are not one computation
208
+ # with a flag flipped: +requires+ treats +except+ as "these go in the
209
+ # other bucket", while +optional+ has no other bucket — +:all+ ignores
210
+ # +except+ and +:none+ uses it to drop fields entirely.
211
+ def declare(attrs, opts, required:, using:, except:, as:, &block)
212
+ merged_opts = @group&.deep_merge(opts) || opts
213
+ declared_as = as || merged_opts[:as]
214
+
215
+ if using
216
+ return require_required_and_optional_fields(attrs.first, using:, except:) if required
217
+
218
+ return require_optional_fields(attrs.first, using:, except:)
219
+ end
220
+
221
+ validates(attrs, merged_opts, required:)
222
+ return push_declared_params(attrs, as: declared_as) unless block
223
+
224
+ new_scope(attrs.first, type: merged_opts[:type], as: declared_as, optional: !required, &block)
225
+ end
226
+
227
+ def legacy_options?(args)
228
+ args.size > 1 && args.last.is_a?(Hash)
229
+ end
230
+
231
+ # Re-invokes +method_name+ with the trailing Hash splatted as keyword
232
+ # arguments, so Ruby routes its keys to the same place they would have
233
+ # reached had the caller omitted the braces.
234
+ def redispatch_legacy_options(method_name, args, opts, &)
235
+ Grape.deprecator.warn("Passing a positional options Hash to `#{method_name}` is deprecated. Pass keyword arguments instead.")
236
+ __send__(method_name, *args[0..-2], **args.last.merge(opts), &)
237
+ end
238
+
260
239
  def first_hash_key_or_param(parameter)
261
240
  parameter.is_a?(Hash) ? parameter.keys.first : parameter
262
241
  end
242
+
243
+ def map_params(params, element, is_array: false)
244
+ case params
245
+ when Array
246
+ params.map do |el|
247
+ map_params(el, element, is_array: true)
248
+ end
249
+ when Hash
250
+ params[element] || (@optional && is_array ? EmptyOptionalValue : {})
251
+ when EmptyOptionalValue
252
+ EmptyOptionalValue
253
+ else
254
+ {}
255
+ end
256
+ end
263
257
  end
264
258
  end
265
259
  end
@@ -3,171 +3,167 @@
3
3
  module Grape
4
4
  module DSL
5
5
  module RequestResponse
6
- extend ActiveSupport::Concern
6
+ # Specify the default format for the API's serializers.
7
+ # May be `:json` or `:txt` (default).
8
+ def default_format(new_format = nil)
9
+ return inheritable_setting.default_format if new_format.nil?
7
10
 
8
- include Grape::DSL::Configuration
11
+ inheritable_setting.default_format = new_format.to_sym
12
+ end
9
13
 
10
- module ClassMethods
11
- # Specify the default format for the API's serializers.
12
- # May be `:json` or `:txt` (default).
13
- def default_format(new_format = nil)
14
- namespace_inheritable(:default_format, new_format.nil? ? nil : new_format.to_sym)
15
- end
14
+ # Specify the format for the API's serializers.
15
+ # May be `:json`, `:xml`, `:txt`, etc.
16
+ def format(new_format = nil)
17
+ return inheritable_setting.format if new_format.nil?
16
18
 
17
- # Specify the format for the API's serializers.
18
- # May be `:json`, `:xml`, `:txt`, etc.
19
- def format(new_format = nil)
20
- if new_format
21
- namespace_inheritable(:format, new_format.to_sym)
22
- # define the default error formatters
23
- namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(new_format, **{}))
24
- # define a single mime type
25
- mime_type = content_types[new_format.to_sym]
26
- raise Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type
27
-
28
- namespace_stackable(:content_types, new_format.to_sym => mime_type)
29
- else
30
- namespace_inheritable(:format)
31
- end
32
- end
19
+ symbolic_new_format = new_format.to_sym
20
+ inheritable_setting.format = symbolic_new_format
21
+ inheritable_setting.default_error_formatter = Grape::ErrorFormatter.formatter_for(symbolic_new_format)
33
22
 
34
- # Specify a custom formatter for a content-type.
35
- def formatter(content_type, new_formatter)
36
- namespace_stackable(:formatters, content_type.to_sym => new_formatter)
37
- end
23
+ content_type = content_types[symbolic_new_format]
24
+ raise Grape::Exceptions::MissingMimeType.new(new_format) unless content_type
38
25
 
39
- # Specify a custom parser for a content-type.
40
- def parser(content_type, new_parser)
41
- namespace_stackable(:parsers, content_type.to_sym => new_parser)
42
- end
26
+ inheritable_setting.add_content_type(symbolic_new_format, content_type)
27
+ end
43
28
 
44
- # Specify a default error formatter.
45
- def default_error_formatter(new_formatter_name = nil)
46
- if new_formatter_name
47
- new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name, **{})
48
- namespace_inheritable(:default_error_formatter, new_formatter)
49
- else
50
- namespace_inheritable(:default_error_formatter)
51
- end
52
- end
29
+ # Specify a custom formatter for a content-type.
30
+ def formatter(content_type, new_formatter)
31
+ inheritable_setting.add_formatter(content_type.to_sym, new_formatter)
32
+ end
53
33
 
54
- def error_formatter(format, options)
55
- formatter = if options.is_a?(Hash) && options.key?(:with)
56
- options[:with]
57
- else
58
- options
59
- end
34
+ # Specify a custom parser for a content-type.
35
+ def parser(content_type, new_parser)
36
+ inheritable_setting.add_parser(content_type.to_sym, new_parser)
37
+ end
60
38
 
61
- namespace_stackable(:error_formatters, format.to_sym => formatter)
62
- end
39
+ # Specify a default error formatter, by the name it is registered under.
40
+ # A name nothing is registered for used to be stored as
41
+ # +ErrorFormatter::Txt+ — the fallback the lookup applied — so a typo
42
+ # read back as a working setting and silently rendered errors as text.
43
+ def default_error_formatter(new_formatter_name = nil)
44
+ return inheritable_setting.default_error_formatter if new_formatter_name.nil?
63
45
 
64
- # Specify additional content-types, e.g.:
65
- # content_type :xls, 'application/vnd.ms-excel'
66
- def content_type(key, val)
67
- namespace_stackable(:content_types, key.to_sym => val)
68
- end
46
+ formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name)
47
+ raise Grape::Exceptions::UnknownErrorFormatter.new(new_formatter_name) if formatter.nil?
69
48
 
70
- # All available content types.
71
- def content_types
72
- c_types = namespace_stackable_with_hash(:content_types)
73
- Grape::ContentTypes.content_types_for c_types
74
- end
49
+ inheritable_setting.default_error_formatter = formatter
50
+ end
75
51
 
76
- # Specify the default status code for errors.
77
- def default_error_status(new_status = nil)
78
- namespace_inheritable(:default_error_status, new_status)
79
- end
52
+ # Specify a custom error formatter for a format, passed positionally or
53
+ # as +with:+. A nil formatter used to be registered as-is, which is the
54
+ # one thing the registration cannot mean: the format then resolves as if
55
+ # the call had never been made. Reject it here, where the mistake is.
56
+ def error_formatter(format, options = nil, with: nil)
57
+ formatter = with || options
58
+ raise ArgumentError, "error_formatter `#{format.inspect}` requires a formatter, given positionally or as `with:`" if formatter.nil?
80
59
 
81
- # Allows you to rescue certain exceptions that occur to return
82
- # a grape error rather than raising all the way to the
83
- # server level.
84
- #
85
- # @example Rescue from custom exceptions
86
- # class ExampleAPI < Grape::API
87
- # class CustomError < StandardError; end
88
- #
89
- # rescue_from CustomError
90
- # end
91
- #
92
- # @overload rescue_from(*exception_classes, **options)
93
- # @param [Array] exception_classes A list of classes that you want to rescue, or
94
- # the symbol :all to rescue from all exceptions.
95
- # @param [Block] block Execution block to handle the given exception.
96
- # @param [Hash] options Options for the rescue usage.
97
- # @option options [Boolean] :backtrace Include a backtrace in the rescue response.
98
- # @option options [Boolean] :rescue_subclasses Also rescue subclasses of exception classes
99
- # @param [Proc] handler Execution proc to handle the given exception as an
100
- # alternative to passing a block.
101
- def rescue_from(*args, &block)
102
- if args.last.is_a?(Proc)
103
- handler = args.pop
104
- elsif block
105
- handler = block
106
- end
107
-
108
- options = args.extract_options!
109
- raise ArgumentError, 'both :with option and block cannot be passed' if block && options.key?(:with)
110
-
111
- handler ||= extract_with(options)
112
-
113
- if args.include?(:all)
114
- namespace_inheritable(:rescue_all, true)
115
- namespace_inheritable(:all_rescue_handler, handler)
116
- elsif args.include?(:grape_exceptions)
117
- namespace_inheritable(:rescue_all, true)
118
- namespace_inheritable(:rescue_grape_exceptions, true)
119
- namespace_inheritable(:grape_exceptions_rescue_handler, handler)
120
- else
121
- handler_type =
122
- case options[:rescue_subclasses]
123
- when nil, true
124
- :rescue_handlers
125
- else
126
- :base_only_rescue_handlers
127
- end
128
-
129
- namespace_reverse_stackable(handler_type, args.to_h { |arg| [arg, handler] })
130
- end
131
-
132
- namespace_stackable(:rescue_options, options)
133
- end
60
+ inheritable_setting.add_error_formatter(format.to_sym, formatter)
61
+ end
62
+
63
+ # Specify additional content-types, e.g.:
64
+ # content_type :xls, 'application/vnd.ms-excel'
65
+ def content_type(key, val)
66
+ inheritable_setting.add_content_type(key.to_sym, val)
67
+ end
68
+
69
+ # All available content types.
70
+ def content_types
71
+ Grape::ContentTypes.content_types_for(inheritable_setting.content_types)
72
+ end
73
+
74
+ # Specify the default status code for errors.
75
+ def default_error_status(new_status = nil)
76
+ return inheritable_setting.default_error_status if new_status.nil?
134
77
 
135
- # Allows you to specify a default representation entity for a
136
- # class. This allows you to map your models to their respective
137
- # entities once and then simply call `present` with the model.
138
- #
139
- # @example
140
- # class ExampleAPI < Grape::API
141
- # represent User, with: Entity::User
142
- #
143
- # get '/me' do
144
- # present current_user # with: Entity::User is assumed
145
- # end
146
- # end
147
- #
148
- # Note that Grape will automatically go up the class ancestry to
149
- # try to find a representing entity, so if you, for example, define
150
- # an entity to represent `Object` then all presented objects will
151
- # bubble up and utilize the entity provided on that `represent` call.
152
- #
153
- # @param model_class [Class] The model class that will be represented.
154
- # @option options [Class] :with The entity class that will represent the model.
155
- def represent(model_class, options)
156
- raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with].is_a?(Class)
157
-
158
- namespace_stackable(:representations, model_class => options[:with])
78
+ inheritable_setting.default_error_status = new_status
79
+ end
80
+
81
+ # Allows you to rescue certain exceptions that occur to return
82
+ # a grape error rather than raising all the way to the
83
+ # server level.
84
+ #
85
+ # @example Rescue from custom exceptions
86
+ # class ExampleAPI < Grape::API
87
+ # class CustomError < StandardError; end
88
+ #
89
+ # rescue_from CustomError
90
+ # end
91
+ #
92
+ META_RESCUE_SELECTORS = %i[all grape_exceptions internal_grape_exceptions].freeze
93
+ private_constant :META_RESCUE_SELECTORS
94
+
95
+ # @overload rescue_from(*exception_classes, **options)
96
+ # @param [Array] exception_classes A list of classes that you want to rescue, or
97
+ # one of the meta selectors +:all+, +:grape_exceptions+,
98
+ # +:internal_grape_exceptions+. Meta selectors must be used alone;
99
+ # mixing with exception classes raises +ArgumentError+.
100
+ # @param [Block] block Execution block to handle the given exception.
101
+ # @param [Proc] with Execution proc to handle the given exception as an alternative
102
+ # to passing a block.
103
+ # @param [Boolean] rescue_subclasses Also rescue subclasses of exception classes;
104
+ # defaults to +true+.
105
+ # @param [Boolean] backtrace Include the rescued exception's backtrace in the
106
+ # rescue response body.
107
+ # @param [Boolean] original_exception Include +inspect+ of the rescued exception
108
+ # in the rescue response body.
109
+ def rescue_from(*args, with: nil, rescue_subclasses: true, backtrace: false, original_exception: false, &block)
110
+ handler = extract_handler(args, with:, block:)
111
+ meta_selector = (args & META_RESCUE_SELECTORS).first
112
+ raise ArgumentError, "rescue_from `#{meta_selector.inspect}` does not accept additional arguments" if meta_selector && args.size > 1
113
+
114
+ case meta_selector
115
+ when :all
116
+ inheritable_setting.add_all_rescue_handler(handler)
117
+ when :grape_exceptions
118
+ inheritable_setting.add_grape_exceptions_rescue_handler(handler)
119
+ when :internal_grape_exceptions
120
+ inheritable_setting.add_internal_grape_exceptions_rescue_handler(handler)
121
+ else
122
+ inheritable_setting.add_rescue_handlers(args.to_h { |klass| [klass, handler] }, subclasses: rescue_subclasses)
159
123
  end
160
124
 
161
- private
125
+ inheritable_setting.add_rescue_options(RescueOptions.new(backtrace:, original_exception:))
126
+ end
127
+
128
+ # Allows you to specify a default representation entity for a
129
+ # class. This allows you to map your models to their respective
130
+ # entities once and then simply call `present` with the model.
131
+ #
132
+ # @example
133
+ # class ExampleAPI < Grape::API
134
+ # represent User, with: Entity::User
135
+ #
136
+ # get '/me' do
137
+ # present current_user # with: Entity::User is assumed
138
+ # end
139
+ # end
140
+ #
141
+ # Note that Grape will automatically go up the class ancestry to
142
+ # try to find a representing entity, so if you, for example, define
143
+ # an entity to represent `Object` then all presented objects will
144
+ # bubble up and utilize the entity provided on that `represent` call.
145
+ #
146
+ # @param model_class [Class] The model class that will be represented.
147
+ # @option options [Class] :with The entity class that will represent the model.
148
+ def represent(model_class, with:)
149
+ raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless with.is_a?(Class)
150
+
151
+ inheritable_setting.add_representation(model_class, with)
152
+ end
153
+
154
+ private
162
155
 
163
- def extract_with(options)
164
- return unless options.key?(:with)
156
+ def extract_handler(args, with:, block:)
157
+ raise ArgumentError, 'both :with option and block cannot be passed' if block && with
165
158
 
166
- with_option = options.delete(:with)
167
- return with_option if with_option.instance_of?(Proc)
168
- return with_option.to_sym if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
159
+ return args.pop if args.last.is_a?(Proc)
160
+ return block if block
161
+ return unless with
169
162
 
170
- raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
163
+ case with
164
+ when Proc, Symbol then with
165
+ when String then with.to_sym
166
+ else raise ArgumentError, "with: `#{with.class}`, expected Symbol, String or Proc"
171
167
  end
172
168
  end
173
169
  end