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
data/README.md CHANGED
@@ -1,172 +1,22 @@
1
1
  ![grape logo](grape.png)
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/grape.svg)](http://badge.fury.io/rb/grape)
4
- [![Build Status](https://github.com/ruby-grape/grape/workflows/test/badge.svg?branch=master)](https://github.com/ruby-grape/grape/actions)
5
- [![Code Climate](https://codeclimate.com/github/ruby-grape/grape.svg)](https://codeclimate.com/github/ruby-grape/grape)
4
+ [![test](https://github.com/ruby-grape/grape/actions/workflows/test.yml/badge.svg)](https://github.com/ruby-grape/grape/actions/workflows/test.yml)
6
5
  [![Coverage Status](https://coveralls.io/repos/github/ruby-grape/grape/badge.svg?branch=master)](https://coveralls.io/github/ruby-grape/grape?branch=master)
7
- [![Inline docs](https://inch-ci.org/github/ruby-grape/grape.svg)](https://inch-ci.org/github/ruby-grape/grape)
8
- [![Join the chat at https://gitter.im/ruby-grape/grape](https://badges.gitter.im/ruby-grape/grape.svg)](https://gitter.im/ruby-grape/grape?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
9
-
10
- ## Table of Contents
11
-
12
- - [What is Grape?](#what-is-grape)
13
- - [Stable Release](#stable-release)
14
- - [Project Resources](#project-resources)
15
- - [Grape for Enterprise](#grape-for-enterprise)
16
- - [Installation](#installation)
17
- - [Basic Usage](#basic-usage)
18
- - [Mounting](#mounting)
19
- - [All](#all)
20
- - [Rack](#rack)
21
- - [ActiveRecord without Rails](#activerecord-without-rails)
22
- - [Rails 4](#rails-4)
23
- - [Rails 5+](#rails-5)
24
- - [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
25
- - [Rails](#rails)
26
- - [Rails < 5.2](#rails--52)
27
- - [Rails 6.0](#rails-60)
28
- - [Modules](#modules)
29
- - [Remounting](#remounting)
30
- - [Mount Configuration](#mount-configuration)
31
- - [Versioning](#versioning)
32
- - [Path](#path)
33
- - [Header](#header)
34
- - [Accept-Version Header](#accept-version-header)
35
- - [Param](#param)
36
- - [Describing Methods](#describing-methods)
37
- - [Configuration](#configuration)
38
- - [Parameters](#parameters)
39
- - [Params Class](#params-class)
40
- - [Declared](#declared)
41
- - [Include Parent Namespaces](#include-parent-namespaces)
42
- - [Include Missing](#include-missing)
43
- - [Evaluate Given](#evaluate-given)
44
- - [Parameter Validation and Coercion](#parameter-validation-and-coercion)
45
- - [Supported Parameter Types](#supported-parameter-types)
46
- - [Integer/Fixnum and Coercions](#integerfixnum-and-coercions)
47
- - [Custom Types and Coercions](#custom-types-and-coercions)
48
- - [Multipart File Parameters](#multipart-file-parameters)
49
- - [First-Class JSON Types](#first-class-json-types)
50
- - [Multiple Allowed Types](#multiple-allowed-types)
51
- - [Validation of Nested Parameters](#validation-of-nested-parameters)
52
- - [Dependent Parameters](#dependent-parameters)
53
- - [Group Options](#group-options)
54
- - [Renaming](#renaming)
55
- - [Built-in Validators](#built-in-validators)
56
- - [allow_blank](#allow_blank)
57
- - [values](#values)
58
- - [except_values](#except_values)
59
- - [same_as](#same_as)
60
- - [regexp](#regexp)
61
- - [mutually_exclusive](#mutually_exclusive)
62
- - [exactly_one_of](#exactly_one_of)
63
- - [at_least_one_of](#at_least_one_of)
64
- - [all_or_none_of](#all_or_none_of)
65
- - [Nested mutually_exclusive, exactly_one_of, at_least_one_of, all_or_none_of](#nested-mutually_exclusive-exactly_one_of-at_least_one_of-all_or_none_of)
66
- - [Namespace Validation and Coercion](#namespace-validation-and-coercion)
67
- - [Custom Validators](#custom-validators)
68
- - [Validation Errors](#validation-errors)
69
- - [I18n](#i18n)
70
- - [Custom Validation messages](#custom-validation-messages)
71
- - [presence, allow_blank, values, regexp](#presence-allow_blank-values-regexp)
72
- - [same_as](#same_as-1)
73
- - [all_or_none_of](#all_or_none_of-1)
74
- - [mutually_exclusive](#mutually_exclusive-1)
75
- - [exactly_one_of](#exactly_one_of-1)
76
- - [at_least_one_of](#at_least_one_of-1)
77
- - [Coerce](#coerce)
78
- - [With Lambdas](#with-lambdas)
79
- - [Pass symbols for i18n translations](#pass-symbols-for-i18n-translations)
80
- - [Overriding Attribute Names](#overriding-attribute-names)
81
- - [With Default](#with-default)
82
- - [Headers](#headers)
83
- - [Request](#request)
84
- - [Header Case Handling](#header-case-handling)
85
- - [Response](#response)
86
- - [Routes](#routes)
87
- - [Helpers](#helpers)
88
- - [Path Helpers](#path-helpers)
89
- - [Parameter Documentation](#parameter-documentation)
90
- - [Cookies](#cookies)
91
- - [HTTP Status Code](#http-status-code)
92
- - [Redirecting](#redirecting)
93
- - [Recognizing Path](#recognizing-path)
94
- - [Allowed Methods](#allowed-methods)
95
- - [Raising Exceptions](#raising-exceptions)
96
- - [Default Error HTTP Status Code](#default-error-http-status-code)
97
- - [Handling 404](#handling-404)
98
- - [Exception Handling](#exception-handling)
99
- - [Rescuing exceptions inside namespaces](#rescuing-exceptions-inside-namespaces)
100
- - [Unrescuable Exceptions](#unrescuable-exceptions)
101
- - [Exceptions that should be rescued explicitly](#exceptions-that-should-be-rescued-explicitly)
102
- - [Rails 3.x](#rails-3x)
103
- - [Logging](#logging)
104
- - [API Formats](#api-formats)
105
- - [JSONP](#jsonp)
106
- - [CORS](#cors)
107
- - [Content-type](#content-type)
108
- - [API Data Formats](#api-data-formats)
109
- - [JSON and XML Processors](#json-and-xml-processors)
110
- - [RESTful Model Representations](#restful-model-representations)
111
- - [Grape Entities](#grape-entities)
112
- - [Hypermedia and Roar](#hypermedia-and-roar)
113
- - [Rabl](#rabl)
114
- - [Active Model Serializers](#active-model-serializers)
115
- - [Sending Raw or No Data](#sending-raw-or-no-data)
116
- - [Authentication](#authentication)
117
- - [Basic and Digest Auth](#basic-and-digest-auth)
118
- - [Register custom middleware for authentication](#register-custom-middleware-for-authentication)
119
- - [Describing and Inspecting an API](#describing-and-inspecting-an-api)
120
- - [Current Route and Endpoint](#current-route-and-endpoint)
121
- - [Before, After and Finally](#before-after-and-finally)
122
- - [Anchoring](#anchoring)
123
- - [Using Custom Middleware](#using-custom-middleware)
124
- - [Grape Middleware](#grape-middleware)
125
- - [Rails Middleware](#rails-middleware)
126
- - [Remote IP](#remote-ip)
127
- - [Writing Tests](#writing-tests)
128
- - [Writing Tests with Rack](#writing-tests-with-rack)
129
- - [RSpec](#rspec)
130
- - [Airborne](#airborne)
131
- - [MiniTest](#minitest)
132
- - [Writing Tests with Rails](#writing-tests-with-rails)
133
- - [RSpec](#rspec-1)
134
- - [MiniTest](#minitest-1)
135
- - [Stubbing Helpers](#stubbing-helpers)
136
- - [Reloading API Changes in Development](#reloading-api-changes-in-development)
137
- - [Reloading in Rack Applications](#reloading-in-rack-applications)
138
- - [Reloading in Rails Applications](#reloading-in-rails-applications)
139
- - [Performance Monitoring](#performance-monitoring)
140
- - [Active Support Instrumentation](#active-support-instrumentation)
141
- - [endpoint_run.grape](#endpoint_rungrape)
142
- - [endpoint_render.grape](#endpoint_rendergrape)
143
- - [endpoint_run_filters.grape](#endpoint_run_filtersgrape)
144
- - [endpoint_run_validators.grape](#endpoint_run_validatorsgrape)
145
- - [format_response.grape](#format_responsegrape)
146
- - [Monitoring Products](#monitoring-products)
147
- - [Contributing to Grape](#contributing-to-grape)
148
- - [Security](#security)
149
- - [License](#license)
150
- - [Copyright](#copyright)
151
6
 
152
7
  ## What is Grape?
153
8
 
154
- Grape is a REST-like API framework for Ruby. It's designed to run on Rack
155
- or complement existing web application frameworks such as Rails and Sinatra by
156
- providing a simple DSL to easily develop RESTful APIs. It has built-in support
157
- for common conventions, including multiple formats, subdomain/prefix restriction,
158
- content negotiation, versioning and much more.
9
+ Grape is a REST-like API framework for Ruby. It's designed to run on Rack or complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily develop RESTful APIs. It has built-in support for common conventions, including multiple formats, subdomain/prefix restriction, content negotiation, versioning and much more.
159
10
 
160
11
  ## Stable Release
161
12
 
162
- You're reading the documentation for the stable release of Grape, **1.8.0**.
163
- Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
13
+ You're reading the documentation for the stable release of Grape, 4.0.1.
164
14
 
165
15
  ## Project Resources
166
16
 
167
17
  * [Grape Website](http://www.ruby-grape.org)
168
18
  * [Documentation](http://www.rubydoc.info/gems/grape)
169
- * Need help? Try [Grape Google Group](http://groups.google.com/group/ruby-grape) or [Gitter](https://gitter.im/ruby-grape/grape)
19
+ * Need help? [Open an Issue](https://github.com/ruby-grape/grape/issues)
170
20
  * [Follow us on Twitter](https://twitter.com/grapeframework)
171
21
 
172
22
  ## Grape for Enterprise
@@ -177,7 +27,7 @@ The maintainers of Grape are working with Tidelift to deliver commercial support
177
27
 
178
28
  ## Installation
179
29
 
180
- Ruby 2.4 or newer is required.
30
+ Ruby 3.3 or newer is required.
181
31
 
182
32
  Grape is available as a gem, to install it run:
183
33
 
@@ -186,8 +36,7 @@ Grape is available as a gem, to install it run:
186
36
  ## Basic Usage
187
37
 
188
38
  Grape APIs are Rack applications that are created by subclassing `Grape::API`.
189
- Below is a simple example showing some of the more common features of Grape in
190
- the context of recreating parts of the Twitter API.
39
+ Below is a simple example showing some of the more common features of Grape in the context of recreating parts of the Twitter API.
191
40
 
192
41
  ```ruby
193
42
  module Twitter
@@ -266,12 +115,16 @@ module Twitter
266
115
  end
267
116
  ```
268
117
 
118
+ ## Rails 7.1
119
+
120
+ Grape's [deprecator](https://api.rubyonrails.org/v7.1.0/classes/ActiveSupport/Deprecation.html) will be added to your application's deprecators [automatically](lib/grape/railtie.rb) as `:grape`, so that your application's configuration can be applied to it.
121
+
269
122
  ## Mounting
270
123
 
271
124
  ### All
272
125
 
273
126
 
274
- By default Grape will compile the routes on the first route, it is possible to pre-load routes using the `compile!` method.
127
+ By default Grape will compile the routes on the first route, but it is possible to pre-load routes using the `compile!` method.
275
128
 
276
129
  ```ruby
277
130
  Twitter::API.compile!
@@ -281,8 +134,7 @@ This can be added to your `config.ru` (if using rackup), `application.rb` (if us
281
134
 
282
135
  ### Rack
283
136
 
284
- The above sample creates a Rack application that can be run from a rackup `config.ru` file
285
- with `rackup`:
137
+ The above sample creates a Rack application that can be run from a rackup `config.ru` file with `rackup`:
286
138
 
287
139
  ```ruby
288
140
  run Twitter::API
@@ -306,32 +158,9 @@ And would respond to the following routes:
306
158
 
307
159
  Grape will also automatically respond to HEAD and OPTIONS for all GET, and just OPTIONS for all other routes.
308
160
 
309
- ### ActiveRecord without Rails
310
-
311
- If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
312
- is handled correctly.
313
-
314
- #### Rails 4
315
-
316
- The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
317
- `config.ru` before mounting Grape, e.g.:
318
-
319
- ```ruby
320
- use ActiveRecord::ConnectionAdapters::ConnectionManagement
321
- ```
322
-
323
- #### Rails 5+
324
-
325
- Use [otr-activerecord](https://github.com/jhollinger/otr-activerecord) as follows:
326
-
327
- ```ruby
328
- use OTR::ActiveRecord::ConnectionManagement
329
- ```
330
-
331
161
  ### Alongside Sinatra (or other frameworks)
332
162
 
333
- If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using
334
- `Rack::Cascade`:
163
+ If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using `Rack::Cascade`:
335
164
 
336
165
  ```ruby
337
166
  # Example config.ru
@@ -367,21 +196,8 @@ Modify `config/routes`:
367
196
  ```ruby
368
197
  mount Twitter::API => '/'
369
198
  ```
370
-
371
- #### Rails < 5.2
372
-
373
- Modify `application.rb`:
374
-
375
- ```ruby
376
- config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
377
- config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
378
- ```
379
-
380
- See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
381
-
382
- #### Rails 6.0
383
-
384
- For Rails versions greater than 6.0.0.beta2, `Zeitwerk` autoloader is the default for CRuby. By default `Zeitwerk` inflects `api` as `Api` instead of `API`. To make our example work, you need to uncomment the lines at the bottom of `config/initializers/inflections.rb`, and add `API` as an acronym:
199
+ #### Zeitwerk
200
+ Rails's default autoloader is `Zeitwerk`. By default, it inflects `api` as `Api` instead of `API`. To make our example work, you need to uncomment the lines at the bottom of `config/initializers/inflections.rb`, and add `API` as an acronym:
385
201
 
386
202
  ```ruby
387
203
  ActiveSupport::Inflector.inflections(:en) do |inflect|
@@ -391,8 +207,7 @@ end
391
207
 
392
208
  ### Modules
393
209
 
394
- You can mount multiple API implementations inside another one. These don't have to be
395
- different versions, but may be components of the same API.
210
+ You can mount multiple API implementations inside another one. These don't have to be different versions, but may be components of the same API.
396
211
 
397
212
  ```ruby
398
213
  class Twitter::API < Grape::API
@@ -409,7 +224,7 @@ class Twitter::API < Grape::API
409
224
  end
410
225
  ```
411
226
 
412
- Keep in mind such declarations as `before/after/rescue_from` must be placed before `mount` in a case where they should be inherited.
227
+ Declarations as `before/after/rescue_from` can be placed before or after `mount`. In any case they will be inherited.
413
228
 
414
229
  ```ruby
415
230
  class Twitter::API < Grape::API
@@ -417,8 +232,20 @@ class Twitter::API < Grape::API
417
232
  header 'X-Base-Header', 'will be defined for all APIs that are mounted below'
418
233
  end
419
234
 
235
+ rescue_from :all do
236
+ error!({ "error" => "Internal Server Error" }, 500)
237
+ end
238
+
420
239
  mount Twitter::Users
421
240
  mount Twitter::Search
241
+
242
+ after do
243
+ clean_cache!
244
+ end
245
+
246
+ rescue_from ZeroDivisionError do
247
+ error!({ "error" => "Not found" }, 404)
248
+ end
422
249
  end
423
250
  ```
424
251
 
@@ -484,7 +311,7 @@ mount ::Some::Api => '/some/api', with: { condition: true }
484
311
 
485
312
  You can access `configuration` on the class (to use as dynamic attributes), inside blocks (like namespace)
486
313
 
487
- If you want logic happening given on an `configuration`, you can use the helper `given`.
314
+ If you want logic happening based on a `configuration`, you can use the helper `given`.
488
315
 
489
316
  ```ruby
490
317
  class ConditionalEndpoint::API < Grape::API
@@ -549,10 +376,69 @@ end
549
376
 
550
377
  ## Versioning
551
378
 
552
- There are four strategies in which clients can reach your API's endpoints: `:path`,
553
- `:header`, `:accept_version_header` and `:param`. The default strategy is `:path`.
379
+ You have the option to provide various versions of your API by establishing a separate `Grape::API` class for each offered version and then integrating them into a primary `Grape::API` class. Ensure that newer versions are mounted before older ones. The default approach to versioning directs the request to the subsequent Rack middleware if a specific version is not found.
380
+
381
+ ```ruby
382
+ require 'v1'
383
+ require 'v2'
384
+ require 'v3'
385
+ class App < Grape::API
386
+ mount V3
387
+ mount V2
388
+ mount V1
389
+ end
390
+ ```
554
391
 
555
- ### Path
392
+ To maintain the same endpoints from earlier API versions without rewriting them, you can indicate multiple versions within the previous API versions.
393
+
394
+ ```ruby
395
+ class V1 < Grape::API
396
+ version 'v1', 'v2', 'v3'
397
+
398
+ get '/foo' do
399
+ # your code for GET /foo
400
+ end
401
+
402
+ get '/other' do
403
+ # your code for GET /other
404
+ end
405
+ end
406
+
407
+ class V2 < Grape::API
408
+ version 'v2', 'v3'
409
+
410
+ get '/var' do
411
+ # your code for GET /var
412
+ end
413
+ end
414
+
415
+ class V3 < Grape::API
416
+ version 'v3'
417
+
418
+ get '/foo' do
419
+ # your new code for GET /foo
420
+ end
421
+ end
422
+ ```
423
+
424
+ Using the example provided, the subsequent endpoints will be accessible across various versions:
425
+
426
+ ```shell
427
+ GET /v1/foo
428
+ GET /v1/other
429
+ GET /v2/foo # => Same behavior as v1
430
+ GET /v2/other # => Same behavior as v1
431
+ GET /v2/var # => New endpoint not available in v1
432
+ GET /v3/foo # => Different behavior to v1 and v2
433
+ GET /v3/other # => Same behavior as v1 and v2
434
+ GET /v3/var # => Same behavior as v2
435
+ ```
436
+
437
+ There are four strategies in which clients can reach your API's endpoints: `:path`, `:header`, `:accept_version_header` and `:param`. The default strategy is `:path`.
438
+
439
+ ### Strategies
440
+
441
+ #### Path
556
442
 
557
443
  ```ruby
558
444
  version 'v1', using: :path
@@ -562,7 +448,7 @@ Using this versioning strategy, clients should pass the desired version in the U
562
448
 
563
449
  curl http://localhost:9292/v1/statuses/public_timeline
564
450
 
565
- ### Header
451
+ #### Header
566
452
 
567
453
  ```ruby
568
454
  version 'v1', using: :header, vendor: 'twitter'
@@ -576,20 +462,19 @@ vnd.vendor-and-or-resource-v1234+format
576
462
 
577
463
  Basically all tokens between the final `-` and the `+` will be interpreted as the version.
578
464
 
579
- Using this versioning strategy, clients should pass the desired version in the HTTP `Accept` head.
465
+ Using this versioning strategy, clients should pass the desired version in the HTTP `Accept` header.
580
466
 
581
467
  curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
582
468
 
583
- By default, the first matching version is used when no `Accept` header is
584
- supplied. This behavior is similar to routing in Rails. To circumvent this default behavior,
585
- one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
586
- is returned when no correct `Accept` header is supplied.
469
+ By default, the first matching version is used when no `Accept` header is supplied. This behavior is similar to routing in Rails. To circumvent this default behavior, one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error is returned when no correct `Accept` header is supplied.
587
470
 
588
- When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is returned if the `:cascade`
589
- option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route
590
- matches.
471
+ When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is returned if the `:cascade` option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route matches.
591
472
 
592
- ### Accept-Version Header
473
+ Grape will evaluate the relative quality preference included in Accept headers and default to a quality of 1.0 when omitted. In the following example a Grape API that supports XML and JSON in that order will return JSON:
474
+
475
+ curl -H "Accept: text/xml;q=0.8, application/json;q=0.9" localhost:1234/resource
476
+
477
+ #### Accept-Version Header
593
478
 
594
479
  ```ruby
595
480
  version 'v1', using: :accept_version_header
@@ -599,20 +484,15 @@ Using this versioning strategy, clients should pass the desired version in the H
599
484
 
600
485
  curl -H "Accept-Version:v1" http://localhost:9292/statuses/public_timeline
601
486
 
602
- By default, the first matching version is used when no `Accept-Version` header is
603
- supplied. This behavior is similar to routing in Rails. To circumvent this default behavior,
604
- one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
605
- is returned when no correct `Accept` header is supplied and the `:cascade` option is set to `false`.
606
- Otherwise a `404 Not Found` error is returned by Rack if no other route matches.
487
+ By default, the first matching version is used when no `Accept-Version` header is supplied. This behavior is similar to routing in Rails. To circumvent this default behavior, one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error is returned when no correct `Accept-Version` header is supplied and the `:cascade` option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route matches.
607
488
 
608
- ### Param
489
+ #### Param
609
490
 
610
491
  ```ruby
611
492
  version 'v1', using: :param
612
493
  ```
613
494
 
614
- Using this versioning strategy, clients should pass the desired version as a request parameter,
615
- either in the URL query string or in the request body.
495
+ Using this versioning strategy, clients should pass the desired version as a request parameter, either in the URL query string or in the request body.
616
496
 
617
497
  curl http://localhost:9292/statuses/public_timeline?apiver=v1
618
498
 
@@ -625,11 +505,32 @@ version 'v1', using: :param, parameter: 'v'
625
505
  curl http://localhost:9292/statuses/public_timeline?v=v1
626
506
 
627
507
 
508
+ ## Linting
509
+
510
+ You can check whether your API is in conformance with the [Rack's specification](https://github.com/rack/rack/blob/main/SPEC.rdoc) by calling `lint!` at the API level or through [configuration](#configuration).
511
+
512
+ ```ruby
513
+ class Api < Grape::API
514
+ lint!
515
+ end
516
+ ```
517
+ ```ruby
518
+ Grape.configure do |config|
519
+ config.lint = true
520
+ end
521
+ ```
522
+ ```ruby
523
+ Grape.config.lint = true
524
+ ```
525
+
526
+ ### Bug in Rack::ETag under Rack 3.X
527
+ If you're using Rack 3.X and the `Rack::ETag` middleware (used by [Rails](https://guides.rubyonrails.org/rails_on_rack.html#inspecting-middleware-stack)), a [bug](https://github.com/rack/rack/pull/2324) related to linting has been fixed in [3.1.13](https://github.com/rack/rack/blob/v3.1.13/CHANGELOG.md#3113---2025-04-13) and [3.0.15](https://github.com/rack/rack/blob/v3.1.13/CHANGELOG.md#3015---2025-04-13) respectively.
528
+
628
529
  ## Describing Methods
629
530
 
630
531
  You can add a description to API methods and namespaces. The description would be used by [grape-swagger][grape-swagger] to generate swagger compliant documentation.
631
532
 
632
- Note: Description block is only for documentation and won't affects API behavior.
533
+ Note: Description block is only for documentation and won't affect API behavior.
633
534
 
634
535
  ```ruby
635
536
  desc 'Returns your public timeline.' do
@@ -665,7 +566,7 @@ end
665
566
  * `params`: Define parameters directly from an `Entity`
666
567
  * `success`: (former entity) The `Entity` to be used to present the success response for this route.
667
568
  * `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities.
668
- * `default`: The definition and `Entity` used to present the default response for this route.
569
+ * `default_response`: (former `default`) The definition and `Entity` used to present the default response for this route.
669
570
  * `named`: A helper to give a route a name and find it with this name in the documentation Hash
670
571
  * `headers`: A definition of the used Headers
671
572
  * Other options can be found in [grape-swagger][grape-swagger]
@@ -678,6 +579,7 @@ Use `Grape.configure` to set up global settings at load time.
678
579
  Currently the configurable settings are:
679
580
 
680
581
  * `param_builder`: Sets the [Parameter Builder](#parameters), defaults to `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder`.
582
+ * `raise_rendering_errors`: Lets an error response that cannot be rendered propagate out of the middleware stack instead of being answered with a failsafe `500`, defaults to `false`. See [When the error response itself cannot be rendered](#when-the-error-response-itself-cannot-be-rendered).
681
583
 
682
584
  To change a setting value make sure that at some point during load time the following code runs
683
585
 
@@ -691,10 +593,13 @@ For example, for the `param_builder`, the following code could run in an initial
691
593
 
692
594
  ```ruby
693
595
  Grape.configure do |config|
694
- config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
596
+ config.param_builder = :hashie_mash
695
597
  end
696
598
  ```
697
599
 
600
+ Available parameter builders are `:hash`, `:hash_with_indifferent_access`, and `:hashie_mash`.
601
+ See [params_builder](lib/grape/params_builder).
602
+
698
603
  You can also configure a single API:
699
604
 
700
605
  ```ruby
@@ -703,13 +608,11 @@ API.configure do |config|
703
608
  end
704
609
  ```
705
610
 
706
- This will be available inside the API with `configuration`, as if it were
707
- [mount configuration](#mount-configuration).
611
+ This will be available inside the API with `configuration`, as if it were [mount configuration](#mount-configuration).
708
612
 
709
613
  ## Parameters
710
614
 
711
- Request parameters are available through the `params` hash object. This includes `GET`, `POST`
712
- and `PUT` parameters, along with any named parameters you specify in your route strings.
615
+ Request parameters are available through the `params` hash object. This includes `GET`, `POST` and `PUT` parameters, along with any named parameters you specify in your route strings.
713
616
 
714
617
  ```ruby
715
618
  get :public_timeline do
@@ -717,8 +620,7 @@ get :public_timeline do
717
620
  end
718
621
  ```
719
622
 
720
- Parameters are automatically populated from the request body on `POST` and `PUT` for form input, JSON and
721
- XML content-types.
623
+ Parameters are automatically populated from the request body on `POST`, `PUT` and `QUERY` for form input, JSON and XML content-types.
722
624
 
723
625
  The request:
724
626
 
@@ -764,7 +666,7 @@ By default parameters are available as `ActiveSupport::HashWithIndifferentAccess
764
666
 
765
667
  ```ruby
766
668
  class API < Grape::API
767
- include Grape::Extensions::Hashie::Mash::ParamBuilder
669
+ build_with :hashie_mash
768
670
 
769
671
  params do
770
672
  optional :color, type: String
@@ -778,16 +680,15 @@ The class can also be overridden on individual parameter blocks using `build_wit
778
680
 
779
681
  ```ruby
780
682
  params do
781
- build_with Grape::Extensions::Hash::ParamBuilder
683
+ build_with :hash
782
684
  optional :color, type: String
783
685
  end
784
686
  ```
785
687
 
786
- Or globally with the [Configuration](#configuration) `Grape.configure.param_builder`.
787
-
788
688
  In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.
789
689
 
790
- Available parameter builders are `Grape::Extensions::Hash::ParamBuilder`, `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder` and `Grape::Extensions::Hashie::Mash::ParamBuilder`.
690
+ Available parameter builders are `:hash`, `:hash_with_indifferent_access`, and `:hashie_mash`.
691
+ See [params_builder](lib/grape/params_builder).
791
692
 
792
693
  ### Declared
793
694
 
@@ -1057,8 +958,7 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
1057
958
  }
1058
959
  ````
1059
960
 
1060
- Note that an attribute with a `nil` value is not considered *missing* and will also be returned
1061
- when `include_missing` is set to `false`:
961
+ Note that an attribute with a `nil` value is not considered *missing* and will also be returned when `include_missing` is set to `false`:
1062
962
 
1063
963
  **Request**
1064
964
 
@@ -1176,6 +1076,35 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/child -d '{"chil
1176
1076
  }
1177
1077
  ````
1178
1078
 
1079
+ ### Parameter Precedence
1080
+
1081
+ Using `route_param` takes higher precedence over a regular parameter defined with same name:
1082
+
1083
+ ```ruby
1084
+ params do
1085
+ requires :foo, type: String
1086
+ end
1087
+ route_param :foo do
1088
+ get do
1089
+ { value: params[:foo] }
1090
+ end
1091
+ end
1092
+ ```
1093
+
1094
+ **Request**
1095
+
1096
+ ```bash
1097
+ curl -X POST -H "Content-Type: application/json" localhost:9292/bar -d '{"foo": "baz"}'
1098
+ ```
1099
+
1100
+ **Response**
1101
+
1102
+ ```json
1103
+ {
1104
+ "value": "bar"
1105
+ }
1106
+ ```
1107
+
1179
1108
  ## Parameter Validation and Coercion
1180
1109
 
1181
1110
  You can define validations and coercion options for your parameters using a `params` block.
@@ -1197,8 +1126,7 @@ put ':id' do
1197
1126
  end
1198
1127
  ```
1199
1128
 
1200
- When a type is specified an implicit validation is done after the coercion to ensure
1201
- the output type is the one declared.
1129
+ When a type is specified an implicit validation is done after the coercion to ensure the output type is the one declared.
1202
1130
 
1203
1131
  Optional parameters can have a default value.
1204
1132
 
@@ -1210,9 +1138,7 @@ params do
1210
1138
  end
1211
1139
  ```
1212
1140
 
1213
- Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
1214
- number for each call to the endpoint of this `params` block. To have the default evaluate
1215
- lazily with each request use a lambda, like `:random_number` above.
1141
+ Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same number for each call to the endpoint of this `params` block. To have the default evaluate lazily with each request use a lambda, like `:random_number` above.
1216
1142
 
1217
1143
  Note that default values will be passed through to any validation options specified.
1218
1144
  The following example will always fail if `:color` is not explicitly provided.
@@ -1231,6 +1157,15 @@ params do
1231
1157
  end
1232
1158
  ```
1233
1159
 
1160
+ You can use the value of one parameter as the default value of some other parameter. In this case, if the `primary_color` parameter is not provided, it will have the same value as the `color` one. If both of them not provided, both of them will have `blue` value.
1161
+
1162
+ ```ruby
1163
+ params do
1164
+ optional :color, type: String, default: 'blue'
1165
+ optional :primary_color, type: String, default: -> (params) { params[:color] }
1166
+ end
1167
+ ```
1168
+
1234
1169
  ### Supported Parameter Types
1235
1170
 
1236
1171
  The following are all valid types, supported out of the box by Grape:
@@ -1248,36 +1183,9 @@ The following are all valid types, supported out of the box by Grape:
1248
1183
  * Rack::Multipart::UploadedFile (alias `File`)
1249
1184
  * JSON
1250
1185
 
1251
- ### Integer/Fixnum and Coercions
1252
-
1253
- Please be aware that the behavior differs between Ruby 2.4 and earlier versions.
1254
- In Ruby 2.4, values consisting of numbers are converted to Integer, but in earlier versions it will be treated as Fixnum.
1255
-
1256
- ```ruby
1257
- params do
1258
- requires :integers, type: Hash do
1259
- requires :int, coerce: Integer
1260
- end
1261
- end
1262
- get '/int' do
1263
- params[:integers][:int].class
1264
- end
1265
-
1266
- ...
1267
-
1268
- get '/int' integers: { int: '45' }
1269
- #=> Integer in ruby 2.4
1270
- #=> Fixnum in earlier ruby versions
1271
- ```
1272
-
1273
1186
  ### Custom Types and Coercions
1274
1187
 
1275
- Aside from the default set of supported types listed above, any class can be
1276
- used as a type as long as an explicit coercion method is supplied. If the type
1277
- implements a class-level `parse` method, Grape will use it automatically.
1278
- This method must take one string argument and return an instance of the correct
1279
- type, or return an instance of `Grape::Types::InvalidValue` which optionally
1280
- accepts a message to be returned in the response.
1188
+ Aside from the default set of supported types listed above, any class can be used as a type as long as an explicit coercion method is supplied. If the type implements a class-level `parse` method, Grape will use it automatically. This method must take one string argument and return an instance of the correct type, or return an instance of `Grape::Types::InvalidValue` which optionally accepts a message to be returned in the response.
1281
1189
 
1282
1190
  ```ruby
1283
1191
  class Color
@@ -1287,7 +1195,7 @@ class Color
1287
1195
  end
1288
1196
 
1289
1197
  def self.parse(value)
1290
- return new(value) if %w[blue red green]).include?(value)
1198
+ return new(value) if %w[blue red green].include?(value)
1291
1199
 
1292
1200
  Grape::Types::InvalidValue.new('Unsupported color')
1293
1201
  end
@@ -1305,10 +1213,7 @@ get '/stuff' do
1305
1213
  end
1306
1214
  ```
1307
1215
 
1308
- Alternatively, a custom coercion method may be supplied for any type of parameter
1309
- using `coerce_with`. Any class or object may be given that implements a `parse` or
1310
- `call` method, in that order of precedence. The method must accept a single string
1311
- parameter, and the return value must match the given `type`.
1216
+ Alternatively, a custom coercion method may be supplied for any type of parameter using `coerce_with`. Any class or object may be given that implements a `parse` or `call` method, in that order of precedence. The method must accept a single string parameter, and the return value must match the given `type`.
1312
1217
 
1313
1218
  ```ruby
1314
1219
  params do
@@ -1332,9 +1237,7 @@ params do
1332
1237
  end
1333
1238
  ```
1334
1239
 
1335
- Grape will assert that coerced values match the given `type`, and will reject the request
1336
- if they do not. To override this behaviour, custom types may implement a `parsed?` method
1337
- that should accept a single argument and return `true` if the value passes type validation.
1240
+ Grape will assert that coerced values match the given `type`, and will reject the request if they do not. To override this behaviour, custom types may implement a `parsed?` method that should accept a single argument and return `true` if the value passes type validation.
1338
1241
 
1339
1242
  ```ruby
1340
1243
  class SecureUri
@@ -1369,9 +1272,7 @@ end
1369
1272
 
1370
1273
  ### First-Class `JSON` Types
1371
1274
 
1372
- Grape supports complex parameters given as JSON-formatted strings using the special `type: JSON`
1373
- declaration. JSON objects and arrays of objects are accepted equally, with nested validation
1374
- rules applied to all objects in either case:
1275
+ Grape supports complex parameters given as JSON-formatted strings using the special `type: JSON` declaration. JSON objects and arrays of objects are accepted equally, with nested validation rules applied to all objects in either case:
1375
1276
 
1376
1277
  ```ruby
1377
1278
  params do
@@ -1390,8 +1291,7 @@ client.get('/', json: '{"int":4}') # => HTTP 400
1390
1291
  client.get('/', json: '[{"int":4}]') # => HTTP 400
1391
1292
  ```
1392
1293
 
1393
- Additionally `type: Array[JSON]` may be used, which explicitly marks the parameter as an array
1394
- of objects. If a single object is supplied it will be wrapped.
1294
+ Additionally `type: Array[JSON]` may be used, which explicitly marks the parameter as an array of objects. If a single object is supplied it will be wrapped.
1395
1295
 
1396
1296
  ```ruby
1397
1297
  params do
@@ -1403,8 +1303,7 @@ get '/' do
1403
1303
  params[:json].each { |obj| ... } # always works
1404
1304
  end
1405
1305
  ```
1406
- For stricter control over the type of JSON structure which may be supplied,
1407
- use `type: Array, coerce_with: JSON` or `type: Hash, coerce_with: JSON`.
1306
+ For stricter control over the type of JSON structure which may be supplied, use `type: Array, coerce_with: JSON` or `type: Hash, coerce_with: JSON`.
1408
1307
 
1409
1308
  ### Multiple Allowed Types
1410
1309
 
@@ -1423,8 +1322,7 @@ client.get('/', status_code: 300) # => 300
1423
1322
  client.get('/', status_code: %w(404 NOT FOUND)) # => [404, "NOT", "FOUND"]
1424
1323
  ```
1425
1324
 
1426
- As a special case, variant-member-type collections may also be declared, by
1427
- passing a `Set` or `Array` with more than one member to `type`:
1325
+ As a special case, variant-member-type collections may also be declared, by passing a `Set` or `Array` with more than one member to `type`:
1428
1326
 
1429
1327
  ```ruby
1430
1328
  params do
@@ -1437,14 +1335,61 @@ end
1437
1335
  client.get('/', status_codes: %w(1 two)) # => [1, "two"]
1438
1336
  ```
1439
1337
 
1338
+ ### Multiple Hash Schemas with `oneof`
1339
+
1340
+ A Hash parameter that may take one of several different shapes can be declared with the `oneof:` option. Each variant is a `Proc` that uses the normal `params` DSL — so the full validator surface (`requires`, `optional`, `regexp:`, `values:`, `allow_blank:`, nested `Hash`/`Array`, etc.) is available inside.
1341
+
1342
+ ```ruby
1343
+ params do
1344
+ requires :value, type: Hash, oneof: [
1345
+ proc { requires :fixed_price, type: Float },
1346
+ proc do
1347
+ requires :time_unit, type: String
1348
+ requires :rate, type: Float
1349
+ end
1350
+ ]
1351
+ end
1352
+ post '/pricing' do
1353
+ params[:value]
1354
+ end
1355
+ ```
1356
+
1357
+ Both of these requests succeed:
1358
+
1359
+ ```bash
1360
+ curl -d '{"value":{"fixed_price":100.0}}' -H 'Content-Type: application/json' /pricing
1361
+ curl -d '{"value":{"time_unit":"hour","rate":50.0}}' -H 'Content-Type: application/json' /pricing
1362
+ ```
1363
+
1364
+ A request that doesn't match any variant returns `400` with `value does not match any of the allowed schemas`.
1365
+
1366
+ Variants are tried in declaration order; the first variant that validates without errors wins, and any coercions it performed (e.g. `"150.5"` → `150.5`) are applied to the request params. Nested hash structures inside variants work the same as elsewhere in Grape:
1367
+
1368
+ ```ruby
1369
+ params do
1370
+ requires :options, type: Hash, oneof: [
1371
+ proc do
1372
+ requires :form, type: Hash do
1373
+ requires :colour, type: String
1374
+ optional :size, type: Integer
1375
+ end
1376
+ end,
1377
+ proc do
1378
+ requires :api, type: Hash do
1379
+ requires :authenticated, type: Grape::API::Boolean
1380
+ end
1381
+ end
1382
+ ]
1383
+ end
1384
+ ```
1385
+
1386
+ `oneof:` requires `type: Hash`. The variants array must be non-empty and each entry must be a `Proc`; violating either raises an `ArgumentError` at definition time.
1387
+
1440
1388
  ### Validation of Nested Parameters
1441
1389
 
1442
1390
  Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
1443
- In the [above example](#parameter-validation-and-coercion), this means `params[:media][:url]` is required along with `params[:id]`,
1444
- and `params[:audio][:format]` is required only if `params[:audio]` is present.
1445
- With a block, `group`, `requires` and `optional` accept an additional option `type` which can
1446
- be either `Array` or `Hash`, and defaults to `Array`. Depending on the value, the nested
1447
- parameters will be treated either as values of a hash or as values of hashes in an array.
1391
+ In the [above example](#parameter-validation-and-coercion), this means `params[:media][:url]` is required along with `params[:id]`, and `params[:audio][:format]` is required only if `params[:audio]` is present.
1392
+ With a block, `group`, `requires` and `optional` accept an additional option `type` which can be either `Array` or `Hash`, and defaults to `Array`. Depending on the value, the nested parameters will be treated either as values of a hash or as values of hashes in an array.
1448
1393
 
1449
1394
  ```ruby
1450
1395
  params do
@@ -1462,9 +1407,7 @@ end
1462
1407
 
1463
1408
  ### Dependent Parameters
1464
1409
 
1465
- Suppose some of your parameters are only relevant if another parameter is given;
1466
- Grape allows you to express this relationship through the `given` method in your
1467
- parameters block, like so:
1410
+ Suppose some of your parameters are only relevant if another parameter is given; Grape allows you to express this relationship through the `given` method in your parameters block, like so:
1468
1411
 
1469
1412
  ```ruby
1470
1413
  params do
@@ -1503,31 +1446,45 @@ Note: param in `given` should be the renamed one. In the example, it should be `
1503
1446
 
1504
1447
  ### Group Options
1505
1448
 
1506
- Parameters options can be grouped. It can be useful if you want to extract
1507
- common validation or types for several parameters. The example below presents a
1508
- typical case when parameters share common options.
1449
+ Parameters options can be grouped. It can be useful if you want to extract common validation or types for several parameters.
1450
+ Within these groups, individual parameters can extend or selectively override the common settings, allowing you to maintain the defaults at the group level while still applying parameter-specific rules where necessary.
1451
+
1452
+ The example below presents a typical case when parameters share common options.
1509
1453
 
1510
1454
  ```ruby
1511
1455
  params do
1512
- requires :first_name, type: String, regexp: /w+/, desc: 'First name'
1513
- requires :middle_name, type: String, regexp: /w+/, desc: 'Middle name'
1514
- requires :last_name, type: String, regexp: /w+/, desc: 'Last name'
1456
+ requires :first_name, type: String, regexp: /w+/, desc: 'First name', documentation: { in: 'body' }
1457
+ optional :middle_name, type: String, regexp: /w+/, desc: 'Middle name', documentation: { in: 'body', x: { nullable: true } }
1458
+ requires :last_name, type: String, regexp: /w+/, desc: 'Last name', documentation: { in: 'body' }
1515
1459
  end
1516
1460
  ```
1517
1461
 
1518
- Grape allows you to present the same logic through the `with` method in your
1519
- parameters block, like so:
1462
+ Grape allows you to present the same logic through the `with` method in your parameters block, like so:
1520
1463
 
1521
1464
  ```ruby
1522
1465
  params do
1523
- with(type: String, regexp: /w+/) do
1466
+ with(type: String, regexp: /w+/, documentation: { in: 'body' }) do
1524
1467
  requires :first_name, desc: 'First name'
1525
- requires :middle_name, desc: 'Middle name'
1468
+ optional :middle_name, desc: 'Middle name', documentation: { x: { nullable: true } }
1526
1469
  requires :last_name, desc: 'Last name'
1527
1470
  end
1528
1471
  end
1529
1472
  ```
1530
1473
 
1474
+ You can organize settings into layers using nested `with` blocks. Each layer can use, add to, or change the settings of the layer above it. This helps to keep complex parameters organized and consistent, while still allowing for specific customizations to be made.
1475
+
1476
+ ```ruby
1477
+ params do
1478
+ with(documentation: { in: 'body' }) do # Applies documentation to all nested parameters
1479
+ with(type: String, regexp: /\w+/) do # Applies type and validation to names
1480
+ requires :first_name, desc: 'First name'
1481
+ requires :last_name, desc: 'Last name'
1482
+ end
1483
+ optional :age, type: Integer, desc: 'Age', documentation: { x: { nullable: true } } # Specific settings for 'age'
1484
+ end
1485
+ end
1486
+ ```
1487
+
1531
1488
  ### Renaming
1532
1489
 
1533
1490
  You can rename parameters using `as`, which can be useful when refactoring existing APIs:
@@ -1550,13 +1507,9 @@ The value passed to `as` will be the key when calling `declared(params)`.
1550
1507
 
1551
1508
  #### `allow_blank`
1552
1509
 
1553
- Parameters can be defined as `allow_blank`, ensuring that they contain a value. By default, `requires`
1554
- only validates that a parameter was sent in the request, regardless its value. With `allow_blank: false`,
1555
- empty values or whitespace only values are invalid.
1510
+ Parameters can be defined as `allow_blank`, ensuring that they contain a value. By default, `requires` only validates that a parameter was sent in the request, regardless its value. With `allow_blank: false`, empty values or whitespace only values are invalid.
1556
1511
 
1557
- `allow_blank` can be combined with both `requires` and `optional`. If the parameter is required, it has to contain
1558
- a value. If it's optional, it's possible to not send it in the request, but if it's being sent, it has to have
1559
- some value, and not an empty string/only whitespaces.
1512
+ `allow_blank` can be combined with both `requires` and `optional`. If the parameter is required, it has to contain a value. If it's optional, it's possible to not send it in the request, but if it's being sent, it has to have some value, and not an empty string/only whitespaces.
1560
1513
 
1561
1514
 
1562
1515
  ```ruby
@@ -1593,7 +1546,7 @@ Note endless ranges are also supported with ActiveSupport >= 6.0, but they requi
1593
1546
  ```ruby
1594
1547
  params do
1595
1548
  requires :minimum, type: Integer, values: 10..
1596
- optional :maximum, type: Integer, values: ..10
1549
+ optional :maximum, type: Integer, values: ..10
1597
1550
  end
1598
1551
  ```
1599
1552
 
@@ -1606,12 +1559,31 @@ params do
1606
1559
  end
1607
1560
  ```
1608
1561
 
1562
+ An exclusive upper bound is expressed with the standard Ruby `...` range (three dots). Ruby has no range syntax for an exclusive *lower* bound; an arity-one `:values` Proc predicate (see below) is the recommended way to express one, though excluding a specific endpoint (e.g. zero) via `:except_values` also works:
1563
+
1564
+ ```ruby
1565
+ params do
1566
+ requires :score, type: Float, values: 0.0...5.0 # 0.0 <= score < 5.0
1567
+ requires :positive, type: Float, values: ->(v) { v > 0.0 } # amount > 0.0 (recommended)
1568
+ requires :other, type: Float, values: 0.0.., except_values: [0.0] # amount > 0.0 (alternative)
1569
+ end
1570
+ ```
1571
+
1572
+ Combined with `:type`, ranges (open or closed) cover common numeric bound checks without a dedicated validator:
1573
+
1574
+ ```ruby
1575
+ params do
1576
+ requires :quantity, type: Integer, values: 1.. # must be a positive integer
1577
+ requires :discount, type: Float, values: 0.0..100.0 # must be between 0 and 100
1578
+ requires :rating, type: Integer, values: 5..5 # must be exactly 5
1579
+ requires :numbers, type: [Integer], values: 1.. # every element must be positive
1580
+ end
1581
+ ```
1582
+
1609
1583
  The `:values` option can also be supplied with a `Proc`, evaluated lazily with each request.
1610
- If the Proc has arity zero (i.e. it takes no arguments) it is expected to return either a list
1611
- or a range which will then be used to validate the parameter.
1584
+ If the Proc has arity zero (i.e. it takes no arguments) it is expected to return either a list or a range which will then be used to validate the parameter.
1612
1585
 
1613
- For example, given a status model you may want to restrict by hashtags that you have
1614
- previously defined in the `HashTag` model.
1586
+ For example, given a status model you may want to restrict by hashtags that you have previously defined in the `HashTag` model.
1615
1587
 
1616
1588
  ```ruby
1617
1589
  params do
@@ -1619,10 +1591,7 @@ params do
1619
1591
  end
1620
1592
  ```
1621
1593
 
1622
- Alternatively, a Proc with arity one (i.e. taking one argument) can be used to explicitly validate
1623
- each parameter value. In that case, the Proc is expected to return a truthy value if the parameter
1624
- value is valid. The parameter will be considered invalid if the Proc returns a falsy value or if it
1625
- raises a StandardError.
1594
+ Alternatively, a Proc with arity one (i.e. taking one argument) can be used to explicitly validate each parameter value. In that case, the Proc is expected to return a truthy value if the parameter value is valid. The parameter will be considered invalid if the Proc returns a falsy value or if it raises a StandardError.
1626
1595
 
1627
1596
  ```ruby
1628
1597
  params do
@@ -1632,26 +1601,27 @@ end
1632
1601
 
1633
1602
  While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
1634
1603
 
1635
- Note that [allow_blank](#allow_blank) validator applies while using `:values`. In the following example the absence of `:allow_blank` does not prevent `:state` from receiving blank values because `:allow_blank` defaults to `true`.
1604
+ When using `optional` together with `:values`, a missing key, a `nil` value, and any value that coerces to `nil` (such as `""` for `type: Symbol`) all pass validation — `optional` collapses "key may be absent" with "value may be nil". To reject blank values while still allowing the key to be absent, add `allow_blank: false`:
1636
1605
 
1637
1606
  ```ruby
1638
1607
  params do
1639
- requires :state, type: Symbol, values: [:active, :inactive]
1608
+ optional :state, type: Symbol, values: [:active, :inactive], allow_blank: false
1640
1609
  end
1641
1610
  ```
1642
1611
 
1612
+ With `requires`, blank values are already rejected: `requires` enforces presence and `:values` rejects `nil`.
1613
+
1643
1614
  #### `except_values`
1644
1615
 
1645
1616
  Parameters can be restricted from having a specific set of values with the `:except_values` option.
1646
1617
 
1647
- The `except_values` validator behaves similarly to the `values` validator in that it accepts either
1648
- an Array, a Range, or a Proc. Unlike the `values` validator, however, `except_values` only accepts
1649
- Procs with arity zero.
1618
+ The `except_values` validator behaves similarly to the `values` validator in that it accepts either an Array, a Range (including open/beginless/endless ranges), or a Proc. Unlike the `values` validator, however, `except_values` only accepts Procs with arity zero.
1650
1619
 
1651
1620
  ```ruby
1652
1621
  params do
1653
1622
  requires :browser, except_values: [ 'ie6', 'ie7', 'ie8' ]
1654
1623
  requires :port, except_values: { value: 0..1024, message: 'is not allowed' }
1624
+ requires :negative, type: Integer, except_values: ..-1
1655
1625
  requires :hashtag, except_values: -> { Hashtag.FORBIDDEN_LIST }
1656
1626
  end
1657
1627
  ```
@@ -1667,11 +1637,24 @@ params do
1667
1637
  end
1668
1638
  ```
1669
1639
 
1640
+ #### `length`
1641
+
1642
+ Parameters with types that support `#length` method can be restricted to have a specific length with the `:length` option.
1643
+
1644
+ The validator accepts `:min` or `:max` or both options or only `:is` to validate that the value of the parameter is within the given limits.
1645
+
1646
+ ```ruby
1647
+ params do
1648
+ requires :code, type: String, length: { is: 2 }
1649
+ requires :str, type: String, length: { min: 3 }
1650
+ requires :list, type: [Integer], length: { min: 3, max: 5 }
1651
+ requires :hash, type: Hash, length: { max: 5 }
1652
+ end
1653
+ ```
1654
+
1670
1655
  #### `regexp`
1671
1656
 
1672
- Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
1673
- does not match the regular expression an error will be returned. Note that this is true for both `requires`
1674
- and `optional` parameters.
1657
+ Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value does not match the regular expression an error will be returned. Note that this is true for both `requires` and `optional` parameters.
1675
1658
 
1676
1659
  ```ruby
1677
1660
  params do
@@ -1806,8 +1789,7 @@ namespace :statuses do
1806
1789
  end
1807
1790
  ```
1808
1791
 
1809
- The `namespace` method has a number of aliases, including: `group`, `resource`,
1810
- `resources`, and `segment`. Use whichever reads the best for your API.
1792
+ The `namespace` method has a number of aliases, including: `group`, `resource`, `resources`, and `segment`. Use whichever reads the best for your API.
1811
1793
 
1812
1794
  You can conveniently define a route parameter as a namespace using `route_param`.
1813
1795
 
@@ -1844,9 +1826,9 @@ end
1844
1826
  ```ruby
1845
1827
  class AlphaNumeric < Grape::Validations::Validators::Base
1846
1828
  def validate_param!(attr_name, params)
1847
- unless params[attr_name] =~ /\A[[:alnum:]]+\z/
1848
- raise Grape::Exceptions::Validation.new params: [@scope.full_name(attr_name)], message: 'must consist of alpha-numeric characters'
1849
- end
1829
+ return if params[attr_name].match?(/\A[[:alnum:]]+\z/)
1830
+
1831
+ validation_error!(attr_name, 'must consist of alpha-numeric characters')
1850
1832
  end
1851
1833
  end
1852
1834
  ```
@@ -1862,9 +1844,9 @@ You can also create custom classes that take parameters.
1862
1844
  ```ruby
1863
1845
  class Length < Grape::Validations::Validators::Base
1864
1846
  def validate_param!(attr_name, params)
1865
- unless params[attr_name].length <= @option
1866
- raise Grape::Exceptions::Validation.new params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
1867
- end
1847
+ return if params[attr_name].length <= @options
1848
+
1849
+ validation_error!(attr_name, "must be at the most #{@options} characters long")
1868
1850
  end
1869
1851
  end
1870
1852
  ```
@@ -1886,10 +1868,10 @@ class Admin < Grape::Validations::Validators::Base
1886
1868
  # @attrs being [:admin_field] and once with @attrs being [:admin_false_field]
1887
1869
  return unless request.params.key?(@attrs.first)
1888
1870
  # check if admin flag is set to true
1889
- return unless @option
1871
+ return unless @options
1890
1872
  # check if user is admin or not
1891
1873
  # as an example get a token from request and check if it's admin or not
1892
- raise Grape::Exceptions::Validation.new params: @attrs, message: 'Can not set admin-only field.' unless request.headers['X-Access-Token'] == 'admin'
1874
+ validation_error!(@attrs, 'Can not set admin-only field.') unless request.headers['X-Access-Token'] == 'admin'
1893
1875
  end
1894
1876
  end
1895
1877
  ```
@@ -1904,7 +1886,50 @@ params do
1904
1886
  end
1905
1887
  ```
1906
1888
 
1907
- Every validation will have its own instance of the validator, which means that the validator can have a state.
1889
+ Each validator is instantiated once at route definition time and frozen. Any setup (option parsing, message building) should happen in `initialize`, not in `validate_param!` or `validate`.
1890
+
1891
+ #### Available helpers
1892
+
1893
+ The following protected/private helpers are available in any `Grape::Validations::Validators::Base` subclass:
1894
+
1895
+ | Helper | Description |
1896
+ |---|---|
1897
+ | `default_message_key(key)` | Class-level macro. Declares the default I18n key for `validation_error!`. A per-option `:message` override still takes precedence. |
1898
+ | `validation_error!(attr_name_or_params, message = @exception_message)` | Raises `Grape::Exceptions::Validation`. Accepts a single attribute name or a pre-computed array of full param names. |
1899
+ | `@options` | The validator option value, deep-frozen at initialization. |
1900
+ | `@attrs` | Frozen array of attribute names this validator applies to. |
1901
+ | `@scope` | The `ParamsScope` — use `@scope.full_name(attr_name)` for the fully-qualified param name. |
1902
+ | `option_value` | Returns `@options[:value]` if present, otherwise `@options`. |
1903
+ | `options_key?(key)` | Returns true if `@options` is a hash with a non-nil `key`. |
1904
+ | `hash_like?(obj)` | Returns true if `obj` responds to `key?`. |
1905
+ | `scrub(value)` | Returns `value` with invalid byte sequences scrubbed. |
1906
+ | `translate(key, **opts)` | I18n lookup with `:en` fallback and `grape.errors.messages` scope. Called at request time to respect per-request locale. |
1907
+
1908
+ Use `default_message_key` for a fixed I18n key. The message is resolved once at route definition time via `message`, so a per-option `:message` override still wins:
1909
+
1910
+ ```ruby
1911
+ class SpecialValidator < Grape::Validations::Validators::Base
1912
+ default_message_key :special
1913
+
1914
+ def validate_param!(attr_name, params)
1915
+ return if valid?(params[attr_name])
1916
+
1917
+ validation_error!(attr_name)
1918
+ end
1919
+ end
1920
+ ```
1921
+
1922
+ For interpolated messages that must respect per-request locale, call `translate` directly inside `validate_param!`:
1923
+
1924
+ ```ruby
1925
+ class SpecialValidator < Grape::Validations::Validators::Base
1926
+ def validate_param!(attr_name, params)
1927
+ return if valid?(params[attr_name])
1928
+
1929
+ validation_error!(attr_name, translate(:special, min: 2, max: 10))
1930
+ end
1931
+ end
1932
+ ```
1908
1933
 
1909
1934
  ### Validation Errors
1910
1935
 
@@ -1947,8 +1972,8 @@ To skip all subsequent validation checks when a specific param is found invalid,
1947
1972
  The following example will not check if `:wine` is present unless it finds `:beer`.
1948
1973
  ```ruby
1949
1974
  params do
1950
- required :beer, fail_fast: true
1951
- required :wine
1975
+ requires :beer, fail_fast: true
1976
+ requires :wine
1952
1977
  end
1953
1978
  ```
1954
1979
  The result of empty params would be a single `Grape::Exceptions::ValidationErrors` error.
@@ -1956,17 +1981,38 @@ The result of empty params would be a single `Grape::Exceptions::ValidationError
1956
1981
  Similarly, no regular expression test will be performed if `:blah` is blank in the following example.
1957
1982
  ```ruby
1958
1983
  params do
1959
- required :blah, allow_blank: false, regexp: /blah/, fail_fast: true
1984
+ requires :blah, allow_blank: false, regexp: /blah/, fail_fast: true
1960
1985
  end
1961
1986
  ```
1962
1987
 
1963
1988
  ### I18n
1964
1989
 
1965
- Grape supports I18n for parameter-related error messages, but will fallback to English if
1966
- translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
1990
+ Grape supports I18n for parameter-related error messages, but will fallback to English if translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
1967
1991
 
1968
1992
  In case your app enforces available locales only and :en is not included in your available locales, Grape cannot fall back to English and will return the translation key for the error message. To avoid this behaviour, either provide a translation for your default locale or add :en to your available locales.
1969
1993
 
1994
+ Custom validators that inherit from `Grape::Validations::Validators::Base` have access to a `translate` helper (see `Grape::Util::Translation`) and should use it instead of calling `I18n` directly. It applies the same `:en` fallback as built-in validators, defaults `scope` to `'grape.errors.messages'`, and handles interpolation without needing `format`:
1995
+
1996
+ ```ruby
1997
+ # Good — scope defaults to 'grape.errors.messages', interpolation forwarded automatically
1998
+ translate(:special, min: 2, max: 10)
1999
+
2000
+ # Bad — format is unnecessary and risks conflicting with I18n reserved keys
2001
+ format I18n.t(:special, scope: 'grape.errors.messages'), min: 2, max: 10
2002
+ ```
2003
+
2004
+ Example custom validator using an interpolated i18n message:
2005
+
2006
+ ```ruby
2007
+ class SpecialValidator < Grape::Validations::Validators::Base
2008
+ def validate_param!(attr_name, params)
2009
+ return if valid?(params[attr_name])
2010
+
2011
+ validation_error!(attr_name, translate(:special, min: 2, max: 10))
2012
+ end
2013
+ end
2014
+ ```
2015
+
1970
2016
  ### Custom Validation messages
1971
2017
 
1972
2018
  Grape supports custom validation messages for parameter-related and coerce-related error messages.
@@ -1988,6 +2034,16 @@ params do
1988
2034
  end
1989
2035
  ```
1990
2036
 
2037
+ #### `length`
2038
+
2039
+ ```ruby
2040
+ params do
2041
+ requires :code, type: String, length: { is: 2, message: 'code is expected to be exactly 2 characters long' }
2042
+ requires :str, type: String, length: { min: 5, message: 'str is expected to be at least 5 characters long' }
2043
+ requires :list, type: [Integer], length: { min: 2, max: 3, message: 'list is expected to have between 2 and 3 elements' }
2044
+ end
2045
+ ```
2046
+
1991
2047
  #### `all_or_none_of`
1992
2048
 
1993
2049
  ```ruby
@@ -2096,6 +2152,40 @@ params do
2096
2152
  end
2097
2153
  ```
2098
2154
 
2155
+ ### Using `dry-validation` or `dry-schema`
2156
+
2157
+ As an alternative to the `params` DSL described above, you can use a schema or `dry-validation` contract to describe an endpoint's parameters. This can be especially useful if you use the above already in some other parts of your application. If not, you'll need to add `dry-validation` or `dry-schema` to your `Gemfile`.
2158
+
2159
+ Then call `contract` with a contract or schema defined previously:
2160
+
2161
+ ```rb
2162
+ CreateOrdersSchema = Dry::Schema.Params do
2163
+ required(:orders).array(:hash) do
2164
+ required(:name).filled(:string)
2165
+ optional(:volume).maybe(:integer, lt?: 9)
2166
+ end
2167
+ end
2168
+
2169
+ # ...
2170
+
2171
+ contract CreateOrdersSchema
2172
+ ```
2173
+
2174
+ or with a block, using the [schema definition syntax](https://dry-rb.org/gems/dry-schema/1.13/#quick-start):
2175
+
2176
+ ```rb
2177
+ contract do
2178
+ required(:orders).array(:hash) do
2179
+ required(:name).filled(:string)
2180
+ optional(:volume).maybe(:integer, lt?: 9)
2181
+ end
2182
+ end
2183
+ ```
2184
+
2185
+ The latter will define a coercing schema (`Dry::Schema.Params`). When using the former approach, it's up to you to decide whether the input will need coercing.
2186
+
2187
+ The `params` and `contract` declarations can also be used together in the same API, e.g. to describe different parts of a nested namespace for an endpoint.
2188
+
2099
2189
  ## Headers
2100
2190
 
2101
2191
  ### Request
@@ -2123,8 +2213,9 @@ curl -H "secret_PassWord: swordfish" ...
2123
2213
 
2124
2214
  The header name will have been normalized for you.
2125
2215
 
2126
- - In the `header` helper names will be coerced into a capitalized kebab case.
2127
- - In the `env` collection they appear in all uppercase, in snake case, and prefixed with 'HTTP_'.
2216
+ - In the `header` helper names will be coerced into a downcased kebab case as `secret-password` if using Rack 3.
2217
+ - In the `header` helper names will be coerced into a capitalized kebab case as `Secret-PassWord` if using Rack < 3.
2218
+ - In the `env` collection they appear in all uppercase, in snake case, and prefixed with 'HTTP_' as `HTTP_SECRET_PASSWORD`
2128
2219
 
2129
2220
  The header name will have been normalized per HTTP standards defined in [RFC2616 Section 4.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2) regardless of what is being sent by a client.
2130
2221
 
@@ -2194,8 +2285,7 @@ namespace ':id' do
2194
2285
  end
2195
2286
  ```
2196
2287
 
2197
- Optionally, you can define requirements for your named route parameters using regular
2198
- expressions on namespace or endpoint. The route will match only if all requirements are met.
2288
+ Optionally, you can define requirements for your named route parameters using regular expressions on namespace or endpoint. The route will match only if all requirements are met.
2199
2289
 
2200
2290
  ```ruby
2201
2291
  get ':id', requirements: { id: /[0-9]*/ } do
@@ -2211,10 +2301,81 @@ namespace :outer, requirements: { id: /[0-9]*/ } do
2211
2301
  end
2212
2302
  ```
2213
2303
 
2304
+ A requirement can also name one of the capture types of [Mustermann](https://github.com/sinatra/mustermann), the pattern library Grape routes with, instead of a regular expression. Besides constraining the match, a capture type that carries a converter hands the endpoint the converted value rather than a String.
2305
+
2306
+ ```ruby
2307
+ get ':id', requirements: { id: Integer } do
2308
+ params[:id] # => 23, an Integer, for GET /23 — GET /michael is a 404
2309
+ end
2310
+
2311
+ get 'events/:on', requirements: { on: :date } do
2312
+ params[:on] # => #<Date: 2020-01-02>, for GET /events/2020-01-02
2313
+ end
2314
+ ```
2315
+
2316
+ `Integer`, `Float`, `Symbol`, `Date` and `Gem::Version` convert, as do their symbol spellings `:integer`, `:float`, `:symbol`, `:date` and `:version`. `:uuid`, `:slug` and `:locale` constrain the match without converting. A name Mustermann has no capture type for raises when the API is defined.
2317
+
2318
+ A `params` block still owns what the endpoint sees, since validation runs after the router: declaring `requires :id, type: String` alongside `requirements: { id: Integer }` hands the endpoint `"23"`.
2319
+
2320
+ `route_param` names the parameter itself, so it takes the constraint on its own — `route_param :id, requirements: Integer` — and refuses a Hash, which would name the parameter a second time. A namespace or an endpoint can carry several captures, so requirements there are the Hash form keyed by param name.
2321
+
2322
+ `route_param :id` defines the parameter on its own — `params[:id]` reaches the endpoint, and the route documents it as a path capture. `type:` and `requirements:` are two ways of typing it, and they work at different layers. `type: Integer` declares `requires :id, type: Integer`: the value is coerced, appears in `declared(params)` and carries its type into the route's documented params, and a value the path let through but the type rejects is a 400. `requirements: Integer` types the route instead: a segment that does not match is not this route at all, so it is a 404, and the value arrives already converted by the router, undeclared and documented only as a capture. A declared `type: Integer` also narrows the path, but to digits alone, so `/-7` is a 404 there while the requirement matches it. Given both, the requirement decides what the route matches and the declared type decides what the endpoint sees.
2323
+
2324
+ #### Dots in a path segment
2325
+
2326
+ A path capture matches `[^/?#.]+`: a dot introduces the format suffix, so a captured segment never contains one. A dotted value therefore either fails to route or arrives truncated, and the declared type makes no difference — `type: String` and `type: Float` compile the same pattern as an untyped parameter, only `type: Integer` narrows it to digits.
2327
+
2328
+ ```ruby
2329
+ class WithFormat < Grape::API
2330
+ format :json
2331
+ route_param :name do
2332
+ get { params[:name] } # GET /report.pdf => 404, the segment cannot hold the dot
2333
+ end
2334
+ end
2335
+
2336
+ class WithoutFormat < Grape::API
2337
+ route_param :name do
2338
+ # GET /report.pdf => 200, params[:name] is "report" and params[:format] is "pdf"
2339
+ get { params[:name] }
2340
+ end
2341
+ end
2342
+ ```
2343
+
2344
+ The second one is the one to watch: the extension is taken as the format, so the endpoint is handed a truncated value and the client still gets a 200.
2345
+
2346
+ Percent-encoding the dot routes it through — `GET /report%2Epdf` hands the endpoint `"report.pdf"` — and so does a requirement that admits dots:
2347
+
2348
+ ```ruby
2349
+ route_param :name, requirements: /.+/ do
2350
+ get { params[:name] } # => "report.pdf" for GET /report.pdf, "1.2.3" for GET /1.2.3
2351
+ end
2352
+ ```
2353
+
2354
+ Such a regexp claims the extension as well, so `GET /a.b.json` hands the endpoint `"a.b.json"` and format-by-extension no longer applies to that route. A narrower constraint leaves the suffix alone: `get ':n', requirements: { n: Float }` matches `/4.2` and still reads `.json` as the format.
2355
+
2356
+ ### The QUERY Method
2357
+
2358
+ Grape supports [`QUERY`](https://www.rfc-editor.org/info/rfc10008/), a safe and idempotent method that carries its query in the request content rather than in the URI. It is the method to reach for when a query is too large, too structured, or too sensitive to encode into a query string.
2359
+
2360
+ ```ruby
2361
+ params do
2362
+ requires :q, type: String
2363
+ optional :limit, type: Integer
2364
+ end
2365
+ query '/search' do
2366
+ Status.search(params[:q], limit: params[:limit])
2367
+ end
2368
+ ```
2369
+
2370
+ ```
2371
+ curl -X QUERY 'http://localhost:9292/search' -H Content-Type:application/json -d '{"q": "grape", "limit": 10}'
2372
+ ```
2373
+
2374
+ The content is parsed into `params` exactly as it is for `POST`, and a successful response defaults to `200`, not `201`. Because the content *is* the query, a `QUERY` request that arrives without a `Content-Type` is rejected with `400` instead of falling back to the API's default format.
2375
+
2214
2376
  ## Helpers
2215
2377
 
2216
- You can define helper methods that your endpoints can use with the `helpers`
2217
- macro by either giving a block or an array of modules.
2378
+ You can define helper methods that your endpoints can use with the `helpers` macro by either giving a block or an array of modules.
2218
2379
 
2219
2380
  ```ruby
2220
2381
  module StatusHelpers
@@ -2453,11 +2614,36 @@ end
2453
2614
  API.recognize_path '/statuses'
2454
2615
  ```
2455
2616
 
2617
+ Since version `2.1.0`, the `recognize_path` method takes into account the parameters type to determine which endpoint should match with given path.
2618
+
2619
+ ```ruby
2620
+ class Books < Grape::API
2621
+ resource :books do
2622
+ route_param :id, type: Integer do
2623
+ # GET /books/:id
2624
+ get do
2625
+ #...
2626
+ end
2627
+ end
2628
+
2629
+ resource :share do
2630
+ # POST /books/share
2631
+ post do
2632
+ # ....
2633
+ end
2634
+ end
2635
+ end
2636
+ end
2637
+
2638
+ API.recognize_path '/books/1' # => /books/:id
2639
+ API.recognize_path '/books/share' # => /books/share
2640
+ API.recognize_path '/books/other' # => nil
2641
+ ```
2642
+
2643
+
2456
2644
  ## Allowed Methods
2457
2645
 
2458
- When you add a `GET` route for a resource, a route for the `HEAD`
2459
- method will also be added automatically. You can disable this
2460
- behavior with `do_not_route_head!`.
2646
+ When you add a `GET` route for a resource, a route for the `HEAD` method will also be added automatically. You can disable this behavior with `do_not_route_head!`.
2461
2647
 
2462
2648
  ``` ruby
2463
2649
  class API < Grape::API
@@ -2469,11 +2655,7 @@ class API < Grape::API
2469
2655
  end
2470
2656
  ```
2471
2657
 
2472
- When you add a route for a resource, a route for the `OPTIONS`
2473
- method will also be added. The response to an OPTIONS request will
2474
- include an "Allow" header listing the supported methods. If the resource
2475
- has `before` and `after` callbacks they will be executed, but no other callbacks will
2476
- run.
2658
+ When you add a route for a resource, a route for the `OPTIONS` method will also be added. The response to an OPTIONS request will include an "Allow" header listing the supported methods. If the resource has `before` and `after` callbacks they will be executed, but no other callbacks will run.
2477
2659
 
2478
2660
  ```ruby
2479
2661
  class API < Grape::API
@@ -2502,10 +2684,7 @@ curl -v -X OPTIONS http://localhost:3000/rt_count
2502
2684
 
2503
2685
  You can disable this behavior with `do_not_route_options!`.
2504
2686
 
2505
- If a request for a resource is made with an unsupported HTTP method, an
2506
- HTTP 405 (Method Not Allowed) response will be returned. If the resource
2507
- has `before` callbacks they will be executed, but no other callbacks will
2508
- run.
2687
+ If a request for a resource is made with an unsupported HTTP method, an HTTP 405 (Method Not Allowed) response will be returned. If the resource has `before` callbacks they will be executed, but no other callbacks will run.
2509
2688
 
2510
2689
  ``` shell
2511
2690
  curl -X DELETE -v http://localhost:3000/rt_count/
@@ -2531,8 +2710,7 @@ Anything that responds to `#to_s` can be given as a first argument to `error!`.
2531
2710
  error! :not_found, 404
2532
2711
  ```
2533
2712
 
2534
- You can also return JSON formatted objects by raising error! and passing a hash
2535
- instead of a message.
2713
+ You can also return JSON formatted objects by raising error! and passing a hash instead of a message.
2536
2714
 
2537
2715
  ```ruby
2538
2716
  error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
@@ -2544,7 +2722,7 @@ You can set additional headers for the response. They will be merged with header
2544
2722
  error!('Something went wrong', 500, 'X-Error-Detail' => 'Invalid token.')
2545
2723
  ```
2546
2724
 
2547
- You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
2725
+ You can present documented errors with a Grape entity using the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
2548
2726
 
2549
2727
  ```ruby
2550
2728
  module API
@@ -2597,8 +2775,7 @@ route :any, '*path' do
2597
2775
  end
2598
2776
  ```
2599
2777
 
2600
- It is very crucial to __define this endpoint at the very end of your API__, as it
2601
- literally accepts every request.
2778
+ It is very crucial to __define this endpoint at the very end of your API__, as it literally accepts every request.
2602
2779
 
2603
2780
  ## Exception Handling
2604
2781
 
@@ -2632,6 +2809,19 @@ rescue_from :grape_exceptions do |e|
2632
2809
  end
2633
2810
  ```
2634
2811
 
2812
+ The opt-in takes precedence over a catch-all handler, whether that catch-all is written as `rescue_from :all` or as a class such as `rescue_from StandardError`. So a validation failure stays a `400` rather than becoming whatever the catch-all returns:
2813
+
2814
+ ```ruby
2815
+ class Twitter::API < Grape::API
2816
+ rescue_from StandardError do
2817
+ error!('server error', 500)
2818
+ end
2819
+ rescue_from :grape_exceptions # validation errors still answer 400
2820
+ end
2821
+ ```
2822
+
2823
+ A handler registered for a specific Grape exception class is more precise than the opt-in and still wins, so `rescue_from Grape::Exceptions::ValidationErrors` keeps its own handler. The opt-in only outranks handlers that matched through a non-Grape ancestor.
2824
+
2635
2825
  You can also rescue specific exceptions.
2636
2826
 
2637
2827
  ```ruby
@@ -2642,6 +2832,29 @@ end
2642
2832
 
2643
2833
  In this case ```UserDefinedError``` must be inherited from ```StandardError```.
2644
2834
 
2835
+ When several classes could match, the one registered **first** in a scope wins — as with the clauses of a Ruby `rescue`. Register the more specific class before the broader one, or the narrower handler never runs:
2836
+
2837
+ ```ruby
2838
+ class Twitter::API < Grape::API
2839
+ rescue_from ArgumentError do ... end # matched first
2840
+ rescue_from StandardError do ... end # everything else
2841
+ end
2842
+ ```
2843
+
2844
+ Grape warns when a `rescue_from` is registered for a class an earlier one in the same scope already covers. This is about ordering within a scope; a handler in a nested namespace or a mounted API always takes precedence over one inherited from an enclosing scope, whatever the classes are.
2845
+
2846
+ A `rescue_from` also has to be declared **above** the routes it should cover. A route captures the handlers registered before it, so one declared below a route never runs for it:
2847
+
2848
+ ```ruby
2849
+ class Twitter::API < Grape::API
2850
+ rescue_from :all do ... end # covers GET /statuses
2851
+ get :statuses do ... end
2852
+ rescue_from ArgumentError do ... end # never runs for GET /statuses
2853
+ end
2854
+ ```
2855
+
2856
+ The same goes for `use`, `helpers` and the `before`/`after` callbacks.
2857
+
2645
2858
  Notice that you could combine these two approaches (rescuing custom errors takes precedence). For example, it's useful for handling all exceptions except Grape validation errors.
2646
2859
 
2647
2860
  ```ruby
@@ -2656,12 +2869,12 @@ end
2656
2869
 
2657
2870
  The error format will match the request format. See "Content-Types" below.
2658
2871
 
2659
- Custom error formatters for existing and additional types can be defined with a proc.
2872
+ Custom error formatters for existing and additional types can be defined with a proc. The formatter receives a `Grape::Exceptions::ErrorResponse` value object as `error:` plus three context kwargs — `env:`, `include_backtrace:`, `include_original_exception:`. Pull just the keys you need with `**` to ignore the rest:
2660
2873
 
2661
2874
  ```ruby
2662
2875
  class Twitter::API < Grape::API
2663
- error_formatter :txt, ->(message, backtrace, options, env, original_exception) {
2664
- "error: #{message} from #{backtrace}"
2876
+ error_formatter :txt, ->(error:, **) {
2877
+ "error #{error.status}: #{error.message} from #{error.backtrace}"
2665
2878
  }
2666
2879
  end
2667
2880
  ```
@@ -2670,8 +2883,8 @@ You can also use a module or class.
2670
2883
 
2671
2884
  ```ruby
2672
2885
  module CustomFormatter
2673
- def self.call(message, backtrace, options, env, original_exception)
2674
- { message: message, backtrace: backtrace }
2886
+ def self.call(error:, **)
2887
+ { status: error.status, message: error.message, backtrace: error.backtrace }
2675
2888
  end
2676
2889
  end
2677
2890
 
@@ -2701,6 +2914,43 @@ class Twitter::API < Grape::API
2701
2914
  end
2702
2915
  ```
2703
2916
 
2917
+ #### Re-raising from inside a `rescue_from` block
2918
+
2919
+ A `rescue_from` block can re-raise an exception to invoke a different handler. This is useful for translating one exception class into another:
2920
+
2921
+ ```ruby
2922
+ class Twitter::API < Grape::API
2923
+ rescue_from Grape::Exceptions::ValidationErrors do |e|
2924
+ raise Api::Exceptions::InvalidValueError, e.full_messages
2925
+ end
2926
+
2927
+ rescue_from Api::Exceptions::InvalidValueError do |e|
2928
+ error!({ errors: e.message }, 422)
2929
+ end
2930
+ end
2931
+ ```
2932
+
2933
+ The first handler re-raises; the second handler runs against the new exception.
2934
+
2935
+ If the re-raised exception has no registered `rescue_from` and is a `Grape::Exceptions::Base` subclass, it is rendered through the default Grape error path (using its own `status` and `message`). Anything else — typos, `NoMethodError`, an unrelated `StandardError` — is treated as an internal error: it is exposed on the rack env for upstream Rack middleware to observe, and rendered to the API consumer as a generic `500 Internal Server Error`. This avoids leaking internal detail in the response body.
2936
+
2937
+ Because the exception is answered rather than raised, nothing above Grape catches it, so it is published under `rack.exception` — the key error trackers read for a handled exception — as well as Grape's own `grape.exception`. Grape does no logging of its own here; register a `rescue_from :internal_grape_exceptions` handler to take that over.
2938
+
2939
+ You can take control of the internal-error path by opting in with `rescue_from :internal_grape_exceptions`:
2940
+
2941
+ ```ruby
2942
+ class Twitter::API < Grape::API
2943
+ rescue_from :internal_grape_exceptions do |e|
2944
+ Sentry.capture_exception(e)
2945
+ error!({ message: 'Something went wrong' }, 500)
2946
+ end
2947
+ end
2948
+ ```
2949
+
2950
+ When this handler is registered the framework hands the original exception to you, and you own the response shape and any logging. The framework deliberately does not log internal errors itself — it has no way to know your preferred format or destination.
2951
+
2952
+ A second raise inside the redispatched handler is not redispatched again — it goes straight to the framework's generic 500. This bounds the chain at one redispatch and prevents loops.
2953
+
2704
2954
  You can also rescue all exceptions with a code block and handle the Rack response at the lowest level.
2705
2955
 
2706
2956
  ```ruby
@@ -2840,33 +3090,43 @@ Any exception that is not subclass of `StandardError` should be rescued explicit
2840
3090
  Usually it is not a case for an application logic as such errors point to problems in Ruby runtime.
2841
3091
  This is following [standard recommendations for exceptions handling](https://ruby-doc.org/core/Exception.html).
2842
3092
 
2843
- ### Rails 3.x
3093
+ #### When the error response itself cannot be rendered
2844
3094
 
2845
- When mounted inside containers, such as Rails 3.x, errors such as "404 Not Found" or
2846
- "406 Not Acceptable" will likely be handled and rendered by Rails handlers. For instance,
2847
- accessing a nonexistent route "/api/foo" raises a 404, which inside rails will ultimately
2848
- be translated to an `ActionController::RoutingError`, which most likely will get rendered
2849
- to a HTML error page.
2850
-
2851
- Most APIs will enjoy preventing downstream handlers from handling errors. You may set the
2852
- `:cascade` option to `false` for the entire API or separately on specific `version` definitions,
2853
- which will remove the `X-Cascade: true` header from API responses.
3095
+ A `rescue_from` handler can build a payload its error formatter cannot serialize. The common case is echoing request-derived text back to the client:
2854
3096
 
2855
3097
  ```ruby
2856
- cascade false
3098
+ class Missing < StandardError; end
3099
+
3100
+ class API < Grape::API
3101
+ format :json
3102
+
3103
+ rescue_from(Missing) { |e| error!({ error: 'not_found', detail: e.message }, 404) }
3104
+
3105
+ route_param(:id) { get { raise Missing, "no such thing: #{params[:id]}" } }
3106
+ end
2857
3107
  ```
2858
3108
 
3109
+ `GET /%C3%28` puts an invalid UTF-8 byte in `params[:id]`, and the JSON formatter raises when it reaches that byte in the message — after the handler has already returned, so `rescue_from :all` does not help.
3110
+
3111
+ Rather than let that exception escape the middleware stack, Grape answers `500`: first retrying the API's own format with the framework's `Internal Server Error` message, then falling back to a bare `text/plain` body if even that cannot be rendered.
3112
+
3113
+ The exception is published on the rack env under `rack.exception` — the key error trackers read to find an exception that was handled rather than raised — and on Grape's own `grape.exception`. It is also written to `rack.errors`, so it reaches the log with no tracker installed.
3114
+
3115
+ To opt out and have the exception propagate out of the middleware stack instead:
3116
+
2859
3117
  ```ruby
2860
- version 'v1', using: :header, vendor: 'twitter', cascade: false
3118
+ Grape.configure do |config|
3119
+ config.raise_rendering_errors = true
3120
+ end
2861
3121
  ```
2862
3122
 
3123
+ With this on the failsafe never runs: the exception is re-raised untouched, so neither env key is set and nothing is written to `rack.errors`.
3124
+
2863
3125
  ## Logging
2864
3126
 
2865
- `Grape::API` provides a `logger` method which by default will return an instance of the `Logger`
2866
- class from Ruby's standard library.
3127
+ `Grape::API` provides a `logger` method which by default will return an instance of the `Logger` class from Ruby's standard library.
2867
3128
 
2868
- To log messages from within an endpoint, you need to define a helper to make the logger
2869
- available in the endpoint context.
3129
+ To log messages from within an endpoint, you need to define a helper to make the logger available in the endpoint context.
2870
3130
 
2871
3131
  ```ruby
2872
3132
  class API < Grape::API
@@ -2915,9 +3175,7 @@ For similar to Rails request logging try the [grape_logging](https://github.com/
2915
3175
 
2916
3176
  ## API Formats
2917
3177
 
2918
- Your API can declare which content-types to support by using `content_type`. If you do not specify any, Grape will support
2919
- _XML_, _JSON_, _BINARY_, and _TXT_ content-types. The default format is `:txt`; you can change this with `default_format`.
2920
- Essentially, the two APIs below are equivalent.
3178
+ Your API can declare which content-types to support by using `content_type`. If you do not specify any, Grape will support _XML_, _JSON_, _BINARY_, and _TXT_ content-types. The default format is `:txt`; you can change this with `default_format`. Essentially, the two APIs below are equivalent.
2921
3179
 
2922
3180
  ```ruby
2923
3181
  class Twitter::API < Grape::API
@@ -2936,9 +3194,7 @@ class Twitter::API < Grape::API
2936
3194
  end
2937
3195
  ```
2938
3196
 
2939
- If you declare any `content_type` whatsoever, the Grape defaults will be overridden. For example, the following API will only
2940
- support the `:xml` and `:rss` content-types, but not `:txt`, `:json`, or `:binary`. Importantly, this means the `:txt`
2941
- default format is not supported! So, make sure to set a new `default_format`.
3197
+ If you declare any `content_type` whatsoever, the Grape defaults will be overridden. For example, the following API will only support the `:xml` and `:rss` content-types, but not `:txt`, `:json`, or `:binary`. Importantly, this means the `:txt` default format is not supported! So, make sure to set a new `default_format`.
2942
3198
 
2943
3199
  ```ruby
2944
3200
  class Twitter::API < Grape::API
@@ -2949,8 +3205,7 @@ class Twitter::API < Grape::API
2949
3205
  end
2950
3206
  ```
2951
3207
 
2952
- Serialization takes place automatically. For example, you do not have to call `to_json` in each JSON API endpoint
2953
- implementation. The response format (and thus the automatic serialization) is determined in the following order:
3208
+ Serialization takes place automatically. For example, you do not have to call `to_json` in each JSON API endpoint implementation. The response format (and thus the automatic serialization) is determined in the following order:
2954
3209
  * Use the file extension, if specified. If the file is .json, choose the JSON format.
2955
3210
  * Use the value of the `format` parameter in the query string, if specified.
2956
3211
  * Use the format set by the `format` option, if specified.
@@ -2973,20 +3228,15 @@ class MultipleFormatAPI < Grape::API
2973
3228
  end
2974
3229
  ```
2975
3230
 
2976
- * `GET /hello` (with an `Accept: */*` header) does not have an extension or a `format` parameter, so it will respond with
2977
- JSON (the default format).
3231
+ * `GET /hello` (with an `Accept: */*` header) does not have an extension or a `format` parameter, so it will respond with JSON (the default format).
2978
3232
  * `GET /hello.xml` has a recognized extension, so it will respond with XML.
2979
3233
  * `GET /hello?format=xml` has a recognized `format` parameter, so it will respond with XML.
2980
- * `GET /hello.xml?format=json` has a recognized extension (which takes precedence over the `format` parameter), so it will
2981
- respond with XML.
2982
- * `GET /hello.xls` (with an `Accept: */*` header) has an extension, but that extension is not recognized, so it will respond
2983
- with JSON (the default format).
2984
- * `GET /hello.xls` with an `Accept: application/xml` header has an unrecognized extension, but the `Accept` header
2985
- corresponds to a recognized format, so it will respond with XML.
2986
- * `GET /hello.xls` with an `Accept: text/plain` header has an unrecognized extension *and* an unrecognized `Accept` header,
2987
- so it will respond with JSON (the default format).
2988
-
2989
- You can override this process explicitly by specifying `env['api.format']` in the API itself.
3234
+ * `GET /hello.xml?format=json` has a recognized extension (which takes precedence over the `format` parameter), so it will respond with XML.
3235
+ * `GET /hello.xls` (with an `Accept: */*` header) has an extension, but that extension is not recognized, so it will respond with JSON (the default format).
3236
+ * `GET /hello.xls` with an `Accept: application/xml` header has an unrecognized extension, but the `Accept` header corresponds to a recognized format, so it will respond with XML.
3237
+ * `GET /hello.xls` with an `Accept: text/plain` header has an unrecognized extension *and* an unrecognized `Accept` header, so it will respond with JSON (the default format).
3238
+
3239
+ You can override this process explicitly by calling `api_format` in the API itself.
2990
3240
  For example, the following API will let you upload arbitrary files and return their contents as an attachment with the correct MIME type.
2991
3241
 
2992
3242
  ```ruby
@@ -2994,15 +3244,14 @@ class Twitter::API < Grape::API
2994
3244
  post 'attachment' do
2995
3245
  filename = params[:file][:filename]
2996
3246
  content_type MIME::Types.type_for(filename)[0].to_s
2997
- env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
3247
+ api_format :binary # there's no formatter for :binary, data will be returned "as is"
2998
3248
  header 'Content-Disposition', "attachment; filename*=UTF-8''#{CGI.escape(filename)}"
2999
3249
  params[:file][:tempfile].read
3000
3250
  end
3001
3251
  end
3002
3252
  ```
3003
3253
 
3004
- You can have your API only respond to a single format with `format`. If you use this, the API will **not** respond to file
3005
- extensions other than specified in `format`. For example, consider the following API.
3254
+ You can have your API only respond to a single format with `format`. If you use this, the API will **not** respond to file extensions other than specified in `format`. For example, consider the following API.
3006
3255
 
3007
3256
  ```ruby
3008
3257
  class SingleFormatAPI < Grape::API
@@ -3017,14 +3266,10 @@ end
3017
3266
  * `GET /hello` will respond with JSON.
3018
3267
  * `GET /hello.json` will respond with JSON.
3019
3268
  * `GET /hello.xml`, `GET /hello.foobar`, or *any* other extension will respond with an HTTP 404 error code.
3020
- * `GET /hello?format=xml` will respond with an HTTP 406 error code, because the XML format specified by the request parameter
3021
- is not supported.
3022
- * `GET /hello` with an `Accept: application/xml` header will still respond with JSON, since it could not negotiate a
3023
- recognized content-type from the headers and JSON is the effective default.
3269
+ * `GET /hello?format=xml` will respond with an HTTP 406 error code, because the XML format specified by the request parameter is not supported.
3270
+ * `GET /hello` with an `Accept: application/xml` header will still respond with JSON, since it could not negotiate a recognized content-type from the headers and JSON is the effective default.
3024
3271
 
3025
- The formats apply to parsing, too. The following API will only respond to the JSON content-type and will not parse any other
3026
- input than `application/json`, `application/x-www-form-urlencoded`, `multipart/form-data`, `multipart/related` and
3027
- `multipart/mixed`. All other requests will fail with an HTTP 406 error code.
3272
+ The formats apply to parsing, too. The following API will only respond to the JSON content-type and will not parse any other input than `application/json`, `application/x-www-form-urlencoded`, `multipart/form-data`, `multipart/related` and `multipart/mixed`. All other requests will fail with an HTTP 406 error code.
3028
3273
 
3029
3274
  ```ruby
3030
3275
  class Twitter::API < Grape::API
@@ -3080,23 +3325,53 @@ end
3080
3325
  Built-in formatters are the following.
3081
3326
 
3082
3327
  * `:json`: use object's `to_json` when available, otherwise call `MultiJson.dump`
3083
- * `:xml`: use object's `to_xml` when available, usually via `MultiXml`, otherwise call `to_s`
3328
+ * `:xml`: use object's `to_xml` when available, usually via `MultiXml`
3084
3329
  * `:txt`: use object's `to_txt` when available, otherwise `to_s`
3085
3330
  * `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
3086
3331
  * `:binary`: data will be returned "as is"
3087
3332
 
3088
- If a body is present in a request to an API, with a Content-Type header value that is of an unsupported type a
3089
- "415 Unsupported Media Type" error code will be returned by Grape.
3333
+ #### Serving pre-rendered JSON
3334
+
3335
+ The `:json` and `:serializable_hash` formatters encode whatever the endpoint returned,
3336
+ so a String that already holds JSON gets encoded a second time. Wrap the body in
3337
+ `Grape::PrecompiledJson` to opt that response out of encoding.
3338
+
3339
+ ```ruby
3340
+ class API < Grape::API
3341
+ format :json
3342
+
3343
+ get '/cached' do
3344
+ body Grape::PrecompiledJson.new(Rails.cache.read('payload')) # => '{"id":1}', served as-is
3345
+ end
3346
+ end
3347
+ ```
3348
+
3349
+ An `Array` is joined into a JSON array without its members being parsed, so a cached
3350
+ collection is spliced together rather than round-tripped. Members may themselves be
3351
+ `Grape::PrecompiledJson` instances.
3090
3352
 
3091
- Response statuses that indicate no content as defined by [Rack](https://github.com/rack)
3092
- [here](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
3093
- will bypass serialization and the body entity - though there should be none -
3094
- will not be modified.
3353
+ ```ruby
3354
+ Grape::PrecompiledJson.new(['{"id":1}', '{"id":2}']).to_s # => '[{"id":1},{"id":2}]'
3355
+ ```
3356
+
3357
+ Bodies that are not wrapped keep encoding exactly as before, so this is opt-in per
3358
+ response rather than a mode the whole API is in. Wrap only the whole body: a wrapper
3359
+ nested inside a Hash or Array that is then handed to an encoder is serialized as an
3360
+ ordinary object, because no encoder knows to unwrap it. A value that is neither a
3361
+ String nor an Array raises `Grape::Exceptions::InvalidFormatter` and answers 500,
3362
+ rather than handing Rack a body it cannot serve. Nothing checks that the String
3363
+ actually holds JSON — that is the caller's responsibility.
3364
+
3365
+ Errors are unaffected: they are rendered by the error formatter, so `error!` still
3366
+ returns properly encoded JSON.
3367
+
3368
+ If a body is present in a request to an API, with a Content-Type header value that is of an unsupported type a "415 Unsupported Media Type" error code will be returned by Grape.
3369
+
3370
+ Response statuses that indicate no content as defined by [Rack](https://github.com/rack) [here](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567) will bypass serialization and the body entity - though there should be none - will not be modified.
3095
3371
 
3096
3372
  ### JSONP
3097
3373
 
3098
- Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the
3099
- [rack-contrib](https://github.com/rack/rack-contrib) gem. Add `rack-contrib` to your `Gemfile`.
3374
+ Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the [rack-contrib](https://github.com/rack/rack-contrib) gem. Add `rack-contrib` to your `Gemfile`.
3100
3375
 
3101
3376
  ```ruby
3102
3377
  require 'rack/contrib'
@@ -3112,9 +3387,7 @@ end
3112
3387
 
3113
3388
  ### CORS
3114
3389
 
3115
- Grape supports CORS via [Rack::CORS](https://github.com/cyu/rack-cors), part of the
3116
- [rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile`,
3117
- then use the middleware in your config.ru file.
3390
+ Grape supports CORS via [Rack::CORS](https://github.com/cyu/rack-cors), part of the [rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile`, then use the middleware in your config.ru file.
3118
3391
 
3119
3392
  ```ruby
3120
3393
  require 'rack/cors'
@@ -3132,8 +3405,7 @@ run Twitter::API
3132
3405
 
3133
3406
  ## Content-type
3134
3407
 
3135
- Content-type is set by the formatter. You can override the content-type of the response at runtime
3136
- by setting the `Content-Type` header.
3408
+ Content-type is set by the formatter. You can override the content-type of the response at runtime by setting the `Content-Type` header.
3137
3409
 
3138
3410
  ```ruby
3139
3411
  class API < Grape::API
@@ -3146,16 +3418,12 @@ end
3146
3418
 
3147
3419
  ## API Data Formats
3148
3420
 
3149
- Grape accepts and parses input data sent with the POST and PUT methods as described in the Parameters
3150
- section above. It also supports custom data formats. You must declare additional content-types via
3151
- `content_type` and optionally supply a parser via `parser` unless a parser is already available within
3152
- Grape to enable a custom format. Such a parser can be a function or a class.
3421
+ Grape accepts and parses input data sent with the POST and PUT methods as described in the Parameters section above. It also supports custom data formats. You must declare additional content-types via `content_type` and optionally supply a parser via `parser` unless a parser is already available within Grape to enable a custom format. Such a parser can be a function or a class.
3153
3422
 
3154
3423
  With a parser, parsed data is available "as-is" in `env['api.request.body']`.
3155
3424
  Without a parser, data is available "as-is" and in `env['api.request.input']`.
3156
3425
 
3157
- The following example is a trivial parser that will assign any input with the "text/custom" content-type
3158
- to `:value`. The parameter will be available via `params[:value]` inside the API call.
3426
+ The following example is a trivial parser that will assign any input with the "text/custom" content-type to `:value`. The parameter will be available via `params[:value]` inside the API call.
3159
3427
 
3160
3428
  ```ruby
3161
3429
  module CustomParser
@@ -3189,9 +3457,7 @@ Grape uses `JSON` and `ActiveSupport::XmlMini` for JSON and XML parsing by defau
3189
3457
 
3190
3458
  ## RESTful Model Representations
3191
3459
 
3192
- Grape supports a range of ways to present your data with some help from a generic `present` method,
3193
- which accepts two arguments: the object to be presented and the options associated with it. The options
3194
- hash may include `:with`, which defines the entity to expose.
3460
+ Grape supports a range of ways to present your data with some help from a generic `present` method, which accepts two arguments: the object to be presented and the options associated with it. The options hash may include `:with`, which defines the entity to expose.
3195
3461
 
3196
3462
  ### Grape Entities
3197
3463
 
@@ -3270,8 +3536,7 @@ The response will be
3270
3536
  }
3271
3537
  ```
3272
3538
 
3273
- In addition to separately organizing entities, it may be useful to put them as namespaced
3274
- classes underneath the model they represent.
3539
+ In addition to separately organizing entities, it may be useful to put them as namespaced classes underneath the model they represent.
3275
3540
 
3276
3541
  ```ruby
3277
3542
  class Status
@@ -3285,11 +3550,7 @@ class Status
3285
3550
  end
3286
3551
  ```
3287
3552
 
3288
- If you organize your entities this way, Grape will automatically detect the `Entity` class and
3289
- use it to present your models. In this example, if you added `present Status.new` to your endpoint,
3290
- Grape will automatically detect that there is a `Status::Entity` class and use that as the
3291
- representative entity. This can still be overridden by using the `:with` option or an explicit
3292
- `represents` call.
3553
+ If you organize your entities this way, Grape will automatically detect the `Entity` class and use it to present your models. In this example, if you added `present Status.new` to your endpoint, Grape will automatically detect that there is a `Status::Entity` class and use that as the representative entity. This can still be overridden by using the `:with` option or an explicit `represents` call.
3293
3554
 
3294
3555
  You can present `hash` with `Grape::Presenters::Presenter` to keep things consistent.
3295
3556
 
@@ -3322,15 +3583,11 @@ You can use [Roar](https://github.com/apotonick/roar) to render HAL or Collectio
3322
3583
 
3323
3584
  ### Rabl
3324
3585
 
3325
- You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the
3326
- [grape-rabl](https://github.com/ruby-grape/grape-rabl) gem, which defines a custom Grape Rabl
3327
- formatter.
3586
+ You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the [grape-rabl](https://github.com/ruby-grape/grape-rabl) gem, which defines a custom Grape Rabl formatter.
3328
3587
 
3329
3588
  ### Active Model Serializers
3330
3589
 
3331
- You can use [Active Model Serializers](https://github.com/rails-api/active_model_serializers) serializers with the help of the
3332
- [grape-active_model_serializers](https://github.com/jrhe/grape-active_model_serializers) gem, which defines a custom Grape AMS
3333
- formatter.
3590
+ You can use [Active Model Serializers](https://github.com/rails-api/active_model_serializers) serializers with the help of the [grape-active_model_serializers](https://github.com/jrhe/grape-active_model_serializers) gem, which defines a custom Grape AMS formatter.
3334
3591
 
3335
3592
  ## Sending Raw or No Data
3336
3593
 
@@ -3370,9 +3627,7 @@ class API < Grape::API
3370
3627
  end
3371
3628
  ```
3372
3629
 
3373
- You can also set the response to a file with `sendfile`. This works with the
3374
- [Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send
3375
- the file through your web server software.
3630
+ You can also set the response to a file with `sendfile`. This works with the [Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send the file through your web server software.
3376
3631
 
3377
3632
  ```ruby
3378
3633
  class API < Grape::API
@@ -3414,11 +3669,9 @@ end
3414
3669
 
3415
3670
  ## Authentication
3416
3671
 
3417
- ### Basic and Digest Auth
3672
+ ### Basic Auth
3418
3673
 
3419
- Grape has built-in Basic and Digest authentication (the given `block`
3420
- is executed in the context of the current `Endpoint`). Authentication
3421
- applies to the current namespace and any children, but not parents.
3674
+ Grape has built-in Basic authentication (the given `block` is executed in the context of the current `Endpoint`). Authentication applies to the current namespace and any children, but not parents.
3422
3675
 
3423
3676
  ```ruby
3424
3677
  http_basic do |username, password|
@@ -3427,32 +3680,15 @@ http_basic do |username, password|
3427
3680
  end
3428
3681
  ```
3429
3682
 
3430
- Digest auth supports clear-text passwords and password hashes.
3431
-
3432
- ```ruby
3433
- http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
3434
- # lookup the user's password here
3435
- end
3436
- ```
3437
-
3438
- ```ruby
3439
- http_digest(realm: { realm: 'Test Api', opaque: 'app secret', passwords_hashed: true }) do |username|
3440
- # lookup the user's password hash here
3441
- end
3442
- ```
3443
-
3444
3683
  ### Register custom middleware for authentication
3445
3684
 
3446
- Grape can use custom Middleware for authentication. How to implement these
3447
- Middleware have a look at `Rack::Auth::Basic` or similar implementations.
3448
-
3685
+ Grape can use custom Middleware for authentication. How to implement these Middleware have a look at `Rack::Auth::Basic` or similar implementations.
3449
3686
 
3450
3687
  For registering a Middleware you need the following options:
3451
3688
 
3452
3689
  * `label` - the name for your authenticator to use it later
3453
3690
  * `MiddlewareClass` - the MiddlewareClass to use for authentication
3454
- * `option_lookup_proc` - A Proc with one Argument to lookup the options at
3455
- runtime (return value is an `Array` as Parameter for the Middleware).
3691
+ * `option_lookup_proc` - A Proc with one Argument to lookup the options at runtime (return value is an `Array` as Parameter for the Middleware).
3456
3692
 
3457
3693
  Example:
3458
3694
 
@@ -3476,7 +3712,7 @@ You can access the controller params, headers, and helpers through the context w
3476
3712
 
3477
3713
  Grape routes can be reflected at runtime. This can notably be useful for generating documentation.
3478
3714
 
3479
- Grape exposes arrays of API versions and compiled routes. Each route contains a `route_prefix`, `route_version`, `route_namespace`, `route_method`, `route_path` and `route_params`. You can add custom route settings to the route metadata with `route_setting`.
3715
+ Grape exposes arrays of API versions and compiled routes. Each route contains a `prefix`, `version`, `namespace`, `method` and `params`. You can add custom route settings to the route metadata with `route_setting`.
3480
3716
 
3481
3717
  ```ruby
3482
3718
  class TwitterAPI < Grape::API
@@ -3499,14 +3735,14 @@ TwitterAPI::routes[0].description # => 'Includes custom settings.'
3499
3735
  TwitterAPI::routes[0].settings[:custom] # => { key: 'value' }
3500
3736
  ```
3501
3737
 
3502
- Note that `Route#route_xyz` methods have been deprecated since 0.15.0.
3738
+ Note that `Route#route_xyz` methods have been deprecated since 0.15.0 and removed since 2.0.1.
3503
3739
 
3504
3740
  Please use `Route#xyz` instead.
3505
3741
 
3506
3742
  Note that difference of `Route#options` and `Route#settings`.
3507
3743
 
3508
- The `options` can be referred from your route, it should be set by specifing key and value on verb methods such as `get`, `post` and `put`.
3509
- The `settings` can also be referred from your route, but it should be set by specifing key and value on `route_setting`.
3744
+ The `options` can be referred from your route, it should be set by specifying key and value on verb methods such as `get`, `post` and `put`.
3745
+ The `settings` can also be referred from your route, but it should be set by specifying key and value on `route_setting`.
3510
3746
 
3511
3747
  ## Current Route and Endpoint
3512
3748
 
@@ -3519,15 +3755,12 @@ class MyAPI < Grape::API
3519
3755
  requires :id, type: Integer, desc: 'Identity.'
3520
3756
  end
3521
3757
  get 'params/:id' do
3522
- route.route_params[params[:id]] # yields the parameter description
3758
+ route.params[params[:id]] # yields the parameter description
3523
3759
  end
3524
3760
  end
3525
3761
  ```
3526
3762
 
3527
- The current endpoint responding to the request is `self` within the API block
3528
- or `env['api.endpoint']` elsewhere. The endpoint has some interesting properties,
3529
- such as `source` which gives you access to the original code block of the API
3530
- implementation. This can be particularly useful for building a logger middleware.
3763
+ The current endpoint responding to the request is `self` within the API block or `env['api.endpoint']` elsewhere. The endpoint has some interesting properties, such as `source` which gives you access to the original code block of the API implementation. This can be particularly useful for building a logger middleware.
3531
3764
 
3532
3765
  ```ruby
3533
3766
  class ApiLogger < Grape::Middleware::Base
@@ -3541,10 +3774,8 @@ end
3541
3774
 
3542
3775
  ## Before, After and Finally
3543
3776
 
3544
- Blocks can be executed before or after every API call, using `before`, `after`,
3545
- `before_validation` and `after_validation`.
3546
- If the API fails the `after` call will not be triggered, if you need code to execute for sure
3547
- use the `finally`.
3777
+ Blocks can be executed before or after every API call, using `before`, `after`, `before_validation` and `after_validation`.
3778
+ If the API fails the `after` call will not be triggered, if you need code to execute for sure use the `finally`.
3548
3779
 
3549
3780
  Before and after callbacks execute in the following order:
3550
3781
 
@@ -3558,13 +3789,9 @@ Before and after callbacks execute in the following order:
3558
3789
 
3559
3790
  Steps 4, 5 and 6 only happen if validation succeeds.
3560
3791
 
3561
- If a request for a resource is made with an unsupported HTTP method (returning
3562
- HTTP 405) only `before` callbacks will be executed. The remaining callbacks will
3563
- be bypassed.
3792
+ If a request for a resource is made with an unsupported HTTP method (returning HTTP 405) only `before` callbacks will be executed. The remaining callbacks will be bypassed.
3564
3793
 
3565
- If a request for a resource is made that triggers the built-in `OPTIONS` handler,
3566
- only `before` and `after` callbacks will be executed. The remaining callbacks will
3567
- be bypassed.
3794
+ If a request for a resource is made that triggers the built-in `OPTIONS` handler, only `before` and `after` callbacks will be executed. The remaining callbacks will be bypassed.
3568
3795
 
3569
3796
  For example, using a simple `before` block to set a header.
3570
3797
 
@@ -3709,11 +3936,7 @@ Instead of altering a response, you can also terminate and rewrite it from any c
3709
3936
 
3710
3937
  ## Anchoring
3711
3938
 
3712
- Grape by default anchors all request paths, which means that the request URL
3713
- should match from start to end to match, otherwise a `404 Not Found` is
3714
- returned. However, this is sometimes not what you want, because it is not always
3715
- known upfront what can be expected from the call. This is because Rack-mount by
3716
- default anchors requests to match from the start to the end, or not at all.
3939
+ Grape by default anchors all request paths, which means that the request URL should match from start to end to match, otherwise a `404 Not Found` is returned. However, this is sometimes not what you want, because it is not always known upfront what can be expected from the call. This is because Rack-mount by default anchors requests to match from the start to the end, or not at all.
3717
3940
  Rails solves this problem by using a `anchor: false` option in your routes.
3718
3941
  In Grape this option can be used as well when a method is defined.
3719
3942
 
@@ -3729,12 +3952,44 @@ class TwitterAPI < Grape::API
3729
3952
  end
3730
3953
  ```
3731
3954
 
3732
- This will match all paths starting with '/statuses/'. There is one caveat though:
3733
- the `params[:status]` parameter only holds the first part of the request url.
3734
- Luckily this can be circumvented by using the described above syntax for path
3735
- specification and using the `PATH_INFO` Rack environment variable, using
3736
- `env['PATH_INFO']`. This will hold everything that comes after the '/statuses/'
3737
- part.
3955
+ This will match all paths starting with '/statuses/'. There is one caveat though: the `params[:status]` parameter only holds the first part of the request url.
3956
+ Luckily this can be circumvented by using the described above syntax for path specification and using the `PATH_INFO` Rack environment variable, using `env['PATH_INFO']`. This will hold everything that comes after the '/statuses/' part.
3957
+
3958
+ ## Instance Variables
3959
+
3960
+ You can use instance variables to pass information across the various stages of a request. An instance variable set within a `before` validator is accessible within the endpoint's code and can also be utilized within the `rescue_from` handler.
3961
+
3962
+ ```ruby
3963
+ class TwitterAPI < Grape::API
3964
+ before do
3965
+ @var = 1
3966
+ end
3967
+
3968
+ rescue_from :all do
3969
+ puts @var # => 1
3970
+ end
3971
+
3972
+ get '/' do
3973
+ puts @var # => 1
3974
+ raise
3975
+ end
3976
+ end
3977
+ ```
3978
+
3979
+ The values of instance variables cannot be shared among various endpoints within the same API. This limitation arises due to Grape generating a new instance for each request made. Consequently, instance variables set within an endpoint during one request differ from those set during a subsequent request, as they exist within separate instances.
3980
+
3981
+ ```ruby
3982
+ class TwitterAPI < Grape::API
3983
+ get '/first' do
3984
+ @var = 1
3985
+ puts @var # => 1
3986
+ end
3987
+
3988
+ get '/second' do
3989
+ puts @var # => nil
3990
+ end
3991
+ end
3992
+ ```
3738
3993
 
3739
3994
  ## Using Custom Middleware
3740
3995
 
@@ -3943,8 +4198,7 @@ describe Twitter::API do
3943
4198
  end
3944
4199
  ```
3945
4200
 
3946
- In Rails, HTTP request tests would go into the `spec/requests` group. You may want your API code to go into
3947
- `app/api` - you can match that layout under `spec` by adding the following in `spec/rails_helper.rb`.
4201
+ In Rails, HTTP request tests would go into the `spec/requests` group. You may want your API code to go into `app/api` - you can match that layout under `spec` by adding the following in `spec/rails_helper.rb`.
3948
4202
 
3949
4203
  ```ruby
3950
4204
  RSpec.configure do |config|
@@ -3978,10 +4232,15 @@ end
3978
4232
 
3979
4233
  ### Stubbing Helpers
3980
4234
 
3981
- Because helpers are mixed in based on the context when an endpoint is defined, it can
3982
- be difficult to stub or mock them for testing. The `Grape::Endpoint.before_each` method
3983
- can help by allowing you to define behavior on the endpoint that will run before every
3984
- request.
4235
+ Because helpers are mixed in based on the context when an endpoint is defined, it can be difficult to stub or mock them for testing. `Grape::Endpoint.before_each` allows you to define behavior on the endpoint that will run before every request.
4236
+
4237
+ This feature is provided by `Grape::Testing`, a standalone module intended for test environments only. Add it to your test helper:
4238
+
4239
+ ```ruby
4240
+ require 'grape/testing'
4241
+ ```
4242
+
4243
+ Then use it in your tests:
3985
4244
 
3986
4245
  ```ruby
3987
4246
  describe 'an endpoint that needs helpers stubbed' do
@@ -3992,7 +4251,7 @@ describe 'an endpoint that needs helpers stubbed' do
3992
4251
  end
3993
4252
 
3994
4253
  after do
3995
- Grape::Endpoint.before_each nil
4254
+ Grape::Endpoint.reset_before_each
3996
4255
  end
3997
4256
 
3998
4257
  it 'stubs the helper' do
@@ -4009,6 +4268,25 @@ Use [grape-reload](https://github.com/AlexYankee/grape-reload).
4009
4268
 
4010
4269
  ### Reloading in Rails Applications
4011
4270
 
4271
+ #### Rails 7+ (Zeitwerk)
4272
+
4273
+ Rails 7+ uses [Zeitwerk](https://github.com/fxn/zeitwerk) as the default autoloader, which automatically handles reloading of code in development mode without any additional configuration.
4274
+
4275
+ If your API files are in `app/api`, Zeitwerk will automatically autoload and reload them. No additional configuration is needed.
4276
+
4277
+ If you encounter issues with reloading, ensure that:
4278
+
4279
+ 1. Your API files follow Zeitwerk naming conventions (file names should match class names).
4280
+ 2. The `config.enable_reloading` is set to `true` in `config/environments/development.rb` (this is the default).
4281
+
4282
+ For troubleshooting autoloading issues, have a look at the [Rails documentation](https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#troubleshooting).
4283
+
4284
+ See the [Rails Autoloading and Reloading Constants guide](https://guides.rubyonrails.org/autoloading_and_reloading_constants.html) for more information.
4285
+
4286
+ #### Rails 6 and Earlier
4287
+
4288
+ For Rails versions before 7, you need to configure reloading manually.
4289
+
4012
4290
  Add API paths to `config/application.rb`.
4013
4291
 
4014
4292
  ```ruby
@@ -4027,28 +4305,12 @@ if Rails.env.development?
4027
4305
  api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
4028
4306
  Rails.application.reload_routes!
4029
4307
  end
4030
- ActionDispatch::Callbacks.to_prepare do
4308
+ ActiveSupport::Reloader.to_prepare do
4031
4309
  api_reloader.execute_if_updated
4032
4310
  end
4033
4311
  end
4034
4312
  ```
4035
4313
 
4036
- For Rails >= 5.1.4, change this:
4037
-
4038
- ```ruby
4039
- ActionDispatch::Callbacks.to_prepare do
4040
- api_reloader.execute_if_updated
4041
- end
4042
- ```
4043
-
4044
- to this:
4045
-
4046
- ```ruby
4047
- ActiveSupport::Reloader.to_prepare do
4048
- api_reloader.execute_if_updated
4049
- end
4050
- ```
4051
-
4052
4314
  See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request/4368838#4368838) for more information.
4053
4315
 
4054
4316
  ## Performance Monitoring
@@ -4057,27 +4319,30 @@ See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-
4057
4319
 
4058
4320
  Grape has built-in support for [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) which provides simple hook points to instrument key parts of your application.
4059
4321
 
4060
- The following are currently supported:
4061
4322
 
4062
- #### endpoint_run.grape
4323
+ #### Hook Points
4324
+
4325
+ The following hook points are currently supported:
4326
+
4327
+ ##### endpoint_run.grape
4063
4328
 
4064
4329
  The main execution of an endpoint, includes filters and rendering.
4065
4330
 
4066
4331
  * *endpoint* - The endpoint instance
4067
4332
 
4068
- #### endpoint_render.grape
4333
+ ##### endpoint_render.grape
4069
4334
 
4070
4335
  The execution of the main content block of the endpoint.
4071
4336
 
4072
4337
  * *endpoint* - The endpoint instance
4073
4338
 
4074
- #### endpoint_run_filters.grape
4339
+ ##### endpoint_run_filters.grape
4075
4340
 
4076
4341
  * *endpoint* - The endpoint instance
4077
4342
  * *filters* - The filters being executed
4078
4343
  * *type* - The type of filters (before, before_validation, after_validation, after)
4079
4344
 
4080
- #### endpoint_run_validators.grape
4345
+ ##### endpoint_run_validators.grape
4081
4346
 
4082
4347
  The execution of validators.
4083
4348
 
@@ -4085,7 +4350,7 @@ The execution of validators.
4085
4350
  * *validators* - The validators being executed
4086
4351
  * *request* - The request being validated
4087
4352
 
4088
- #### format_response.grape
4353
+ ##### format_response.grape
4089
4354
 
4090
4355
  Serialization or template rendering.
4091
4356
 
@@ -4094,12 +4359,44 @@ Serialization or template rendering.
4094
4359
 
4095
4360
  See the [ActiveSupport::Notifications documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) for information on how to subscribe to these events.
4096
4361
 
4362
+ #### Subscribe to Hooks
4363
+
4364
+ Once subscribed to the instrumentation, you can intercept the events reported above.
4365
+
4366
+ ```ruby
4367
+ ActiveSupport::Notifications.subscribe(/<api_path>/) do |name, start, finish, id, payload|
4368
+ # your code to intercept the notification
4369
+ end
4370
+ ```
4371
+
4372
+ The request data, the API’s internal data, and the response can be retrieved from the payload.
4373
+
4374
+ You can use `payload.fetch(:endpoint)` or directly `payload[:endpoint]`.
4375
+
4376
+ The `:endpoint` contains the data currently being processed, and access to attributes such as `body`, `request`, `params`, `headers`, `cookies` and `response_cookies`
4377
+
4378
+ For example, `payload[:endpoint].body` provides the current state of the response.
4379
+
4380
+ ```ruby
4381
+ ActiveSupport::Notifications.subscribe(/v1/) do |name, start, finish, id, payload|
4382
+ hook_record = {
4383
+ hook: name
4384
+ status: payload[:env]&.dig("api.endpoint")&.status
4385
+ format: payload[:env]&.dig("api.format")
4386
+ body: payload[:endpoint]&.body
4387
+ duration: (finish - start) * 1000
4388
+ }
4389
+ # your code to save the notification
4390
+ end
4391
+ ```
4392
+
4097
4393
  ### Monitoring Products
4098
4394
 
4099
4395
  Grape integrates with following third-party tools:
4100
4396
 
4101
4397
  * **New Relic** - [built-in support](https://docs.newrelic.com/docs/agents/ruby-agent/frameworks/grape-instrumentation) from v3.10.0 of the official [newrelic_rpm](https://github.com/newrelic/rpm) gem, also [newrelic-grape](https://github.com/xinminlabs/newrelic-grape) gem
4102
4398
  * **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
4399
+ * **Rails Performance** - [rails_performance](https://github.com/igorkasyanchuk/rails_performance) gem
4103
4400
  * **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
4104
4401
  * **[AppSignal](https://www.appsignal.com)** - [appsignal-ruby](https://github.com/appsignal/appsignal-ruby) gem, [documentation](http://docs.appsignal.com/getting-started/supported-frameworks.html#grape)
4105
4402
  * **[ElasticAPM](https://www.elastic.co/products/apm)** - [elastic-apm](https://github.com/elastic/apm-agent-ruby) gem, [documentation](https://www.elastic.co/guide/en/apm/agent/ruby/3.x/getting-started-rack.html#getting-started-grape)
@@ -4107,8 +4404,7 @@ Grape integrates with following third-party tools:
4107
4404
 
4108
4405
  ## Contributing to Grape
4109
4406
 
4110
- Grape is work of hundreds of contributors. You're encouraged to submit pull requests, propose
4111
- features and discuss issues.
4407
+ Grape is work of hundreds of contributors. You're encouraged to submit pull requests, propose features and discuss issues.
4112
4408
 
4113
4409
  See [CONTRIBUTING](CONTRIBUTING.md).
4114
4410