grape 1.1.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (306) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +370 -44
  3. data/CONTRIBUTING.md +32 -1
  4. data/LICENSE +1 -1
  5. data/README.md +683 -87
  6. data/UPGRADING.md +481 -17
  7. data/grape.gemspec +15 -4
  8. data/lib/grape/api/helpers.rb +2 -0
  9. data/lib/grape/api/instance.rb +279 -0
  10. data/lib/grape/api.rb +144 -176
  11. data/lib/grape/config.rb +34 -0
  12. data/lib/grape/content_types.rb +34 -0
  13. data/lib/grape/cookies.rb +4 -0
  14. data/lib/grape/dry_types.rb +12 -0
  15. data/lib/grape/dsl/api.rb +1 -1
  16. data/lib/grape/dsl/callbacks.rb +21 -1
  17. data/lib/grape/dsl/configuration.rb +1 -1
  18. data/lib/grape/dsl/desc.rb +41 -23
  19. data/lib/grape/dsl/headers.rb +7 -2
  20. data/lib/grape/dsl/helpers.rb +10 -7
  21. data/lib/grape/dsl/inside_route.rb +118 -62
  22. data/lib/grape/dsl/logger.rb +2 -0
  23. data/lib/grape/dsl/middleware.rb +11 -4
  24. data/lib/grape/dsl/parameters.rb +33 -19
  25. data/lib/grape/dsl/request_response.rb +12 -9
  26. data/lib/grape/dsl/routing.rb +22 -13
  27. data/lib/grape/dsl/settings.rb +10 -6
  28. data/lib/grape/dsl/validations.rb +19 -14
  29. data/lib/grape/eager_load.rb +20 -0
  30. data/lib/grape/endpoint.rb +67 -58
  31. data/lib/grape/error_formatter/base.rb +2 -0
  32. data/lib/grape/error_formatter/json.rb +11 -7
  33. data/lib/grape/error_formatter/txt.rb +2 -0
  34. data/lib/grape/error_formatter/xml.rb +4 -6
  35. data/lib/grape/error_formatter.rb +4 -2
  36. data/lib/grape/exceptions/base.rb +23 -16
  37. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  38. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  39. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  40. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  41. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  42. data/lib/grape/exceptions/invalid_response.rb +11 -0
  43. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  44. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  45. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  46. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  47. data/lib/grape/exceptions/missing_group_type.rb +10 -1
  48. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  49. data/lib/grape/exceptions/missing_option.rb +2 -0
  50. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  51. data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
  52. data/lib/grape/exceptions/unknown_options.rb +2 -0
  53. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  54. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  55. data/lib/grape/exceptions/unsupported_group_type.rb +10 -1
  56. data/lib/grape/exceptions/validation.rb +5 -8
  57. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  58. data/lib/grape/exceptions/validation_errors.rb +16 -13
  59. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  60. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  61. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  62. data/lib/grape/extensions/hash.rb +2 -0
  63. data/lib/grape/extensions/hashie/mash.rb +2 -0
  64. data/lib/grape/formatter/json.rb +3 -0
  65. data/lib/grape/formatter/serializable_hash.rb +4 -1
  66. data/lib/grape/formatter/txt.rb +2 -0
  67. data/lib/grape/formatter/xml.rb +3 -0
  68. data/lib/grape/formatter.rb +5 -3
  69. data/lib/grape/http/headers.rb +50 -18
  70. data/lib/grape/locale/en.yml +11 -8
  71. data/lib/grape/middleware/auth/base.rb +7 -7
  72. data/lib/grape/middleware/auth/dsl.rb +9 -2
  73. data/lib/grape/middleware/auth/strategies.rb +2 -0
  74. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  75. data/lib/grape/middleware/base.rb +13 -8
  76. data/lib/grape/middleware/error.rb +22 -17
  77. data/lib/grape/middleware/filter.rb +2 -0
  78. data/lib/grape/middleware/formatter.rb +12 -10
  79. data/lib/grape/middleware/globals.rb +2 -0
  80. data/lib/grape/middleware/helpers.rb +12 -0
  81. data/lib/grape/middleware/stack.rb +16 -6
  82. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
  83. data/lib/grape/middleware/versioner/header.rb +13 -9
  84. data/lib/grape/middleware/versioner/param.rb +4 -1
  85. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
  86. data/lib/grape/middleware/versioner/path.rb +5 -1
  87. data/lib/grape/middleware/versioner.rb +2 -0
  88. data/lib/grape/namespace.rb +14 -2
  89. data/lib/grape/parser/json.rb +3 -1
  90. data/lib/grape/parser/xml.rb +3 -1
  91. data/lib/grape/parser.rb +4 -2
  92. data/lib/grape/path.rb +16 -3
  93. data/lib/grape/presenters/presenter.rb +2 -0
  94. data/lib/grape/request.rb +21 -9
  95. data/lib/grape/router/attribute_translator.rb +41 -8
  96. data/lib/grape/router/pattern.rb +21 -17
  97. data/lib/grape/router/route.rb +15 -29
  98. data/lib/grape/router.rb +36 -29
  99. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  100. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  101. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  102. data/lib/grape/types/invalid_value.rb +8 -0
  103. data/lib/grape/util/base_inheritable.rb +43 -0
  104. data/lib/grape/util/cache.rb +20 -0
  105. data/lib/grape/util/endpoint_configuration.rb +8 -0
  106. data/lib/grape/util/env.rb +19 -17
  107. data/lib/grape/util/inheritable_setting.rb +3 -3
  108. data/lib/grape/util/inheritable_values.rb +7 -25
  109. data/lib/grape/util/json.rb +4 -0
  110. data/lib/grape/util/lazy_block.rb +27 -0
  111. data/lib/grape/util/lazy_object.rb +43 -0
  112. data/lib/grape/util/lazy_value.rb +99 -0
  113. data/lib/grape/util/registrable.rb +2 -0
  114. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  115. data/lib/grape/util/stackable_values.rb +21 -34
  116. data/lib/grape/util/strict_hash_configuration.rb +3 -1
  117. data/lib/grape/util/xml.rb +2 -0
  118. data/lib/grape/validations/attributes_doc.rb +58 -0
  119. data/lib/grape/validations/attributes_iterator.rb +16 -6
  120. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  121. data/lib/grape/validations/params_scope.rb +174 -94
  122. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  123. data/lib/grape/validations/types/array_coercer.rb +63 -0
  124. data/lib/grape/validations/types/build_coercer.rb +47 -49
  125. data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
  126. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  127. data/lib/grape/validations/types/dry_type_coercer.rb +72 -0
  128. data/lib/grape/validations/types/file.rb +22 -18
  129. data/lib/grape/validations/types/invalid_value.rb +17 -0
  130. data/lib/grape/validations/types/json.rb +47 -39
  131. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  132. data/lib/grape/validations/types/primitive_coercer.rb +75 -0
  133. data/lib/grape/validations/types/set_coercer.rb +38 -0
  134. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  135. data/lib/grape/validations/types.rb +106 -63
  136. data/lib/grape/validations/validator_factory.rb +8 -11
  137. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  138. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  139. data/lib/grape/validations/validators/as_validator.rb +14 -0
  140. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  141. data/lib/grape/validations/validators/base.rb +84 -68
  142. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  143. data/lib/grape/validations/validators/default_validator.rb +51 -0
  144. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  145. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  146. data/lib/grape/validations/validators/multiple_params_base.rb +27 -16
  147. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  148. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  149. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  150. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  151. data/lib/grape/validations/validators/values_validator.rb +88 -0
  152. data/lib/grape/validations.rb +18 -6
  153. data/lib/grape/version.rb +3 -1
  154. data/lib/grape.rb +175 -94
  155. data/spec/grape/api/custom_validations_spec.rb +117 -44
  156. data/spec/grape/api/deeply_included_options_spec.rb +4 -4
  157. data/spec/grape/api/defines_boolean_in_params_spec.rb +38 -0
  158. data/spec/grape/api/documentation_spec.rb +59 -0
  159. data/spec/grape/api/inherited_helpers_spec.rb +1 -1
  160. data/spec/grape/api/instance_spec.rb +103 -0
  161. data/spec/grape/api/invalid_format_spec.rb +3 -1
  162. data/spec/grape/api/namespace_parameters_in_route_spec.rb +1 -1
  163. data/spec/grape/api/nested_helpers_spec.rb +1 -1
  164. data/spec/grape/api/optional_parameters_in_route_spec.rb +1 -1
  165. data/spec/grape/api/parameters_modification_spec.rb +2 -2
  166. data/spec/grape/api/patch_method_helpers_spec.rb +1 -1
  167. data/spec/grape/api/recognize_path_spec.rb +2 -2
  168. data/spec/grape/api/required_parameters_in_route_spec.rb +1 -1
  169. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +1 -1
  170. data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
  171. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +10 -16
  172. data/spec/grape/api/shared_helpers_spec.rb +1 -1
  173. data/spec/grape/api_remount_spec.rb +473 -0
  174. data/spec/grape/api_spec.rb +995 -231
  175. data/spec/grape/config_spec.rb +17 -0
  176. data/spec/grape/dsl/callbacks_spec.rb +3 -2
  177. data/spec/grape/dsl/desc_spec.rb +43 -17
  178. data/spec/grape/dsl/headers_spec.rb +40 -10
  179. data/spec/grape/dsl/helpers_spec.rb +6 -5
  180. data/spec/grape/dsl/inside_route_spec.rb +189 -38
  181. data/spec/grape/dsl/logger_spec.rb +17 -19
  182. data/spec/grape/dsl/middleware_spec.rb +11 -2
  183. data/spec/grape/dsl/parameters_spec.rb +3 -1
  184. data/spec/grape/dsl/request_response_spec.rb +8 -7
  185. data/spec/grape/dsl/routing_spec.rb +22 -9
  186. data/spec/grape/dsl/settings_spec.rb +1 -1
  187. data/spec/grape/dsl/validations_spec.rb +1 -16
  188. data/spec/grape/endpoint/declared_spec.rb +846 -0
  189. data/spec/grape/endpoint_spec.rb +136 -577
  190. data/spec/grape/entity_spec.rb +31 -24
  191. data/spec/grape/exceptions/base_spec.rb +81 -0
  192. data/spec/grape/exceptions/body_parse_errors_spec.rb +4 -1
  193. data/spec/grape/exceptions/invalid_accept_header_spec.rb +65 -23
  194. data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
  195. data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
  196. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -2
  197. data/spec/grape/exceptions/missing_group_type_spec.rb +21 -0
  198. data/spec/grape/exceptions/missing_mime_type_spec.rb +1 -1
  199. data/spec/grape/exceptions/missing_option_spec.rb +2 -2
  200. data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
  201. data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
  202. data/spec/grape/exceptions/unsupported_group_type_spec.rb +23 -0
  203. data/spec/grape/exceptions/validation_errors_spec.rb +21 -15
  204. data/spec/grape/exceptions/validation_spec.rb +6 -4
  205. data/spec/grape/extensions/param_builders/hash_spec.rb +8 -8
  206. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +9 -9
  207. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +9 -9
  208. data/spec/grape/integration/global_namespace_function_spec.rb +2 -2
  209. data/spec/grape/integration/rack_sendfile_spec.rb +14 -10
  210. data/spec/grape/integration/rack_spec.rb +25 -8
  211. data/spec/grape/loading_spec.rb +9 -9
  212. data/spec/grape/middleware/auth/base_spec.rb +2 -1
  213. data/spec/grape/middleware/auth/dsl_spec.rb +19 -10
  214. data/spec/grape/middleware/auth/strategies_spec.rb +62 -22
  215. data/spec/grape/middleware/base_spec.rb +36 -17
  216. data/spec/grape/middleware/error_spec.rb +11 -4
  217. data/spec/grape/middleware/exception_spec.rb +112 -162
  218. data/spec/grape/middleware/formatter_spec.rb +65 -29
  219. data/spec/grape/middleware/globals_spec.rb +8 -5
  220. data/spec/grape/middleware/stack_spec.rb +25 -13
  221. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -2
  222. data/spec/grape/middleware/versioner/header_spec.rb +37 -14
  223. data/spec/grape/middleware/versioner/param_spec.rb +8 -2
  224. data/spec/grape/middleware/versioner/path_spec.rb +6 -2
  225. data/spec/grape/middleware/versioner_spec.rb +2 -2
  226. data/spec/grape/named_api_spec.rb +19 -0
  227. data/spec/grape/parser_spec.rb +10 -6
  228. data/spec/grape/path_spec.rb +53 -53
  229. data/spec/grape/presenters/presenter_spec.rb +8 -7
  230. data/spec/grape/request_spec.rb +29 -3
  231. data/spec/grape/util/inheritable_setting_spec.rb +9 -8
  232. data/spec/grape/util/inheritable_values_spec.rb +5 -3
  233. data/spec/grape/util/reverse_stackable_values_spec.rb +5 -2
  234. data/spec/grape/util/stackable_values_spec.rb +10 -7
  235. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -1
  236. data/spec/grape/validations/attributes_doc_spec.rb +153 -0
  237. data/spec/grape/validations/instance_behaivour_spec.rb +13 -14
  238. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +40 -0
  239. data/spec/grape/validations/params_scope_spec.rb +568 -99
  240. data/spec/grape/validations/single_attribute_iterator_spec.rb +57 -0
  241. data/spec/grape/validations/types/array_coercer_spec.rb +33 -0
  242. data/spec/grape/validations/types/primitive_coercer_spec.rb +150 -0
  243. data/spec/grape/validations/types/set_coercer_spec.rb +32 -0
  244. data/spec/grape/validations/types_spec.rb +44 -45
  245. data/spec/grape/validations/validators/all_or_none_spec.rb +134 -32
  246. data/spec/grape/validations/validators/allow_blank_spec.rb +137 -141
  247. data/spec/grape/validations/validators/at_least_one_of_spec.rb +169 -31
  248. data/spec/grape/validations/validators/coerce_spec.rb +491 -151
  249. data/spec/grape/validations/validators/default_spec.rb +242 -78
  250. data/spec/grape/validations/validators/exactly_one_of_spec.rb +198 -40
  251. data/spec/grape/validations/validators/except_values_spec.rb +6 -5
  252. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +181 -30
  253. data/spec/grape/validations/validators/presence_spec.rb +45 -2
  254. data/spec/grape/validations/validators/regexp_spec.rb +27 -33
  255. data/spec/grape/validations/validators/same_as_spec.rb +57 -0
  256. data/spec/grape/validations/validators/values_spec.rb +227 -180
  257. data/spec/grape/validations_spec.rb +502 -72
  258. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  259. data/spec/integration/multi_json/json_spec.rb +2 -2
  260. data/spec/integration/multi_xml/xml_spec.rb +2 -2
  261. data/spec/shared/versioning_examples.rb +34 -29
  262. data/spec/spec_helper.rb +31 -5
  263. data/spec/support/basic_auth_encode_helpers.rb +3 -1
  264. data/spec/support/chunks.rb +14 -0
  265. data/spec/support/content_type_helpers.rb +2 -0
  266. data/spec/support/endpoint_faker.rb +2 -0
  267. data/spec/support/file_streamer.rb +2 -0
  268. data/spec/support/integer_helpers.rb +2 -0
  269. data/spec/support/versioned_helpers.rb +8 -8
  270. metadata +111 -61
  271. data/Appraisals +0 -32
  272. data/Dangerfile +0 -2
  273. data/Gemfile +0 -33
  274. data/Gemfile.lock +0 -231
  275. data/Guardfile +0 -10
  276. data/RELEASING.md +0 -111
  277. data/Rakefile +0 -25
  278. data/benchmark/simple.rb +0 -27
  279. data/benchmark/simple_with_type_coercer.rb +0 -22
  280. data/gemfiles/multi_json.gemfile +0 -35
  281. data/gemfiles/multi_xml.gemfile +0 -35
  282. data/gemfiles/rack_1.5.2.gemfile +0 -35
  283. data/gemfiles/rack_edge.gemfile +0 -35
  284. data/gemfiles/rails_3.gemfile +0 -36
  285. data/gemfiles/rails_4.gemfile +0 -35
  286. data/gemfiles/rails_5.gemfile +0 -35
  287. data/gemfiles/rails_edge.gemfile +0 -35
  288. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  289. data/lib/grape/util/content_types.rb +0 -26
  290. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
  291. data/lib/grape/validations/validators/all_or_none.rb +0 -20
  292. data/lib/grape/validations/validators/allow_blank.rb +0 -16
  293. data/lib/grape/validations/validators/as.rb +0 -15
  294. data/lib/grape/validations/validators/at_least_one_of.rb +0 -20
  295. data/lib/grape/validations/validators/coerce.rb +0 -74
  296. data/lib/grape/validations/validators/default.rb +0 -48
  297. data/lib/grape/validations/validators/exactly_one_of.rb +0 -29
  298. data/lib/grape/validations/validators/except_values.rb +0 -20
  299. data/lib/grape/validations/validators/mutual_exclusion.rb +0 -25
  300. data/lib/grape/validations/validators/presence.rb +0 -10
  301. data/lib/grape/validations/validators/regexp.rb +0 -11
  302. data/lib/grape/validations/validators/values.rb +0 -71
  303. data/pkg/grape-0.17.0.gem +0 -0
  304. data/pkg/grape-0.19.0.gem +0 -0
  305. data/spec/grape/dsl/configuration_spec.rb +0 -14
  306. data/spec/grape/validations/attributes_iterator_spec.rb +0 -4
data/README.md CHANGED
@@ -1,12 +1,10 @@
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://travis-ci.org/ruby-grape/grape.svg?branch=master)](https://travis-ci.org/ruby-grape/grape)
5
- [![Dependency Status](https://gemnasium.com/ruby-grape/grape.svg)](https://gemnasium.com/ruby-grape/grape)
4
+ [![Build Status](https://github.com/ruby-grape/grape/workflows/test/badge.svg?branch=master)](https://github.com/ruby-grape/grape/actions)
6
5
  [![Code Climate](https://codeclimate.com/github/ruby-grape/grape.svg)](https://codeclimate.com/github/ruby-grape/grape)
7
6
  [![Coverage Status](https://coveralls.io/repos/github/ruby-grape/grape/badge.svg?branch=master)](https://coveralls.io/github/ruby-grape/grape?branch=master)
8
- [![Inline docs](http://inch-ci.org/github/ruby-grape/grape.svg)](http://inch-ci.org/github/ruby-grape/grape)
9
- [![git.legal](https://git.legal/projects/1364/badge.svg "Number of libraries approved")](https://git.legal/projects/1364)
7
+ [![Inline docs](https://inch-ci.org/github/ruby-grape/grape.svg)](https://inch-ci.org/github/ruby-grape/grape)
10
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)
11
9
 
12
10
  ## Table of Contents
@@ -14,62 +12,77 @@
14
12
  - [What is Grape?](#what-is-grape)
15
13
  - [Stable Release](#stable-release)
16
14
  - [Project Resources](#project-resources)
15
+ - [Grape for Enterprise](#grape-for-enterprise)
17
16
  - [Installation](#installation)
18
17
  - [Basic Usage](#basic-usage)
19
18
  - [Mounting](#mounting)
19
+ - [All](#all)
20
20
  - [Rack](#rack)
21
21
  - [ActiveRecord without Rails](#activerecord-without-rails)
22
+ - [Rails 4](#rails-4)
23
+ - [Rails 5+](#rails-5)
22
24
  - [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
23
25
  - [Rails](#rails)
26
+ - [Rails < 5.2](#rails--52)
27
+ - [Rails 6.0](#rails-60)
24
28
  - [Modules](#modules)
29
+ - [Remounting](#remounting)
30
+ - [Mount Configuration](#mount-configuration)
25
31
  - [Versioning](#versioning)
26
32
  - [Path](#path)
27
33
  - [Header](#header)
28
34
  - [Accept-Version Header](#accept-version-header)
29
35
  - [Param](#param)
30
36
  - [Describing Methods](#describing-methods)
37
+ - [Configuration](#configuration)
31
38
  - [Parameters](#parameters)
32
39
  - [Params Class](#params-class)
33
40
  - [Declared](#declared)
34
41
  - [Include Parent Namespaces](#include-parent-namespaces)
35
42
  - [Include Missing](#include-missing)
43
+ - [Evaluate Given](#evaluate-given)
36
44
  - [Parameter Validation and Coercion](#parameter-validation-and-coercion)
37
45
  - [Supported Parameter Types](#supported-parameter-types)
38
46
  - [Integer/Fixnum and Coercions](#integerfixnum-and-coercions)
39
47
  - [Custom Types and Coercions](#custom-types-and-coercions)
40
48
  - [Multipart File Parameters](#multipart-file-parameters)
41
- - [First-Class `JSON` Types](#first-class-json-types)
49
+ - [First-Class JSON Types](#first-class-json-types)
42
50
  - [Multiple Allowed Types](#multiple-allowed-types)
43
51
  - [Validation of Nested Parameters](#validation-of-nested-parameters)
44
52
  - [Dependent Parameters](#dependent-parameters)
45
53
  - [Group Options](#group-options)
46
- - [Alias](#alias)
54
+ - [Renaming](#renaming)
47
55
  - [Built-in Validators](#built-in-validators)
48
- - [`allow_blank`](#allow_blank)
49
- - [`values`](#values)
50
- - [`except_values`](#except_values)
51
- - [`regexp`](#regexp)
52
- - [`mutually_exclusive`](#mutually_exclusive)
53
- - [`exactly_one_of`](#exactly_one_of)
54
- - [`at_least_one_of`](#at_least_one_of)
55
- - [`all_or_none_of`](#all_or_none_of)
56
- - [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)
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)
57
66
  - [Namespace Validation and Coercion](#namespace-validation-and-coercion)
58
67
  - [Custom Validators](#custom-validators)
59
68
  - [Validation Errors](#validation-errors)
60
69
  - [I18n](#i18n)
61
70
  - [Custom Validation messages](#custom-validation-messages)
62
- - [`presence`, `allow_blank`, `values`, `regexp`](#presence-allow_blank-values-regexp)
63
- - [`all_or_none_of`](#all_or_none_of-1)
64
- - [`mutually_exclusive`](#mutually_exclusive-1)
65
- - [`exactly_one_of`](#exactly_one_of-1)
66
- - [`at_least_one_of`](#at_least_one_of-1)
67
- - [`Coerce`](#coerce)
68
- - [`With Lambdas`](#with-lambdas)
69
- - [`Pass symbols for i18n translations`](#pass-symbols-for-i18n-translations)
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)
70
80
  - [Overriding Attribute Names](#overriding-attribute-names)
71
81
  - [With Default](#with-default)
72
82
  - [Headers](#headers)
83
+ - [Request](#request)
84
+ - [Header Case Handling](#header-case-handling)
85
+ - [Response](#response)
73
86
  - [Routes](#routes)
74
87
  - [Helpers](#helpers)
75
88
  - [Path Helpers](#path-helpers)
@@ -105,7 +118,7 @@
105
118
  - [Register custom middleware for authentication](#register-custom-middleware-for-authentication)
106
119
  - [Describing and Inspecting an API](#describing-and-inspecting-an-api)
107
120
  - [Current Route and Endpoint](#current-route-and-endpoint)
108
- - [Before and After](#before-and-after)
121
+ - [Before, After and Finally](#before-after-and-finally)
109
122
  - [Anchoring](#anchoring)
110
123
  - [Using Custom Middleware](#using-custom-middleware)
111
124
  - [Grape Middleware](#grape-middleware)
@@ -132,6 +145,7 @@
132
145
  - [format_response.grape](#format_responsegrape)
133
146
  - [Monitoring Products](#monitoring-products)
134
147
  - [Contributing to Grape](#contributing-to-grape)
148
+ - [Security](#security)
135
149
  - [License](#license)
136
150
  - [Copyright](#copyright)
137
151
 
@@ -145,7 +159,7 @@ content negotiation, versioning and much more.
145
159
 
146
160
  ## Stable Release
147
161
 
148
- You're reading the documentation for the stable release of Grape, **1.1.0**.
162
+ You're reading the documentation for the stable release of Grape, 1.7.1.
149
163
  Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
150
164
 
151
165
  ## Project Resources
@@ -155,17 +169,19 @@ Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
155
169
  * Need help? Try [Grape Google Group](http://groups.google.com/group/ruby-grape) or [Gitter](https://gitter.im/ruby-grape/grape)
156
170
  * [Follow us on Twitter](https://twitter.com/grapeframework)
157
171
 
158
- ## Installation
172
+ ## Grape for Enterprise
173
+
174
+ Available as part of the Tidelift Subscription.
159
175
 
160
- Grape is available as a gem, to install it just install the gem:
176
+ The maintainers of Grape are working with Tidelift to deliver commercial support and maintenance. Save time, reduce risk, and improve code health, while paying the maintainers of Grape. Click [here](https://tidelift.com/subscription/request-a-demo?utm_source=rubygems-grape&utm_medium=referral&utm_campaign=enterprise) for more details.
161
177
 
162
- gem install grape
178
+ ## Installation
163
179
 
164
- If you're using Bundler, add the gem to Gemfile.
180
+ Ruby 2.4 or newer is required.
165
181
 
166
- gem 'grape'
182
+ Grape is available as a gem, to install it run:
167
183
 
168
- Run `bundle install`.
184
+ bundle add grape
169
185
 
170
186
  ## Basic Usage
171
187
 
@@ -204,7 +220,7 @@ module Twitter
204
220
 
205
221
  desc 'Return a status.'
206
222
  params do
207
- requires :id, type: Integer, desc: 'Status id.'
223
+ requires :id, type: Integer, desc: 'Status ID.'
208
224
  end
209
225
  route_param :id do
210
226
  get do
@@ -252,6 +268,17 @@ end
252
268
 
253
269
  ## Mounting
254
270
 
271
+ ### All
272
+
273
+
274
+ By default Grape will compile the routes on the first route, it is possible to pre-load routes using the `compile!` method.
275
+
276
+ ```ruby
277
+ Twitter::API.compile!
278
+ ```
279
+
280
+ This can be added to your `config.ru` (if using rackup), `application.rb` (if using rails), or any file that loads your server.
281
+
255
282
  ### Rack
256
283
 
257
284
  The above sample creates a Rack application that can be run from a rackup `config.ru` file
@@ -261,6 +288,13 @@ with `rackup`:
261
288
  run Twitter::API
262
289
  ```
263
290
 
291
+ (With pre-loading you can use)
292
+
293
+ ```ruby
294
+ Twitter::API.compile!
295
+ run Twitter::API
296
+ ```
297
+
264
298
  And would respond to the following routes:
265
299
 
266
300
  GET /api/statuses/public_timeline
@@ -277,13 +311,21 @@ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just
277
311
  If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
278
312
  is handled correctly.
279
313
 
314
+ #### Rails 4
315
+
280
316
  The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
281
317
  `config.ru` before mounting Grape, e.g.:
282
318
 
283
319
  ```ruby
284
320
  use ActiveRecord::ConnectionAdapters::ConnectionManagement
321
+ ```
285
322
 
286
- run Twitter::API
323
+ #### Rails 5+
324
+
325
+ Use [otr-activerecord](https://github.com/jhollinger/otr-activerecord) as follows:
326
+
327
+ ```ruby
328
+ use OTR::ActiveRecord::ConnectionManagement
287
329
  ```
288
330
 
289
331
  ### Alongside Sinatra (or other frameworks)
@@ -310,13 +352,24 @@ class Web < Sinatra::Base
310
352
  end
311
353
 
312
354
  use Rack::Session::Cookie
313
- run Rack::Cascade.new [API, Web]
355
+ run Rack::Cascade.new [Web, API]
314
356
  ```
315
357
 
358
+ Note that order of loading apps using `Rack::Cascade` matters. The grape application must be last if you want to raise custom 404 errors from grape (such as `error!('Not Found',404)`). If the grape application is not last and returns 404 or 405 response, [cascade utilizes that as a signal to try the next app](https://www.rubydoc.info/gems/rack/Rack/Cascade). This may lead to undesirable behavior showing the [wrong 404 page from the wrong app](https://github.com/ruby-grape/grape/issues/1515).
359
+
360
+
316
361
  ### Rails
317
362
 
318
363
  Place API files into `app/api`. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for `Twitter::API` should be `app/api/twitter/api.rb`.
319
364
 
365
+ Modify `config/routes`:
366
+
367
+ ```ruby
368
+ mount Twitter::API => '/'
369
+ ```
370
+
371
+ #### Rails < 5.2
372
+
320
373
  Modify `application.rb`:
321
374
 
322
375
  ```ruby
@@ -324,14 +377,18 @@ config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
324
377
  config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
325
378
  ```
326
379
 
327
- Modify `config/routes`:
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:
328
385
 
329
386
  ```ruby
330
- mount Twitter::API => '/'
387
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
388
+ inflect.acronym 'API'
389
+ end
331
390
  ```
332
391
 
333
- See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
334
-
335
392
  ### Modules
336
393
 
337
394
  You can mount multiple API implementations inside another one. These don't have to be
@@ -365,6 +422,131 @@ class Twitter::API < Grape::API
365
422
  end
366
423
  ```
367
424
 
425
+ ## Remounting
426
+
427
+ You can mount the same endpoints in two different locations.
428
+
429
+ ```ruby
430
+ class Voting::API < Grape::API
431
+ namespace 'votes' do
432
+ get do
433
+ # Your logic
434
+ end
435
+
436
+ post do
437
+ # Your logic
438
+ end
439
+ end
440
+ end
441
+
442
+ class Post::API < Grape::API
443
+ mount Voting::API
444
+ end
445
+
446
+ class Comment::API < Grape::API
447
+ mount Voting::API
448
+ end
449
+ ```
450
+
451
+ Assuming that the post and comment endpoints are mounted in `/posts` and `/comments`, you should now be able to do `get /posts/votes`, `post /posts/votes`, `get /comments/votes` and `post /comments/votes`.
452
+
453
+ ### Mount Configuration
454
+
455
+ You can configure remountable endpoints to change how they behave according to where they are mounted.
456
+
457
+ ```ruby
458
+ class Voting::API < Grape::API
459
+ namespace 'votes' do
460
+ desc "Vote for your #{configuration[:votable]}"
461
+ get do
462
+ # Your logic
463
+ end
464
+ end
465
+ end
466
+
467
+ class Post::API < Grape::API
468
+ mount Voting::API, with: { votable: 'posts' }
469
+ end
470
+
471
+ class Comment::API < Grape::API
472
+ mount Voting::API, with: { votable: 'comments' }
473
+ end
474
+ ```
475
+
476
+ Note that if you're passing a hash as the first parameter to `mount`, you will need to explicitly put `()` around parameters:
477
+ ```ruby
478
+ # good
479
+ mount({ ::Some::Api => '/some/api' }, with: { condition: true })
480
+
481
+ # bad
482
+ mount ::Some::Api => '/some/api', with: { condition: true }
483
+ ```
484
+
485
+ You can access `configuration` on the class (to use as dynamic attributes), inside blocks (like namespace)
486
+
487
+ If you want logic happening given on an `configuration`, you can use the helper `given`.
488
+
489
+ ```ruby
490
+ class ConditionalEndpoint::API < Grape::API
491
+ given configuration[:some_setting] do
492
+ get 'mount_this_endpoint_conditionally' do
493
+ configuration[:configurable_response]
494
+ end
495
+ end
496
+ end
497
+ ```
498
+
499
+ If you want a block of logic running every time an endpoint is mounted (within which you can access the `configuration` Hash)
500
+
501
+
502
+ ```ruby
503
+ class ConditionalEndpoint::API < Grape::API
504
+ mounted do
505
+ YourLogger.info "This API was mounted at: #{Time.now}"
506
+
507
+ get configuration[:endpoint_name] do
508
+ configuration[:configurable_response]
509
+ end
510
+ end
511
+ end
512
+ ```
513
+
514
+ More complex results can be achieved by using `mounted` as an expression within which the `configuration` is already evaluated as a Hash.
515
+
516
+ ```ruby
517
+ class ExpressionEndpointAPI < Grape::API
518
+ get(mounted { configuration[:route_name] || 'default_name' }) do
519
+ # some logic
520
+ end
521
+ end
522
+ ```
523
+
524
+ ```ruby
525
+ class BasicAPI < Grape::API
526
+ desc 'Statuses index' do
527
+ params: mounted { configuration[:entity] || API::Entities::Status }.documentation
528
+ end
529
+ params do
530
+ requires :all, using: mounted { configuration[:entity] || API::Entities::Status }.documentation
531
+ end
532
+ get '/statuses' do
533
+ statuses = Status.all
534
+ type = current_user.admin? ? :full : :default
535
+ present statuses, with: mounted { configuration[:entity] || API::Entities::Status }, type: type
536
+ end
537
+ end
538
+
539
+ class V1 < Grape::API
540
+ version 'v1'
541
+ mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Entities::Status } }
542
+ end
543
+
544
+ class V2 < Grape::API
545
+ version 'v2'
546
+ mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Entities::V2::Status } }
547
+ end
548
+ ```
549
+
368
550
  ## Versioning
369
551
 
370
552
  There are four strategies in which clients can reach your API's endpoints: `:path`,
@@ -445,14 +627,18 @@ version 'v1', using: :param, parameter: 'v'
445
627
 
446
628
  ## Describing Methods
447
629
 
448
- You can add a description to API methods and namespaces.
630
+ 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
+
632
+ Note: Description block is only for documentation and won't affects API behavior.
449
633
 
450
634
  ```ruby
451
635
  desc 'Returns your public timeline.' do
636
+ summary 'summary'
452
637
  detail 'more details'
453
638
  params API::Entities::Status.documentation
454
639
  success API::Entities::Entity
455
640
  failure [[401, 'Unauthorized', 'Entities::Error']]
641
+ default { code: 500, message: 'InvalidRequest', model: Entities::Error }
456
642
  named 'My named route'
457
643
  headers XAuthToken: {
458
644
  description: 'Validates your identity',
@@ -462,7 +648,13 @@ desc 'Returns your public timeline.' do
462
648
  description: 'Not really needed',
463
649
  required: false
464
650
  }
465
-
651
+ hidden false
652
+ deprecated false
653
+ is_array true
654
+ nickname 'nickname'
655
+ produces ['application/json']
656
+ consumes ['application/json']
657
+ tags ['tag1', 'tag2']
466
658
  end
467
659
  get :public_timeline do
468
660
  Status.limit(20)
@@ -471,10 +663,48 @@ end
471
663
 
472
664
  * `detail`: A more enhanced description
473
665
  * `params`: Define parameters directly from an `Entity`
474
- * `success`: (former entity) The `Entity` to be used to present by default this route
475
- * `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities
666
+ * `success`: (former entity) The `Entity` to be used to present the success response for this route.
667
+ * `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.
476
669
  * `named`: A helper to give a route a name and find it with this name in the documentation Hash
477
670
  * `headers`: A definition of the used Headers
671
+ * Other options can be found in [grape-swagger][grape-swagger]
672
+
673
+ [grape-swagger]: https://github.com/ruby-grape/grape-swagger
674
+
675
+ ## Configuration
676
+
677
+ Use `Grape.configure` to set up global settings at load time.
678
+ Currently the configurable settings are:
679
+
680
+ * `param_builder`: Sets the [Parameter Builder](#parameters), defaults to `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder`.
681
+
682
+ To change a setting value make sure that at some point during load time the following code runs
683
+
684
+ ```ruby
685
+ Grape.configure do |config|
686
+ config.setting = value
687
+ end
688
+ ```
689
+
690
+ For example, for the `param_builder`, the following code could run in an initializer:
691
+
692
+ ```ruby
693
+ Grape.configure do |config|
694
+ config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
695
+ end
696
+ ```
697
+
698
+ You can also configure a single API:
699
+
700
+ ```ruby
701
+ API.configure do |config|
702
+ config[key] = value
703
+ end
704
+ ```
705
+
706
+ This will be available inside the API with `configuration`, as if it were
707
+ [mount configuration](#mount-configuration).
478
708
 
479
709
  ## Parameters
480
710
 
@@ -553,13 +783,21 @@ params do
553
783
  end
554
784
  ```
555
785
 
786
+ Or globally with the [Configuration](#configuration) `Grape.configure.param_builder`.
787
+
556
788
  In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.
557
789
 
558
790
  Available parameter builders are `Grape::Extensions::Hash::ParamBuilder`, `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder` and `Grape::Extensions::Hashie::Mash::ParamBuilder`.
559
791
 
560
792
  ### Declared
561
793
 
562
- Grape allows you to access only the parameters that have been declared by your `params` block. It filters out the params that have been passed, but are not allowed. Consider the following API endpoint:
794
+ Grape allows you to access only the parameters that have been declared by your `params` block. It will:
795
+
796
+ * Filter out the params that have been passed, but are not allowed.
797
+ * Include any optional params that are declared but not passed.
798
+ * Perform any parameter renaming on the resulting hash.
799
+
800
+ Consider the following API endpoint:
563
801
 
564
802
  ````ruby
565
803
  format :json
@@ -592,9 +830,9 @@ Once we add parameters requirements, grape will start returning only the declare
592
830
  format :json
593
831
 
594
832
  params do
595
- requires :user, type: Hash do
596
- requires :first_name, type: String
597
- requires :last_name, type: String
833
+ optional :user, type: Hash do
834
+ optional :first_name, type: String
835
+ optional :last_name, type: String
598
836
  end
599
837
  end
600
838
 
@@ -622,6 +860,44 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
622
860
  }
623
861
  ````
624
862
 
863
+ Missing params that are declared as type `Hash` or `Array` will be included.
864
+
865
+ ````ruby
866
+ format :json
867
+
868
+ params do
869
+ optional :user, type: Hash do
870
+ optional :first_name, type: String
871
+ optional :last_name, type: String
872
+ end
873
+ optional :widgets, type: Array
874
+ end
875
+
876
+ post 'users/signup' do
877
+ { 'declared_params' => declared(params) }
878
+ end
879
+ ````
880
+
881
+ **Request**
882
+
883
+ ````bash
884
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{}'
885
+ ````
886
+
887
+ **Response**
888
+
889
+ ````json
890
+ {
891
+ "declared_params": {
892
+ "user": {
893
+ "first_name": null,
894
+ "last_name": null
895
+ },
896
+ "widgets": []
897
+ }
898
+ }
899
+ ````
900
+
625
901
  The returned hash is an `ActiveSupport::HashWithIndifferentAccess`.
626
902
 
627
903
  The `#declared` method is not available to `before` filters, as those are evaluated prior to parameter coercion.
@@ -680,8 +956,10 @@ By default `declared(params)` includes parameters that have `nil` values. If you
680
956
  format :json
681
957
 
682
958
  params do
683
- requires :first_name, type: String
684
- optional :last_name, type: String
959
+ requires :user, type: Hash do
960
+ requires :first_name, type: String
961
+ optional :last_name, type: String
962
+ end
685
963
  end
686
964
 
687
965
  post 'users/signup' do
@@ -712,8 +990,10 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
712
990
  ````json
713
991
  {
714
992
  "declared_params": {
715
- "first_name": "first name",
716
- "last_name": null
993
+ "user": {
994
+ "first_name": "first name",
995
+ "last_name": null
996
+ }
717
997
  }
718
998
  }
719
999
  ````
@@ -800,6 +1080,102 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
800
1080
  }
801
1081
  ````
802
1082
 
1083
+ ### Evaluate Given
1084
+
1085
+ By default `declared(params)` will not evaluate `given` and return all parameters. Use `evaluate_given` to evaluate all `given` blocks and return only parameters that satisfy `given` conditions. Consider the following API:
1086
+
1087
+ ````ruby
1088
+ format :json
1089
+
1090
+ params do
1091
+ optional :child_id, type: Integer
1092
+ given :child_id do
1093
+ requires :father_id, type: Integer
1094
+ end
1095
+ end
1096
+
1097
+ post 'child' do
1098
+ { 'declared_params' => declared(params, evaluate_given: true) }
1099
+ end
1100
+ ````
1101
+
1102
+ **Request**
1103
+
1104
+ ````bash
1105
+ curl -X POST -H "Content-Type: application/json" localhost:9292/child -d '{"father_id": 1}'
1106
+ ````
1107
+
1108
+ **Response with evaluate_given:false**
1109
+
1110
+ ````json
1111
+ {
1112
+ "declared_params": {
1113
+ "child_id": null,
1114
+ "father_id": 1
1115
+ }
1116
+ }
1117
+ ````
1118
+
1119
+ **Response with evaluate_given:true**
1120
+
1121
+ ````json
1122
+ {
1123
+ "declared_params": {
1124
+ "child_id": null
1125
+ }
1126
+ }
1127
+ ````
1128
+
1129
+ It also works on nested hashes:
1130
+
1131
+ ````ruby
1132
+ format :json
1133
+
1134
+ params do
1135
+ requires :child, type: Hash do
1136
+ optional :child_id, type: Integer
1137
+ given :child_id do
1138
+ requires :father_id, type: Integer
1139
+ end
1140
+ end
1141
+ end
1142
+
1143
+ post 'child' do
1144
+ { 'declared_params' => declared(params, evaluate_given: true) }
1145
+ end
1146
+ ````
1147
+
1148
+ **Request**
1149
+
1150
+ ````bash
1151
+ curl -X POST -H "Content-Type: application/json" localhost:9292/child -d '{"child": {"father_id": 1}}'
1152
+ ````
1153
+
1154
+ **Response with evaluate_given:false**
1155
+
1156
+ ````json
1157
+ {
1158
+ "declared_params": {
1159
+ "child": {
1160
+ "child_id": null,
1161
+ "father_id": 1
1162
+ }
1163
+ }
1164
+ }
1165
+ ````
1166
+
1167
+ **Response with evaluate_given:true**
1168
+
1169
+ ````json
1170
+ {
1171
+ "declared_params": {
1172
+ "child": {
1173
+ "child_id": null
1174
+ }
1175
+ }
1176
+ }
1177
+ ````
1178
+
803
1179
  ## Parameter Validation and Coercion
804
1180
 
805
1181
  You can define validations and coercion options for your parameters using a `params` block.
@@ -834,13 +1210,13 @@ params do
834
1210
  end
835
1211
  ```
836
1212
 
837
- Note that default values will be passed through to any validation options specified.
838
- The following example will always fail if `:color` is not explicitly provided.
839
-
840
1213
  Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
841
1214
  number for each call to the endpoint of this `params` block. To have the default evaluate
842
1215
  lazily with each request use a lambda, like `:random_number` above.
843
1216
 
1217
+ Note that default values will be passed through to any validation options specified.
1218
+ The following example will always fail if `:color` is not explicitly provided.
1219
+
844
1220
  ```ruby
845
1221
  params do
846
1222
  optional :color, type: String, default: 'blue', values: ['red', 'green']
@@ -900,7 +1276,8 @@ Aside from the default set of supported types listed above, any class can be
900
1276
  used as a type as long as an explicit coercion method is supplied. If the type
901
1277
  implements a class-level `parse` method, Grape will use it automatically.
902
1278
  This method must take one string argument and return an instance of the correct
903
- type, or raise an exception to indicate the value was invalid. E.g.,
1279
+ type, or return an instance of `Grape::Types::InvalidValue` which optionally
1280
+ accepts a message to be returned in the response.
904
1281
 
905
1282
  ```ruby
906
1283
  class Color
@@ -910,8 +1287,9 @@ class Color
910
1287
  end
911
1288
 
912
1289
  def self.parse(value)
913
- fail 'Invalid color' unless %w(blue red green).include?(value)
914
- new(value)
1290
+ return new(value) if %w[blue red green]).include?(value)
1291
+
1292
+ Grape::Types::InvalidValue.new('Unsupported color')
915
1293
  end
916
1294
  end
917
1295
 
@@ -934,7 +1312,7 @@ parameter, and the return value must match the given `type`.
934
1312
 
935
1313
  ```ruby
936
1314
  params do
937
- requires :passwd, type: String, coerce_with: Base64.method(:decode)
1315
+ requires :passwd, type: String, coerce_with: Base64.method(:decode64)
938
1316
  requires :loud_color, type: Color, coerce_with: ->(c) { Color.parse(c.downcase) }
939
1317
 
940
1318
  requires :obj, type: Hash, coerce_with: JSON do
@@ -943,6 +1321,7 @@ params do
943
1321
  end
944
1322
  end
945
1323
  ```
1324
+ Note that, a `nil` value will call the custom coercion method, while a missing parameter will not.
946
1325
 
947
1326
  Example of use of `coerce_with` with a lambda (a class with a `parse` method could also have been used)
948
1327
  It will parse a string and return an Array of Integers, matching the `Array[Integer]` `type`.
@@ -1109,6 +1488,18 @@ params do
1109
1488
  end
1110
1489
  ```
1111
1490
 
1491
+ You can rename parameters:
1492
+
1493
+ ```ruby
1494
+ params do
1495
+ optional :category, as: :type
1496
+ given type: ->(val) { val == 'foo' } do
1497
+ requires :description
1498
+ end
1499
+ end
1500
+ ```
1501
+
1502
+ Note: param in `given` should be the renamed one. In the example, it should be `type`, not `category`.
1112
1503
 
1113
1504
  ### Group Options
1114
1505
 
@@ -1137,9 +1528,9 @@ params do
1137
1528
  end
1138
1529
  ```
1139
1530
 
1140
- ### Alias
1531
+ ### Renaming
1141
1532
 
1142
- You can set an alias for parameters using `as`, which can be useful when refactoring existing APIs:
1533
+ You can rename parameters using `as`, which can be useful when refactoring existing APIs:
1143
1534
 
1144
1535
  ```ruby
1145
1536
  resource :users do
@@ -1153,7 +1544,7 @@ resource :users do
1153
1544
  end
1154
1545
  ```
1155
1546
 
1156
- The value passed to `as` will be the key when calling `params` or `declared(params)`.
1547
+ The value passed to `as` will be the key when calling `declared(params)`.
1157
1548
 
1158
1549
  ### Built-in Validators
1159
1550
 
@@ -1197,6 +1588,15 @@ params do
1197
1588
  end
1198
1589
  ```
1199
1590
 
1591
+ Note endless ranges are also supported with ActiveSupport >= 6.0, but they require that the type be provided.
1592
+
1593
+ ```ruby
1594
+ params do
1595
+ requires :minimum, type: Integer, values: 10..
1596
+ optional :maximum, type: Integer, values: ..10
1597
+ end
1598
+ ```
1599
+
1200
1600
  Note that *both* range endpoints have to be a `#kind_of?` your `:type` option (if you don't supply the `:type` option, it will be guessed to be equal to the class of the range's first endpoint). So the following is invalid:
1201
1601
 
1202
1602
  ```ruby
@@ -1232,6 +1632,14 @@ end
1232
1632
 
1233
1633
  While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
1234
1634
 
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`.
1636
+
1637
+ ```ruby
1638
+ params do
1639
+ requires :state, type: Symbol, values: [:active, :inactive]
1640
+ end
1641
+ ```
1642
+
1235
1643
  #### `except_values`
1236
1644
 
1237
1645
  Parameters can be restricted from having a specific set of values with the `:except_values` option.
@@ -1248,6 +1656,17 @@ params do
1248
1656
  end
1249
1657
  ```
1250
1658
 
1659
+ #### `same_as`
1660
+
1661
+ A `same_as` option can be given to ensure that values of parameters match.
1662
+
1663
+ ```ruby
1664
+ params do
1665
+ requires :password
1666
+ requires :password_confirmation, same_as: :password
1667
+ end
1668
+ ```
1669
+
1251
1670
  #### `regexp`
1252
1671
 
1253
1672
  Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
@@ -1423,10 +1842,10 @@ end
1423
1842
  ### Custom Validators
1424
1843
 
1425
1844
  ```ruby
1426
- class AlphaNumeric < Grape::Validations::Base
1845
+ class AlphaNumeric < Grape::Validations::Validators::Base
1427
1846
  def validate_param!(attr_name, params)
1428
1847
  unless params[attr_name] =~ /\A[[:alnum:]]+\z/
1429
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: 'must consist of alpha-numeric characters'
1848
+ raise Grape::Exceptions::Validation.new params: [@scope.full_name(attr_name)], message: 'must consist of alpha-numeric characters'
1430
1849
  end
1431
1850
  end
1432
1851
  end
@@ -1441,10 +1860,10 @@ end
1441
1860
  You can also create custom classes that take parameters.
1442
1861
 
1443
1862
  ```ruby
1444
- class Length < Grape::Validations::Base
1863
+ class Length < Grape::Validations::Validators::Base
1445
1864
  def validate_param!(attr_name, params)
1446
1865
  unless params[attr_name].length <= @option
1447
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
1866
+ raise Grape::Exceptions::Validation.new params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
1448
1867
  end
1449
1868
  end
1450
1869
  end
@@ -1459,7 +1878,7 @@ end
1459
1878
  You can also create custom validation that use request to validate the attribute. For example if you want to have parameters that are available to only admins, you can do the following.
1460
1879
 
1461
1880
  ```ruby
1462
- class Admin < Grape::Validations::Base
1881
+ class Admin < Grape::Validations::Validators::Base
1463
1882
  def validate(request)
1464
1883
  # return if the param we are checking was not in request
1465
1884
  # @attrs is a list containing the attribute we are currently validating
@@ -1470,7 +1889,7 @@ class Admin < Grape::Validations::Base
1470
1889
  return unless @option
1471
1890
  # check if user is admin or not
1472
1891
  # as an example get a token from request and check if it's admin or not
1473
- fail Grape::Exceptions::Validation, params: @attrs, message: 'Can not set admin-only field.' unless request.headers['X-Access-Token'] == 'admin'
1892
+ raise Grape::Exceptions::Validation.new params: @attrs, message: 'Can not set admin-only field.' unless request.headers['X-Access-Token'] == 'admin'
1474
1893
  end
1475
1894
  end
1476
1895
  ```
@@ -1485,7 +1904,7 @@ params do
1485
1904
  end
1486
1905
  ```
1487
1906
 
1488
- Every validation will have it's own instance of the validator, which means that the validator can have a state.
1907
+ Every validation will have its own instance of the validator, which means that the validator can have a state.
1489
1908
 
1490
1909
  ### Validation Errors
1491
1910
 
@@ -1546,6 +1965,8 @@ end
1546
1965
  Grape supports I18n for parameter-related error messages, but will fallback to English if
1547
1966
  translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
1548
1967
 
1968
+ 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
+
1549
1970
  ### Custom Validation messages
1550
1971
 
1551
1972
  Grape supports custom validation messages for parameter-related and coerce-related error messages.
@@ -1558,6 +1979,15 @@ params do
1558
1979
  end
1559
1980
  ```
1560
1981
 
1982
+ #### `same_as`
1983
+
1984
+ ```ruby
1985
+ params do
1986
+ requires :password
1987
+ requires :password_confirmation, same_as: { value: :password, message: 'not match' }
1988
+ end
1989
+ ```
1990
+
1561
1991
  #### `all_or_none_of`
1562
1992
 
1563
1993
  ```ruby
@@ -1587,7 +2017,7 @@ params do
1587
2017
  optional :beer
1588
2018
  optional :wine
1589
2019
  optional :juice
1590
- exactly_one_of :beer, :wine, :juice, message: {exactly_one: "are missing, exactly one parameter is required", mutual_exclusion: "are mutually exclusive, exactly one parameter is required"}
2020
+ exactly_one_of :beer, :wine, :juice, message: { exactly_one: "are missing, exactly one parameter is required", mutual_exclusion: "are mutually exclusive, exactly one parameter is required" }
1591
2021
  end
1592
2022
  ```
1593
2023
 
@@ -1606,7 +2036,7 @@ end
1606
2036
 
1607
2037
  ```ruby
1608
2038
  params do
1609
- requires :int, type: {value: Integer, message: "type cast is invalid" }
2039
+ requires :int, type: { value: Integer, message: "type cast is invalid" }
1610
2040
  end
1611
2041
  ```
1612
2042
 
@@ -1668,6 +2098,7 @@ end
1668
2098
 
1669
2099
  ## Headers
1670
2100
 
2101
+ ### Request
1671
2102
  Request headers are available through the `headers` helper or from `env` in their original form.
1672
2103
 
1673
2104
  ```ruby
@@ -1682,13 +2113,30 @@ get do
1682
2113
  end
1683
2114
  ```
1684
2115
 
2116
+ #### Header Case Handling
2117
+
2118
+ The above example may have been requested as follows:
2119
+
2120
+ ``` shell
2121
+ curl -H "secret_PassWord: swordfish" ...
2122
+ ```
2123
+
2124
+ The header name will have been normalized for you.
2125
+
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_'.
2128
+
2129
+ 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
+
2131
+ ### Response
2132
+
1685
2133
  You can set a response header with `header` inside an API.
1686
2134
 
1687
2135
  ```ruby
1688
2136
  header 'X-Robots-Tag', 'noindex'
1689
2137
  ```
1690
2138
 
1691
- When raising `error!`, pass additional headers as arguments.
2139
+ When raising `error!`, pass additional headers as arguments. Additional headers will be merged with headers set before `error!` call.
1692
2140
 
1693
2141
  ```ruby
1694
2142
  error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
@@ -1696,6 +2144,56 @@ error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
1696
2144
 
1697
2145
  ## Routes
1698
2146
 
2147
+ To define routes you can use the `route` method or the shorthands for the HTTP verbs. To define a route that accepts any route set to `:any`.
2148
+ Parts of the path that are denoted with a colon will be interpreted as route parameters.
2149
+
2150
+ ```ruby
2151
+ route :get, 'status' do
2152
+ end
2153
+
2154
+ # is the same as
2155
+
2156
+ get 'status' do
2157
+ end
2158
+
2159
+ # is the same as
2160
+
2161
+ get :status do
2162
+ end
2163
+
2164
+ # is NOT the same as
2165
+
2166
+ get ':status' do # this makes params[:status] available
2167
+ end
2168
+
2169
+ # This will make both params[:status_id] and params[:id] available
2170
+
2171
+ get 'statuses/:status_id/reviews/:id' do
2172
+ end
2173
+ ```
2174
+
2175
+ To declare a namespace that prefixes all routes within, use the `namespace` method. `group`, `resource`, `resources` and `segment` are aliases to this method. Any endpoints within will share their parent context as well as any configuration done in the namespace context.
2176
+
2177
+ The `route_param` method is a convenient method for defining a parameter route segment. If you define a type, it will add a validation for this parameter.
2178
+
2179
+ ```ruby
2180
+ route_param :id, type: Integer do
2181
+ get 'status' do
2182
+ end
2183
+ end
2184
+
2185
+ # is the same as
2186
+
2187
+ namespace ':id' do
2188
+ params do
2189
+ requires :id, type: Integer
2190
+ end
2191
+
2192
+ get 'status' do
2193
+ end
2194
+ end
2195
+ ```
2196
+
1699
2197
  Optionally, you can define requirements for your named route parameters using regular
1700
2198
  expressions on namespace or endpoint. The route will match only if all requirements are met.
1701
2199
 
@@ -1850,6 +2348,18 @@ params do
1850
2348
  end
1851
2349
  ```
1852
2350
 
2351
+ If documentation isn't needed (for instance, it is an internal API), documentation can be disabled.
2352
+
2353
+ ```ruby
2354
+ class API < Grape::API
2355
+ do_not_document!
2356
+
2357
+ # endpoints...
2358
+ end
2359
+ ```
2360
+
2361
+ In this case, Grape won't create objects related to documentation which are retained in RAM forever.
2362
+
1853
2363
  ## Cookies
1854
2364
 
1855
2365
  You can set, get and delete your cookies very simply using `cookies` method.
@@ -2028,6 +2538,12 @@ instead of a message.
2028
2538
  error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
2029
2539
  ```
2030
2540
 
2541
+ You can set additional headers for the response. They will be merged with headers set before `error!` call.
2542
+
2543
+ ```ruby
2544
+ error!('Something went wrong', 500, 'X-Error-Detail' => 'Invalid token.')
2545
+ ```
2546
+
2031
2547
  You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
2032
2548
 
2033
2549
  ```ruby
@@ -2182,7 +2698,7 @@ You can also rescue all exceptions with a code block and handle the Rack respons
2182
2698
  ```ruby
2183
2699
  class Twitter::API < Grape::API
2184
2700
  rescue_from :all do |e|
2185
- Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
2701
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
2186
2702
  end
2187
2703
  end
2188
2704
  ```
@@ -2253,9 +2769,9 @@ class Twitter::API < Grape::API
2253
2769
  end
2254
2770
  ```
2255
2771
 
2256
- The `rescue_from` block must return a `Rack::Response` object, call `error!` or re-raise an exception.
2772
+ The `rescue_from` handler must return a `Rack::Response` object, call `error!`, or raise an exception (either the original exception or another custom one). The exception raised in `rescue_from` will be handled outside Grape. For example, if you mount Grape in Rails, the exception will be handle by [Rails Action Controller](https://guides.rubyonrails.org/action_controller_overview.html#rescue).
2257
2773
 
2258
- The `with` keyword is available as `rescue_from` options, it can be passed method name or Proc object.
2774
+ Alternately, use the `with` option in `rescue_from` to specify a method or a `proc`.
2259
2775
 
2260
2776
  ```ruby
2261
2777
  class Twitter::API < Grape::API
@@ -2271,6 +2787,17 @@ class Twitter::API < Grape::API
2271
2787
  end
2272
2788
  ```
2273
2789
 
2790
+ Inside the `rescue_from` block, the environment of the original controller method(`.self` receiver) is accessible through the `#context` method.
2791
+
2792
+ ```ruby
2793
+ class Twitter::API < Grape::API
2794
+ rescue_from :all do |e|
2795
+ user_id = context.params[:user_id]
2796
+ error!("error for #{user_id}")
2797
+ end
2798
+ end
2799
+ ```
2800
+
2274
2801
  #### Rescuing exceptions inside namespaces
2275
2802
 
2276
2803
  You could put `rescue_from` clauses inside a namespace and they will take precedence over ones
@@ -2293,7 +2820,7 @@ class Twitter::API < Grape::API
2293
2820
  end
2294
2821
  ```
2295
2822
 
2296
- Here `'inner'` will be result of handling occured `ArgumentError`.
2823
+ Here `'inner'` will be result of handling occurred `ArgumentError`.
2297
2824
 
2298
2825
  #### Unrescuable Exceptions
2299
2826
 
@@ -2824,17 +3351,30 @@ end
2824
3351
 
2825
3352
  Use `body false` to return `204 No Content` without any data or content-type.
2826
3353
 
2827
- You can also set the response to a file with `file`.
3354
+ If you want to empty the body with an HTTP status code other than `204 No Content`, you can override the status code after specifying `body false` as follows
3355
+
3356
+ ```ruby
3357
+ class API < Grape::API
3358
+ get '/' do
3359
+ body false
3360
+ status 304
3361
+ end
3362
+ end
3363
+ ```
3364
+
3365
+ You can also set the response to a file with `sendfile`. This works with the
3366
+ [Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send
3367
+ the file through your web server software.
2828
3368
 
2829
3369
  ```ruby
2830
3370
  class API < Grape::API
2831
3371
  get '/' do
2832
- file '/path/to/file'
3372
+ sendfile '/path/to/file'
2833
3373
  end
2834
3374
  end
2835
3375
  ```
2836
3376
 
2837
- If you want a file to be streamed using Rack::Chunked, use `stream`.
3377
+ To stream a file in chunks use `stream`
2838
3378
 
2839
3379
  ```ruby
2840
3380
  class API < Grape::API
@@ -2844,6 +3384,26 @@ class API < Grape::API
2844
3384
  end
2845
3385
  ```
2846
3386
 
3387
+ If you want to stream non-file data use the `stream` method and a `Stream` object.
3388
+ This is an object that responds to `each` and yields for each chunk to send to the client.
3389
+ Each chunk will be sent as it is yielded instead of waiting for all of the content to be available.
3390
+
3391
+ ```ruby
3392
+ class MyStream
3393
+ def each
3394
+ yield 'part 1'
3395
+ yield 'part 2'
3396
+ yield 'part 3'
3397
+ end
3398
+ end
3399
+
3400
+ class API < Grape::API
3401
+ get '/' do
3402
+ stream MyStream.new
3403
+ end
3404
+ end
3405
+ ```
3406
+
2847
3407
  ## Authentication
2848
3408
 
2849
3409
  ### Basic and Digest Auth
@@ -2855,14 +3415,21 @@ applies to the current namespace and any children, but not parents.
2855
3415
  ```ruby
2856
3416
  http_basic do |username, password|
2857
3417
  # verify user's password here
2858
- { 'test' => 'password1' }[username] == password
3418
+ # IMPORTANT: make sure you use a comparison method which isn't prone to a timing attack
2859
3419
  end
2860
3420
  ```
2861
3421
 
3422
+ Digest auth supports clear-text passwords and password hashes.
3423
+
2862
3424
  ```ruby
2863
3425
  http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
2864
3426
  # lookup the user's password here
2865
- { 'user1' => 'password1' }[username]
3427
+ end
3428
+ ```
3429
+
3430
+ ```ruby
3431
+ http_digest(realm: { realm: 'Test Api', opaque: 'app secret', passwords_hashed: true }) do |username|
3432
+ # lookup the user's password hash here
2866
3433
  end
2867
3434
  ```
2868
3435
 
@@ -2893,7 +3460,9 @@ end
2893
3460
 
2894
3461
  ```
2895
3462
 
2896
- Use [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
3463
+ Use [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper), [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
3464
+
3465
+ You can access the controller params, headers, and helpers through the context with the `#context` method inside any auth middleware inherited from `Grape::Middleware::Auth::Base`.
2897
3466
 
2898
3467
  ## Describing and Inspecting an API
2899
3468
 
@@ -2962,19 +3531,22 @@ class ApiLogger < Grape::Middleware::Base
2962
3531
  end
2963
3532
  ```
2964
3533
 
2965
- ## Before and After
3534
+ ## Before, After and Finally
2966
3535
 
2967
3536
  Blocks can be executed before or after every API call, using `before`, `after`,
2968
3537
  `before_validation` and `after_validation`.
3538
+ If the API fails the `after` call will not be triggered, if you need code to execute for sure
3539
+ use the `finally`.
2969
3540
 
2970
3541
  Before and after callbacks execute in the following order:
2971
3542
 
2972
3543
  1. `before`
2973
3544
  2. `before_validation`
2974
3545
  3. _validations_
2975
- 4. `after_validation`
2976
- 5. _the API call_
2977
- 6. `after`
3546
+ 4. `after_validation` (upon successful validation)
3547
+ 5. _the API call_ (upon successful validation)
3548
+ 6. `after` (upon successful validation and API call)
3549
+ 7. `finally` (always)
2978
3550
 
2979
3551
  Steps 4, 5 and 6 only happen if validation succeeds.
2980
3552
 
@@ -2994,6 +3566,14 @@ before do
2994
3566
  end
2995
3567
  ```
2996
3568
 
3569
+ You can ensure a block of code runs after every request (including failures) with `finally`:
3570
+
3571
+ ```ruby
3572
+ finally do
3573
+ # this code will run after every request (successful or failed)
3574
+ end
3575
+ ```
3576
+
2997
3577
  **Namespaces**
2998
3578
 
2999
3579
  Callbacks apply to each API call within and below the current namespace:
@@ -3196,11 +3776,21 @@ class API < Grape::API
3196
3776
  end
3197
3777
  ```
3198
3778
 
3779
+ You can access the controller params, headers, and helpers through the context with the `#context` method inside any middleware inherited from `Grape::Middleware::Base`.
3780
+
3199
3781
  ### Rails Middleware
3200
3782
 
3201
3783
  Note that when you're using Grape mounted on Rails you don't have to use Rails middleware because it's already included into your middleware stack.
3202
3784
  You only have to implement the helpers to access the specific `env` variable.
3203
3785
 
3786
+ If you are using a custom application that is inherited from `Rails::Application` and need to insert a new middleware among the ones initiated via Rails, you will need to register it manually in your custom application class.
3787
+
3788
+ ```ruby
3789
+ class Company::Application < Rails::Application
3790
+ config.middleware.insert_before(Rack::Attack, Middleware::ApiLogger)
3791
+ end
3792
+ ```
3793
+
3204
3794
  ### Remote IP
3205
3795
 
3206
3796
  By default you can access remote IP with `request.ip`. This is the remote IP address implemented by Rack. Sometimes it is desirable to get the remote IP [Rails-style](http://stackoverflow.com/questions/10997005/whats-the-difference-between-request-remote-ip-and-request-ip-in-rails) with `ActionDispatch::RemoteIp`.
@@ -3234,7 +3824,7 @@ Use `rack-test` and define your API as `app`.
3234
3824
  You can test a Grape API with RSpec by making HTTP requests and examining the response.
3235
3825
 
3236
3826
  ```ruby
3237
- require 'spec_helper'
3827
+
3238
3828
 
3239
3829
  describe Twitter::API do
3240
3830
  include Rack::Test::Methods
@@ -3504,6 +4094,8 @@ Grape integrates with following third-party tools:
3504
4094
  * **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
3505
4095
  * **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
3506
4096
  * **[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)
4097
+ * **[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)
4098
+ * **[Datadog APM](https://docs.datadoghq.com/tracing/)** - [ddtrace](https://github.com/datadog/dd-trace-rb) gem, [documentation](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#grape)
3507
4099
 
3508
4100
  ## Contributing to Grape
3509
4101
 
@@ -3512,10 +4104,14 @@ features and discuss issues.
3512
4104
 
3513
4105
  See [CONTRIBUTING](CONTRIBUTING.md).
3514
4106
 
4107
+ ## Security
4108
+
4109
+ See [SECURITY](SECURITY.md) for details.
4110
+
3515
4111
  ## License
3516
4112
 
3517
- MIT License. See LICENSE for details.
4113
+ MIT License. See [LICENSE](LICENSE) for details.
3518
4114
 
3519
4115
  ## Copyright
3520
4116
 
3521
- Copyright (c) 2010-2018 Michael Bleigh, Intridea Inc. and Contributors.
4117
+ Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.