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
@@ -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.namespace_stackable[: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.namespace_stackable[: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.namespace_stackable[: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.namespace_inheritable[:build_params_with] = build_with
35
33
  end
36
34
 
37
35
  # Include reusable params rules among current.
@@ -55,9 +53,8 @@ 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
+ named_params = @api.inheritable_setting.namespace_stackable_with_hash(:named_params) || {}
61
58
  names.each do |name|
62
59
  params_block = named_params.fetch(name) do
63
60
  raise "Params :#{name} not found!"
@@ -125,31 +122,23 @@ module Grape
125
122
  # requires :name, type: String
126
123
  # end
127
124
  # end
128
- def requires(*attrs, &block)
129
- orig_attrs = attrs.clone
130
-
131
- opts = attrs.extract_options!.clone
125
+ def requires(*attrs, using: nil, except: nil, **opts, &block)
132
126
  opts[:presence] = { value: true, message: opts[:message] }
133
- opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
127
+ opts = @group.deep_merge(opts) if @group
134
128
 
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
129
+ return require_required_and_optional_fields(attrs.first, using:, except:) if using
130
+
131
+ validate_attributes(attrs, **opts, &block)
132
+ block ? new_scope(attrs.first, type: opts[:type], as: opts[:as], &block) : push_declared_params(attrs, as: opts[:as])
141
133
  end
142
134
 
143
135
  # Allow, but don't require, one or more parameters for the current
144
136
  # endpoint.
145
137
  # @param (see #requires)
146
138
  # @option (see #requires)
147
- def optional(*attrs, &block)
148
- orig_attrs = attrs.clone
149
-
150
- opts = attrs.extract_options!.clone
139
+ def optional(*attrs, using: nil, except: nil, **opts, &block)
151
140
  type = opts[:type]
152
- opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
141
+ opts = @group.deep_merge(opts) if @group
153
142
 
154
143
  # check type for optional parameter group
155
144
  if attrs && block
@@ -157,44 +146,24 @@ module Grape
157
146
  raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
158
147
  end
159
148
 
160
- if opts[:using]
161
- require_optional_fields(attrs.first, opts)
162
- else
163
- validate_attributes(attrs, opts, &block)
149
+ return require_optional_fields(attrs.first, using:, except:) if using
164
150
 
165
- block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
166
- end
151
+ validate_attributes(attrs, **opts, &block)
152
+ block ? new_scope(attrs.first, type: opts[:type], as: opts[:as], optional: true, &block) : push_declared_params(attrs, as: opts[:as])
167
153
  end
168
154
 
169
155
  # Define common settings for one or more parameters
170
156
  # @param (see #requires)
171
157
  # @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) })
180
- end
181
-
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) })
158
+ def with(**opts, &)
159
+ new_group_attrs = @group&.deep_merge(opts) || opts
160
+ new_group_scope(new_group_attrs, &)
192
161
  end
193
162
 
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) })
163
+ %i[mutually_exclusive exactly_one_of at_least_one_of all_or_none_of].each do |validator|
164
+ define_method validator do |*attrs, message: nil|
165
+ validates(attrs, validator => { value: true, message: })
166
+ end
198
167
  end
199
168
 
200
169
  # Define a block of validations which should be applied if and only if
@@ -204,54 +173,38 @@ module Grape
204
173
  # @raise Grape::Exceptions::UnknownParameter if `attr` has not been
205
174
  # defined in this scope yet
206
175
  # @yield a parameter definition DSL
207
- def given(*attrs, &block)
176
+ def given(*attrs, &)
208
177
  attrs.each do |attr|
209
178
  proxy_attr = first_hash_key_or_param(attr)
210
179
  raise Grape::Exceptions::UnknownParameter.new(proxy_attr) unless declared_param?(proxy_attr)
211
180
  end
212
- new_lateral_scope(dependent_on: attrs, &block)
181
+ new_lateral_scope(dependent_on: attrs, &)
213
182
  end
214
183
 
215
184
  # Test for whether a certain parameter has been defined in this params
216
185
  # block yet.
217
186
  # @return [Boolean] whether the parameter has been defined
218
187
  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
188
+ # Elements of @declared_params of lateral scope are pushed in @parent. So check them in @parent.
189
+ return @parent.declared_param?(param) if lateral?
190
+
191
+ # @declared_params also includes hashes of options and such, but those
192
+ # won't be flattened out.
193
+ @declared_params.flatten.any? do |declared_param_attr|
194
+ first_hash_key_or_param(declared_param_attr.key) == param
228
195
  end
229
196
  end
230
197
 
231
198
  alias group requires
232
199
 
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
200
+ EmptyOptionalValue = Object.new.freeze
248
201
 
249
202
  # @param params [Hash] initial hash of parameters
250
203
  # @return hash of parameters relevant for the current scope
251
204
  # @api private
252
205
  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
206
+ params = @parent.qualifying_params.presence || @parent.params(params) if @parent
207
+ params = map_params(params, @element) if @element
255
208
  params
256
209
  end
257
210
 
@@ -260,6 +213,21 @@ module Grape
260
213
  def first_hash_key_or_param(parameter)
261
214
  parameter.is_a?(Hash) ? parameter.keys.first : parameter
262
215
  end
216
+
217
+ def map_params(params, element, is_array: false)
218
+ case params
219
+ when Array
220
+ params.map do |el|
221
+ map_params(el, element, is_array: true)
222
+ end
223
+ when Hash
224
+ params[element] || (@optional && is_array ? EmptyOptionalValue : {})
225
+ when EmptyOptionalValue
226
+ EmptyOptionalValue
227
+ else
228
+ {}
229
+ end
230
+ end
263
231
  end
264
232
  end
265
233
  end
@@ -3,171 +3,169 @@
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.namespace_inheritable[:default_format] if new_format.nil?
7
10
 
8
- include Grape::DSL::Configuration
11
+ inheritable_setting.namespace_inheritable[: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.namespace_inheritable[: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.namespace_inheritable[:format] = symbolic_new_format
21
+ inheritable_setting.namespace_inheritable[: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.namespace_stackable[:content_types] = { 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.namespace_stackable[:formatters] = { 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.namespace_stackable[:parsers] = { 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.
40
+ def default_error_formatter(new_formatter_name = nil)
41
+ return inheritable_setting.namespace_inheritable[:default_error_formatter] if new_formatter_name.nil?
63
42
 
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
43
+ new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name)
44
+ inheritable_setting.namespace_inheritable[:default_error_formatter] = new_formatter
45
+ end
69
46
 
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
47
+ def error_formatter(format, options = nil, with: nil)
48
+ formatter = with || options
49
+ inheritable_setting.namespace_stackable[:error_formatters] = { format.to_sym => 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 additional content-types, e.g.:
53
+ # content_type :xls, 'application/vnd.ms-excel'
54
+ def content_type(key, val)
55
+ inheritable_setting.namespace_stackable[:content_types] = { key.to_sym => val }
56
+ end
57
+
58
+ # All available content types.
59
+ def content_types
60
+ c_types = inheritable_setting.namespace_stackable_with_hash(:content_types)
61
+ Grape::ContentTypes.content_types_for c_types
62
+ end
63
+
64
+ # Specify the default status code for errors.
65
+ def default_error_status(new_status = nil)
66
+ return inheritable_setting.namespace_inheritable[:default_error_status] if new_status.nil?
80
67
 
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)
68
+ inheritable_setting.namespace_inheritable[:default_error_status] = new_status
69
+ end
70
+
71
+ # Allows you to rescue certain exceptions that occur to return
72
+ # a grape error rather than raising all the way to the
73
+ # server level.
74
+ #
75
+ # @example Rescue from custom exceptions
76
+ # class ExampleAPI < Grape::API
77
+ # class CustomError < StandardError; end
78
+ #
79
+ # rescue_from CustomError
80
+ # end
81
+ #
82
+ META_RESCUE_SELECTORS = %i[all grape_exceptions internal_grape_exceptions].freeze
83
+ private_constant :META_RESCUE_SELECTORS
84
+
85
+ # @overload rescue_from(*exception_classes, **options)
86
+ # @param [Array] exception_classes A list of classes that you want to rescue, or
87
+ # one of the meta selectors +:all+, +:grape_exceptions+,
88
+ # +:internal_grape_exceptions+. Meta selectors must be used alone;
89
+ # mixing with exception classes raises +ArgumentError+.
90
+ # @param [Block] block Execution block to handle the given exception.
91
+ # @param [Proc] with Execution proc to handle the given exception as an alternative
92
+ # to passing a block.
93
+ # @param [Boolean] rescue_subclasses Also rescue subclasses of exception classes;
94
+ # defaults to +true+.
95
+ # @param [Boolean] backtrace Include the rescued exception's backtrace in the
96
+ # rescue response body.
97
+ # @param [Boolean] original_exception Include +inspect+ of the rescued exception
98
+ # in the rescue response body.
99
+ def rescue_from(*args, with: nil, rescue_subclasses: true, backtrace: false, original_exception: false, &block)
100
+ handler = extract_handler(args, with:, block:)
101
+ meta_selector = (args & META_RESCUE_SELECTORS).first
102
+ raise ArgumentError, "rescue_from #{meta_selector.inspect} does not accept additional arguments" if meta_selector && args.size > 1
103
+
104
+ namespace_inheritable = nil
105
+ arg = nil
106
+
107
+ if args.one?
108
+ arg = args.first
109
+ namespace_inheritable = inheritable_setting.namespace_inheritable
133
110
  end
134
111
 
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])
112
+ case arg
113
+ when :all
114
+ namespace_inheritable[:rescue_all] = true
115
+ namespace_inheritable[:all_rescue_handler] = handler
116
+ when :grape_exceptions
117
+ namespace_inheritable[:rescue_all] = true
118
+ namespace_inheritable[:rescue_grape_exceptions] = true
119
+ namespace_inheritable[:grape_exceptions_rescue_handler] = handler
120
+ when :internal_grape_exceptions
121
+ namespace_inheritable[:internal_grape_exceptions_rescue_handler] = handler
122
+ else
123
+ handler_type = rescue_subclasses ? :rescue_handlers : :base_only_rescue_handlers
124
+ inheritable_setting.namespace_reverse_stackable[handler_type] = args.to_h { |klass| [klass, handler] }
159
125
  end
160
126
 
161
- private
127
+ inheritable_setting.namespace_stackable[:rescue_options] = RescueOptions.new(backtrace:, original_exception:)
128
+ end
129
+
130
+ # Allows you to specify a default representation entity for a
131
+ # class. This allows you to map your models to their respective
132
+ # entities once and then simply call `present` with the model.
133
+ #
134
+ # @example
135
+ # class ExampleAPI < Grape::API
136
+ # represent User, with: Entity::User
137
+ #
138
+ # get '/me' do
139
+ # present current_user # with: Entity::User is assumed
140
+ # end
141
+ # end
142
+ #
143
+ # Note that Grape will automatically go up the class ancestry to
144
+ # try to find a representing entity, so if you, for example, define
145
+ # an entity to represent `Object` then all presented objects will
146
+ # bubble up and utilize the entity provided on that `represent` call.
147
+ #
148
+ # @param model_class [Class] The model class that will be represented.
149
+ # @option options [Class] :with The entity class that will represent the model.
150
+ def represent(model_class, with:)
151
+ raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless with.is_a?(Class)
152
+
153
+ inheritable_setting.namespace_stackable[:representations] = { model_class => with }
154
+ end
155
+
156
+ private
162
157
 
163
- def extract_with(options)
164
- return unless options.key?(:with)
158
+ def extract_handler(args, with:, block:)
159
+ raise ArgumentError, 'both :with option and block cannot be passed' if block && with
165
160
 
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)
161
+ return args.pop if args.last.is_a?(Proc)
162
+ return block if block
163
+ return unless with
169
164
 
170
- raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
165
+ case with
166
+ when Proc, Symbol then with
167
+ when String then with.to_sym
168
+ else raise ArgumentError, "with: #{with.class}, expected Symbol, String or Proc"
171
169
  end
172
170
  end
173
171
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module DSL
5
+ # Immutable value object holding the response-shaping booleans accepted
6
+ # by +Grape::DSL::RequestResponse#rescue_from+. Stored on the
7
+ # inheritable settings as +namespace_stackable[:rescue_options]+ and
8
+ # delegated to by +Grape::Middleware::Error+ (which forwards
9
+ # +backtrace+/+original_exception+ to the formatter as
10
+ # +include_backtrace+/+include_original_exception+).
11
+ #
12
+ # Defaults are duplicated on +#initialize+ here and on +#rescue_from+'s
13
+ # signature on purpose: keeping them on both sides means each entry point
14
+ # is self-documenting without needing to import a shared constant — the
15
+ # DSL signature shows what a user sees in the IDE, and the Data object
16
+ # has working defaults when constructed directly (middleware
17
+ # `DEFAULT_OPTIONS`, spec fixtures, etc.). The two must stay in lockstep.
18
+ RescueOptions = Data.define(:backtrace, :original_exception) do
19
+ def initialize(backtrace: false, original_exception: false)
20
+ super
21
+ end
22
+ end
23
+ end
24
+ end