grape 1.0.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (284) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +346 -39
  3. data/LICENSE +1 -1
  4. data/README.md +658 -90
  5. data/UPGRADING.md +472 -17
  6. data/grape.gemspec +17 -6
  7. data/lib/grape/api/helpers.rb +2 -0
  8. data/lib/grape/api/instance.rb +279 -0
  9. data/lib/grape/api.rb +132 -176
  10. data/lib/grape/config.rb +34 -0
  11. data/lib/grape/content_types.rb +34 -0
  12. data/lib/grape/cookies.rb +4 -0
  13. data/lib/grape/dsl/api.rb +2 -0
  14. data/lib/grape/dsl/callbacks.rb +22 -0
  15. data/lib/grape/dsl/configuration.rb +2 -0
  16. data/lib/grape/dsl/desc.rb +44 -12
  17. data/lib/grape/dsl/headers.rb +2 -0
  18. data/lib/grape/dsl/helpers.rb +11 -6
  19. data/lib/grape/dsl/inside_route.rb +116 -36
  20. data/lib/grape/dsl/logger.rb +2 -0
  21. data/lib/grape/dsl/middleware.rb +12 -3
  22. data/lib/grape/dsl/parameters.rb +34 -16
  23. data/lib/grape/dsl/request_response.rb +13 -8
  24. data/lib/grape/dsl/routing.rb +19 -12
  25. data/lib/grape/dsl/settings.rb +22 -4
  26. data/lib/grape/dsl/validations.rb +24 -4
  27. data/lib/grape/eager_load.rb +20 -0
  28. data/lib/grape/endpoint.rb +66 -57
  29. data/lib/grape/error_formatter/base.rb +2 -0
  30. data/lib/grape/error_formatter/json.rb +6 -4
  31. data/lib/grape/error_formatter/txt.rb +10 -3
  32. data/lib/grape/error_formatter/xml.rb +6 -4
  33. data/lib/grape/error_formatter.rb +4 -2
  34. data/lib/grape/exceptions/base.rb +22 -14
  35. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  36. data/lib/grape/exceptions/incompatible_option_values.rb +2 -1
  37. data/lib/grape/exceptions/invalid_accept_header.rb +2 -1
  38. data/lib/grape/exceptions/invalid_formatter.rb +2 -1
  39. data/lib/grape/exceptions/invalid_message_body.rb +2 -1
  40. data/lib/grape/exceptions/invalid_response.rb +11 -0
  41. data/lib/grape/exceptions/invalid_version_header.rb +2 -1
  42. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -1
  43. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -1
  44. data/lib/grape/exceptions/method_not_allowed.rb +2 -1
  45. data/lib/grape/exceptions/missing_group_type.rb +2 -1
  46. data/lib/grape/exceptions/missing_mime_type.rb +2 -1
  47. data/lib/grape/exceptions/missing_option.rb +2 -1
  48. data/lib/grape/exceptions/missing_vendor_option.rb +2 -1
  49. data/lib/grape/exceptions/unknown_options.rb +2 -1
  50. data/lib/grape/exceptions/unknown_parameter.rb +2 -1
  51. data/lib/grape/exceptions/unknown_validator.rb +2 -1
  52. data/lib/grape/exceptions/unsupported_group_type.rb +2 -1
  53. data/lib/grape/exceptions/validation.rb +5 -4
  54. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  55. data/lib/grape/exceptions/validation_errors.rb +16 -13
  56. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  57. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  58. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  59. data/lib/grape/extensions/hash.rb +2 -0
  60. data/lib/grape/extensions/hashie/mash.rb +2 -0
  61. data/lib/grape/formatter/json.rb +3 -0
  62. data/lib/grape/formatter/serializable_hash.rb +4 -1
  63. data/lib/grape/formatter/txt.rb +2 -0
  64. data/lib/grape/formatter/xml.rb +3 -0
  65. data/lib/grape/formatter.rb +5 -3
  66. data/lib/grape/http/headers.rb +50 -18
  67. data/lib/grape/locale/en.yml +3 -1
  68. data/lib/grape/middleware/auth/base.rb +7 -7
  69. data/lib/grape/middleware/auth/dsl.rb +2 -0
  70. data/lib/grape/middleware/auth/strategies.rb +2 -0
  71. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  72. data/lib/grape/middleware/base.rb +12 -7
  73. data/lib/grape/middleware/error.rb +75 -61
  74. data/lib/grape/middleware/filter.rb +2 -0
  75. data/lib/grape/middleware/formatter.rb +17 -13
  76. data/lib/grape/middleware/globals.rb +2 -0
  77. data/lib/grape/middleware/helpers.rb +12 -0
  78. data/lib/grape/middleware/stack.rb +15 -5
  79. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
  80. data/lib/grape/middleware/versioner/header.rb +13 -9
  81. data/lib/grape/middleware/versioner/param.rb +4 -1
  82. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
  83. data/lib/grape/middleware/versioner/path.rb +5 -1
  84. data/lib/grape/middleware/versioner.rb +2 -0
  85. data/lib/grape/namespace.rb +15 -3
  86. data/lib/grape/parser/json.rb +3 -1
  87. data/lib/grape/parser/xml.rb +3 -1
  88. data/lib/grape/parser.rb +4 -2
  89. data/lib/grape/path.rb +16 -3
  90. data/lib/grape/presenters/presenter.rb +2 -0
  91. data/lib/grape/request.rb +19 -9
  92. data/lib/grape/router/attribute_translator.rb +41 -8
  93. data/lib/grape/router/pattern.rb +22 -18
  94. data/lib/grape/router/route.rb +16 -30
  95. data/lib/grape/router.rb +37 -28
  96. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  97. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  98. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  99. data/lib/grape/util/base_inheritable.rb +43 -0
  100. data/lib/grape/util/cache.rb +20 -0
  101. data/lib/grape/util/endpoint_configuration.rb +8 -0
  102. data/lib/grape/util/env.rb +19 -17
  103. data/lib/grape/util/inheritable_setting.rb +3 -3
  104. data/lib/grape/util/inheritable_values.rb +7 -25
  105. data/lib/grape/util/json.rb +2 -0
  106. data/lib/grape/util/lazy_block.rb +27 -0
  107. data/lib/grape/util/lazy_object.rb +43 -0
  108. data/lib/grape/util/lazy_value.rb +99 -0
  109. data/lib/grape/util/registrable.rb +2 -0
  110. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  111. data/lib/grape/util/stackable_values.rb +21 -34
  112. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  113. data/lib/grape/util/xml.rb +2 -0
  114. data/lib/grape/validations/attributes_iterator.rb +16 -6
  115. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  116. data/lib/grape/validations/params_scope.rb +128 -66
  117. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  118. data/lib/grape/validations/types/array_coercer.rb +65 -0
  119. data/lib/grape/validations/types/build_coercer.rb +52 -46
  120. data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
  121. data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
  122. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  123. data/lib/grape/validations/types/file.rb +22 -18
  124. data/lib/grape/validations/types/invalid_value.rb +24 -0
  125. data/lib/grape/validations/types/json.rb +47 -39
  126. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  127. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  128. data/lib/grape/validations/types/set_coercer.rb +40 -0
  129. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  130. data/lib/grape/validations/types.rb +26 -38
  131. data/lib/grape/validations/validator_factory.rb +8 -11
  132. data/lib/grape/validations/validators/all_or_none.rb +8 -12
  133. data/lib/grape/validations/validators/allow_blank.rb +4 -2
  134. data/lib/grape/validations/validators/as.rb +12 -0
  135. data/lib/grape/validations/validators/at_least_one_of.rb +7 -12
  136. data/lib/grape/validations/validators/base.rb +23 -15
  137. data/lib/grape/validations/validators/coerce.rb +47 -28
  138. data/lib/grape/validations/validators/default.rb +7 -6
  139. data/lib/grape/validations/validators/exactly_one_of.rb +10 -22
  140. data/lib/grape/validations/validators/except_values.rb +4 -2
  141. data/lib/grape/validations/validators/multiple_params_base.rb +19 -10
  142. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -17
  143. data/lib/grape/validations/validators/presence.rb +4 -1
  144. data/lib/grape/validations/validators/regexp.rb +5 -2
  145. data/lib/grape/validations/validators/same_as.rb +27 -0
  146. data/lib/grape/validations/validators/values.rb +29 -9
  147. data/lib/grape/validations.rb +2 -0
  148. data/lib/grape/version.rb +3 -1
  149. data/lib/grape.rb +107 -73
  150. data/spec/grape/api/custom_validations_spec.rb +6 -3
  151. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  152. data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
  153. data/spec/grape/api/inherited_helpers_spec.rb +116 -0
  154. data/spec/grape/api/instance_spec.rb +104 -0
  155. data/spec/grape/api/invalid_format_spec.rb +2 -0
  156. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  157. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  158. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  160. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  161. data/spec/grape/api/recognize_path_spec.rb +2 -0
  162. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  163. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  164. data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
  165. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +49 -0
  166. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  167. data/spec/grape/api_remount_spec.rb +473 -0
  168. data/spec/grape/api_spec.rb +775 -65
  169. data/spec/grape/config_spec.rb +19 -0
  170. data/spec/grape/dsl/callbacks_spec.rb +3 -1
  171. data/spec/grape/dsl/configuration_spec.rb +2 -0
  172. data/spec/grape/dsl/desc_spec.rb +42 -16
  173. data/spec/grape/dsl/headers_spec.rb +2 -0
  174. data/spec/grape/dsl/helpers_spec.rb +23 -2
  175. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  176. data/spec/grape/dsl/logger_spec.rb +2 -0
  177. data/spec/grape/dsl/middleware_spec.rb +11 -1
  178. data/spec/grape/dsl/parameters_spec.rb +12 -9
  179. data/spec/grape/dsl/request_response_spec.rb +2 -0
  180. data/spec/grape/dsl/routing_spec.rb +13 -1
  181. data/spec/grape/dsl/settings_spec.rb +4 -2
  182. data/spec/grape/dsl/validations_spec.rb +2 -0
  183. data/spec/grape/endpoint/declared_spec.rb +848 -0
  184. data/spec/grape/endpoint_spec.rb +75 -515
  185. data/spec/grape/entity_spec.rb +19 -11
  186. data/spec/grape/exceptions/base_spec.rb +67 -0
  187. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  188. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  189. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -1
  190. data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
  191. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -1
  192. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  193. data/spec/grape/exceptions/missing_option_spec.rb +2 -1
  194. data/spec/grape/exceptions/unknown_options_spec.rb +3 -2
  195. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -1
  196. data/spec/grape/exceptions/validation_errors_spec.rb +17 -4
  197. data/spec/grape/exceptions/validation_spec.rb +3 -1
  198. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  199. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  200. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  201. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  202. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  203. data/spec/grape/integration/rack_spec.rb +25 -7
  204. data/spec/grape/loading_spec.rb +2 -0
  205. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  206. data/spec/grape/middleware/auth/dsl_spec.rb +6 -4
  207. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  208. data/spec/grape/middleware/base_spec.rb +10 -0
  209. data/spec/grape/middleware/error_spec.rb +4 -3
  210. data/spec/grape/middleware/exception_spec.rb +205 -55
  211. data/spec/grape/middleware/formatter_spec.rb +130 -15
  212. data/spec/grape/middleware/globals_spec.rb +2 -0
  213. data/spec/grape/middleware/stack_spec.rb +15 -1
  214. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  215. data/spec/grape/middleware/versioner/header_spec.rb +10 -2
  216. data/spec/grape/middleware/versioner/param_spec.rb +4 -2
  217. data/spec/grape/middleware/versioner/path_spec.rb +4 -2
  218. data/spec/grape/middleware/versioner_spec.rb +2 -0
  219. data/spec/grape/named_api_spec.rb +21 -0
  220. data/spec/grape/parser_spec.rb +7 -5
  221. data/spec/grape/path_spec.rb +9 -7
  222. data/spec/grape/presenters/presenter_spec.rb +2 -0
  223. data/spec/grape/request_spec.rb +26 -0
  224. data/spec/grape/util/inheritable_setting_spec.rb +4 -2
  225. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  226. data/spec/grape/util/reverse_stackable_values_spec.rb +15 -13
  227. data/spec/grape/util/stackable_values_spec.rb +16 -14
  228. data/spec/grape/util/strict_hash_configuration_spec.rb +3 -1
  229. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  230. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  231. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
  232. data/spec/grape/validations/params_scope_spec.rb +364 -7
  233. data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
  234. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  235. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  236. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  237. data/spec/grape/validations/types_spec.rb +9 -36
  238. data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
  239. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  240. data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
  241. data/spec/grape/validations/validators/coerce_spec.rb +558 -133
  242. data/spec/grape/validations/validators/default_spec.rb +212 -0
  243. data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
  244. data/spec/grape/validations/validators/except_values_spec.rb +6 -3
  245. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
  246. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  247. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  248. data/spec/grape/validations/validators/same_as_spec.rb +65 -0
  249. data/spec/grape/validations/validators/values_spec.rb +73 -19
  250. data/spec/grape/validations_spec.rb +403 -53
  251. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  252. data/spec/integration/multi_json/json_spec.rb +2 -0
  253. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  254. data/spec/shared/versioning_examples.rb +78 -18
  255. data/spec/spec_helper.rb +13 -2
  256. data/spec/support/basic_auth_encode_helpers.rb +3 -1
  257. data/spec/support/chunks.rb +14 -0
  258. data/spec/support/content_type_helpers.rb +3 -1
  259. data/spec/support/eager_load.rb +19 -0
  260. data/spec/support/endpoint_faker.rb +2 -0
  261. data/spec/support/file_streamer.rb +2 -0
  262. data/spec/support/integer_helpers.rb +2 -0
  263. data/spec/support/versioned_helpers.rb +8 -8
  264. metadata +176 -130
  265. data/Appraisals +0 -32
  266. data/Dangerfile +0 -1
  267. data/Gemfile +0 -34
  268. data/Gemfile.lock +0 -229
  269. data/Guardfile +0 -10
  270. data/RELEASING.md +0 -111
  271. data/Rakefile +0 -70
  272. data/benchmark/simple.rb +0 -27
  273. data/benchmark/simple_with_type_coercer.rb +0 -22
  274. data/gemfiles/multi_json.gemfile +0 -36
  275. data/gemfiles/multi_xml.gemfile +0 -36
  276. data/gemfiles/rack_1.5.2.gemfile +0 -36
  277. data/gemfiles/rack_edge.gemfile +0 -36
  278. data/gemfiles/rails_3.gemfile +0 -37
  279. data/gemfiles/rails_4.gemfile +0 -36
  280. data/gemfiles/rails_5.gemfile +0 -36
  281. data/gemfiles/rails_edge.gemfile +0 -36
  282. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  283. data/lib/grape/util/content_types.rb +0 -26
  284. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
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,41 +12,76 @@
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)
41
+ - [Include Parent Namespaces](#include-parent-namespaces)
34
42
  - [Include Missing](#include-missing)
35
43
  - [Parameter Validation and Coercion](#parameter-validation-and-coercion)
36
44
  - [Supported Parameter Types](#supported-parameter-types)
37
45
  - [Integer/Fixnum and Coercions](#integerfixnum-and-coercions)
38
46
  - [Custom Types and Coercions](#custom-types-and-coercions)
39
47
  - [Multipart File Parameters](#multipart-file-parameters)
40
- - [First-Class `JSON` Types](#first-class-json-types)
48
+ - [First-Class JSON Types](#first-class-json-types)
41
49
  - [Multiple Allowed Types](#multiple-allowed-types)
42
50
  - [Validation of Nested Parameters](#validation-of-nested-parameters)
43
51
  - [Dependent Parameters](#dependent-parameters)
44
52
  - [Group Options](#group-options)
53
+ - [Renaming](#renaming)
45
54
  - [Built-in Validators](#built-in-validators)
55
+ - [allow_blank](#allow_blank)
56
+ - [values](#values)
57
+ - [except_values](#except_values)
58
+ - [same_as](#same_as)
59
+ - [regexp](#regexp)
60
+ - [mutually_exclusive](#mutually_exclusive)
61
+ - [exactly_one_of](#exactly_one_of)
62
+ - [at_least_one_of](#at_least_one_of)
63
+ - [all_or_none_of](#all_or_none_of)
64
+ - [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)
46
65
  - [Namespace Validation and Coercion](#namespace-validation-and-coercion)
47
66
  - [Custom Validators](#custom-validators)
48
67
  - [Validation Errors](#validation-errors)
49
68
  - [I18n](#i18n)
50
- - [Custom Validation Messages](#custom-validation-messages)
69
+ - [Custom Validation messages](#custom-validation-messages)
70
+ - [presence, allow_blank, values, regexp](#presence-allow_blank-values-regexp)
71
+ - [same_as](#same_as-1)
72
+ - [all_or_none_of](#all_or_none_of-1)
73
+ - [mutually_exclusive](#mutually_exclusive-1)
74
+ - [exactly_one_of](#exactly_one_of-1)
75
+ - [at_least_one_of](#at_least_one_of-1)
76
+ - [Coerce](#coerce)
77
+ - [With Lambdas](#with-lambdas)
78
+ - [Pass symbols for i18n translations](#pass-symbols-for-i18n-translations)
79
+ - [Overriding Attribute Names](#overriding-attribute-names)
80
+ - [With Default](#with-default)
51
81
  - [Headers](#headers)
82
+ - [Request](#request)
83
+ - [Header Case Handling](#header-case-handling)
84
+ - [Response](#response)
52
85
  - [Routes](#routes)
53
86
  - [Helpers](#helpers)
54
87
  - [Path Helpers](#path-helpers)
@@ -62,6 +95,9 @@
62
95
  - [Default Error HTTP Status Code](#default-error-http-status-code)
63
96
  - [Handling 404](#handling-404)
64
97
  - [Exception Handling](#exception-handling)
98
+ - [Rescuing exceptions inside namespaces](#rescuing-exceptions-inside-namespaces)
99
+ - [Unrescuable Exceptions](#unrescuable-exceptions)
100
+ - [Exceptions that should be rescued explicitly](#exceptions-that-should-be-rescued-explicitly)
65
101
  - [Rails 3.x](#rails-3x)
66
102
  - [Logging](#logging)
67
103
  - [API Formats](#api-formats)
@@ -77,9 +113,11 @@
77
113
  - [Active Model Serializers](#active-model-serializers)
78
114
  - [Sending Raw or No Data](#sending-raw-or-no-data)
79
115
  - [Authentication](#authentication)
116
+ - [Basic and Digest Auth](#basic-and-digest-auth)
117
+ - [Register custom middleware for authentication](#register-custom-middleware-for-authentication)
80
118
  - [Describing and Inspecting an API](#describing-and-inspecting-an-api)
81
119
  - [Current Route and Endpoint](#current-route-and-endpoint)
82
- - [Before and After](#before-and-after)
120
+ - [Before, After and Finally](#before-after-and-finally)
83
121
  - [Anchoring](#anchoring)
84
122
  - [Using Custom Middleware](#using-custom-middleware)
85
123
  - [Grape Middleware](#grape-middleware)
@@ -87,15 +125,26 @@
87
125
  - [Remote IP](#remote-ip)
88
126
  - [Writing Tests](#writing-tests)
89
127
  - [Writing Tests with Rack](#writing-tests-with-rack)
128
+ - [RSpec](#rspec)
129
+ - [Airborne](#airborne)
130
+ - [MiniTest](#minitest)
90
131
  - [Writing Tests with Rails](#writing-tests-with-rails)
132
+ - [RSpec](#rspec-1)
133
+ - [MiniTest](#minitest-1)
91
134
  - [Stubbing Helpers](#stubbing-helpers)
92
135
  - [Reloading API Changes in Development](#reloading-api-changes-in-development)
93
136
  - [Reloading in Rack Applications](#reloading-in-rack-applications)
94
137
  - [Reloading in Rails Applications](#reloading-in-rails-applications)
95
138
  - [Performance Monitoring](#performance-monitoring)
96
139
  - [Active Support Instrumentation](#active-support-instrumentation)
140
+ - [endpoint_run.grape](#endpoint_rungrape)
141
+ - [endpoint_render.grape](#endpoint_rendergrape)
142
+ - [endpoint_run_filters.grape](#endpoint_run_filtersgrape)
143
+ - [endpoint_run_validators.grape](#endpoint_run_validatorsgrape)
144
+ - [format_response.grape](#format_responsegrape)
97
145
  - [Monitoring Products](#monitoring-products)
98
146
  - [Contributing to Grape](#contributing-to-grape)
147
+ - [Security](#security)
99
148
  - [License](#license)
100
149
  - [Copyright](#copyright)
101
150
 
@@ -109,17 +158,28 @@ content negotiation, versioning and much more.
109
158
 
110
159
  ## Stable Release
111
160
 
112
- You're reading the documentation for the stable release of Grape, 1.0.0.
161
+ You're reading the documentation for the stable release of Grape, **1.6.0**.
113
162
  Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
114
163
 
115
164
  ## Project Resources
116
165
 
117
166
  * [Grape Website](http://www.ruby-grape.org)
167
+ * [Documentation](http://www.rubydoc.info/gems/grape)
118
168
  * Need help? Try [Grape Google Group](http://groups.google.com/group/ruby-grape) or [Gitter](https://gitter.im/ruby-grape/grape)
119
169
  * [Follow us on Twitter](https://twitter.com/grapeframework)
120
170
 
171
+ ## Grape for Enterprise
172
+
173
+ Available as part of the Tidelift Subscription.
174
+
175
+ 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.
176
+
177
+ In 2020, we plan to use the money towards gathering Grape contributors for dinner in New York City.
178
+
121
179
  ## Installation
122
180
 
181
+ Ruby 2.4 or newer is required.
182
+
123
183
  Grape is available as a gem, to install it just install the gem:
124
184
 
125
185
  gem install grape
@@ -167,7 +227,7 @@ module Twitter
167
227
 
168
228
  desc 'Return a status.'
169
229
  params do
170
- requires :id, type: Integer, desc: 'Status id.'
230
+ requires :id, type: Integer, desc: 'Status ID.'
171
231
  end
172
232
  route_param :id do
173
233
  get do
@@ -215,6 +275,17 @@ end
215
275
 
216
276
  ## Mounting
217
277
 
278
+ ### All
279
+
280
+
281
+ By default Grape will compile the routes on the first route, it is possible to pre-load routes using the `compile!` method.
282
+
283
+ ```ruby
284
+ Twitter::API.compile!
285
+ ```
286
+
287
+ This can be added to your `config.ru` (if using rackup), `application.rb` (if using rails), or any file that loads your server.
288
+
218
289
  ### Rack
219
290
 
220
291
  The above sample creates a Rack application that can be run from a rackup `config.ru` file
@@ -224,6 +295,13 @@ with `rackup`:
224
295
  run Twitter::API
225
296
  ```
226
297
 
298
+ (With pre-loading you can use)
299
+
300
+ ```ruby
301
+ Twitter::API.compile!
302
+ run Twitter::API
303
+ ```
304
+
227
305
  And would respond to the following routes:
228
306
 
229
307
  GET /api/statuses/public_timeline
@@ -240,13 +318,21 @@ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just
240
318
  If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
241
319
  is handled correctly.
242
320
 
321
+ #### Rails 4
322
+
243
323
  The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
244
324
  `config.ru` before mounting Grape, e.g.:
245
325
 
246
326
  ```ruby
247
327
  use ActiveRecord::ConnectionAdapters::ConnectionManagement
328
+ ```
248
329
 
249
- run Twitter::API
330
+ #### Rails 5+
331
+
332
+ Use [otr-activerecord](https://github.com/jhollinger/otr-activerecord) as follows:
333
+
334
+ ```ruby
335
+ use OTR::ActiveRecord::ConnectionManagement
250
336
  ```
251
337
 
252
338
  ### Alongside Sinatra (or other frameworks)
@@ -273,13 +359,24 @@ class Web < Sinatra::Base
273
359
  end
274
360
 
275
361
  use Rack::Session::Cookie
276
- run Rack::Cascade.new [API, Web]
362
+ run Rack::Cascade.new [Web, API]
277
363
  ```
278
364
 
365
+ 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).
366
+
367
+
279
368
  ### Rails
280
369
 
281
370
  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`.
282
371
 
372
+ Modify `config/routes`:
373
+
374
+ ```ruby
375
+ mount Twitter::API => '/'
376
+ ```
377
+
378
+ #### Rails < 5.2
379
+
283
380
  Modify `application.rb`:
284
381
 
285
382
  ```ruby
@@ -287,21 +384,18 @@ config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
287
384
  config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
288
385
  ```
289
386
 
290
- Modify `config/routes`:
387
+ See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
291
388
 
292
- ```ruby
293
- mount Twitter::API => '/'
294
- ```
389
+ #### Rails 6.0
295
390
 
296
- Additionally, if the version of your Rails is 4.0+ and the application uses the default model layer of ActiveRecord, you will want to use the [hashie-forbidden_attributes gem](https://github.com/Maxim-Filimonov/hashie-forbidden_attributes). This gem disables the security feature of `strong_params` at the model layer, allowing you the use of Grape's own params validation instead.
391
+ 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:
297
392
 
298
393
  ```ruby
299
- # Gemfile
300
- gem 'hashie-forbidden_attributes'
394
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
395
+ inflect.acronym 'API'
396
+ end
301
397
  ```
302
398
 
303
- See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
304
-
305
399
  ### Modules
306
400
 
307
401
  You can mount multiple API implementations inside another one. These don't have to be
@@ -335,6 +429,131 @@ class Twitter::API < Grape::API
335
429
  end
336
430
  ```
337
431
 
432
+ ## Remounting
433
+
434
+ You can mount the same endpoints in two different locations.
435
+
436
+ ```ruby
437
+ class Voting::API < Grape::API
438
+ namespace 'votes' do
439
+ get do
440
+ # Your logic
441
+ end
442
+
443
+ post do
444
+ # Your logic
445
+ end
446
+ end
447
+ end
448
+
449
+ class Post::API < Grape::API
450
+ mount Voting::API
451
+ end
452
+
453
+ class Comment::API < Grape::API
454
+ mount Voting::API
455
+ end
456
+ ```
457
+
458
+ 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`.
459
+
460
+ ### Mount Configuration
461
+
462
+ You can configure remountable endpoints to change how they behave according to where they are mounted.
463
+
464
+ ```ruby
465
+ class Voting::API < Grape::API
466
+ namespace 'votes' do
467
+ desc "Vote for your #{configuration[:votable]}"
468
+ get do
469
+ # Your logic
470
+ end
471
+ end
472
+ end
473
+
474
+ class Post::API < Grape::API
475
+ mount Voting::API, with: { votable: 'posts' }
476
+ end
477
+
478
+ class Comment::API < Grape::API
479
+ mount Voting::API, with: { votable: 'comments' }
480
+ end
481
+ ```
482
+
483
+ Note that if you're passing a hash as the first parameter to `mount`, you will need to explicitly put `()` around parameters:
484
+ ```ruby
485
+ # good
486
+ mount({ ::Some::Api => '/some/api' }, with: { condition: true })
487
+
488
+ # bad
489
+ mount ::Some::Api => '/some/api', with: { condition: true }
490
+ ```
491
+
492
+ You can access `configuration` on the class (to use as dynamic attributes), inside blocks (like namespace)
493
+
494
+ If you want logic happening given on an `configuration`, you can use the helper `given`.
495
+
496
+ ```ruby
497
+ class ConditionalEndpoint::API < Grape::API
498
+ given configuration[:some_setting] do
499
+ get 'mount_this_endpoint_conditionally' do
500
+ configuration[:configurable_response]
501
+ end
502
+ end
503
+ end
504
+ ```
505
+
506
+ If you want a block of logic running every time an endpoint is mounted (within which you can access the `configuration` Hash)
507
+
508
+
509
+ ```ruby
510
+ class ConditionalEndpoint::API < Grape::API
511
+ mounted do
512
+ YourLogger.info "This API was mounted at: #{Time.now}"
513
+
514
+ get configuration[:endpoint_name] do
515
+ configuration[:configurable_response]
516
+ end
517
+ end
518
+ end
519
+ ```
520
+
521
+ More complex results can be achieved by using `mounted` as an expression within which the `configuration` is already evaluated as a Hash.
522
+
523
+ ```ruby
524
+ class ExpressionEndpointAPI < Grape::API
525
+ get(mounted { configuration[:route_name] || 'default_name' }) do
526
+ # some logic
527
+ end
528
+ end
529
+ ```
530
+
531
+ ```ruby
532
+ class BasicAPI < Grape::API
533
+ desc 'Statuses index' do
534
+ params: mounted { configuration[:entity] || API::Entities::Status }.documentation
535
+ end
536
+ params do
537
+ requires :all, using: mounted { configuration[:entity] || API::Entities::Status }.documentation
538
+ end
539
+ get '/statuses' do
540
+ statuses = Status.all
541
+ type = current_user.admin? ? :full : :default
542
+ present statuses, with: mounted { configuration[:entity] || API::Entities::Status }, type: type
543
+ end
544
+ end
545
+
546
+ class V1 < Grape::API
547
+ version 'v1'
548
+ mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Enitities::Status } }
549
+ end
550
+
551
+ class V2 < Grape::API
552
+ version 'v2'
553
+ mount BasicAPI, with: { entity: mounted { configuration[:entity] || API::Enitities::V2::Status } }
554
+ end
555
+ ```
556
+
338
557
  ## Versioning
339
558
 
340
559
  There are four strategies in which clients can reach your API's endpoints: `:path`,
@@ -415,10 +634,13 @@ version 'v1', using: :param, parameter: 'v'
415
634
 
416
635
  ## Describing Methods
417
636
 
418
- You can add a description to API methods and namespaces.
637
+ 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.
638
+
639
+ Note: Description block is only for documentation and won't affects API behavior.
419
640
 
420
641
  ```ruby
421
642
  desc 'Returns your public timeline.' do
643
+ summary 'summary'
422
644
  detail 'more details'
423
645
  params API::Entities::Status.documentation
424
646
  success API::Entities::Entity
@@ -432,7 +654,13 @@ desc 'Returns your public timeline.' do
432
654
  description: 'Not really needed',
433
655
  required: false
434
656
  }
435
-
657
+ hidden false
658
+ deprecated false
659
+ is_array true
660
+ nickname 'nickname'
661
+ produces ['application/json']
662
+ consumes ['application/json']
663
+ tags ['tag1', 'tag2']
436
664
  end
437
665
  get :public_timeline do
438
666
  Status.limit(20)
@@ -445,6 +673,43 @@ end
445
673
  * `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities
446
674
  * `named`: A helper to give a route a name and find it with this name in the documentation Hash
447
675
  * `headers`: A definition of the used Headers
676
+ * Other options can be found in [grape-swagger][grape-swagger]
677
+
678
+ [grape-swagger]: https://github.com/ruby-grape/grape-swagger
679
+
680
+ ## Configuration
681
+
682
+ Use `Grape.configure` to set up global settings at load time.
683
+ Currently the configurable settings are:
684
+
685
+ * `param_builder`: Sets the [Parameter Builder](#parameters), defaults to `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder`.
686
+
687
+ To change a setting value make sure that at some point during load time the following code runs
688
+
689
+ ```ruby
690
+ Grape.configure do |config|
691
+ config.setting = value
692
+ end
693
+ ```
694
+
695
+ For example, for the `param_builder`, the following code could run in an initializer:
696
+
697
+ ```ruby
698
+ Grape.configure do |config|
699
+ config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
700
+ end
701
+ ```
702
+
703
+ You can also configure a single API:
704
+
705
+ ```ruby
706
+ API.configure do |config|
707
+ config[key] = value
708
+ end
709
+ ```
710
+
711
+ This will be available inside the API with `configuration`, as if it were
712
+ [mount configuration](#mount-configuration).
448
713
 
449
714
  ## Parameters
450
715
 
@@ -523,13 +788,21 @@ params do
523
788
  end
524
789
  ```
525
790
 
791
+ Or globally with the [Configuration](#configuration) `Grape.configure.param_builder`.
792
+
526
793
  In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.
527
794
 
528
795
  Available parameter builders are `Grape::Extensions::Hash::ParamBuilder`, `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder` and `Grape::Extensions::Hashie::Mash::ParamBuilder`.
529
796
 
530
797
  ### Declared
531
798
 
532
- 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:
799
+ Grape allows you to access only the parameters that have been declared by your `params` block. It will:
800
+
801
+ * Filter out the params that have been passed, but are not allowed.
802
+ * Include any optional params that are declared but not passed.
803
+ * Perform any parameter renaming on the resulting hash.
804
+
805
+ Consider the following API endpoint:
533
806
 
534
807
  ````ruby
535
808
  format :json
@@ -562,9 +835,9 @@ Once we add parameters requirements, grape will start returning only the declare
562
835
  format :json
563
836
 
564
837
  params do
565
- requires :user, type: Hash do
566
- requires :first_name, type: String
567
- requires :last_name, type: String
838
+ optional :user, type: Hash do
839
+ optional :first_name, type: String
840
+ optional :last_name, type: String
568
841
  end
569
842
  end
570
843
 
@@ -592,6 +865,44 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
592
865
  }
593
866
  ````
594
867
 
868
+ Missing params that are declared as type `Hash` or `Array` will be included.
869
+
870
+ ````ruby
871
+ format :json
872
+
873
+ params do
874
+ optional :user, type: Hash do
875
+ optional :first_name, type: String
876
+ optional :last_name, type: String
877
+ end
878
+ optional :widgets, type: Array
879
+ end
880
+
881
+ post 'users/signup' do
882
+ { 'declared_params' => declared(params) }
883
+ end
884
+ ````
885
+
886
+ **Request**
887
+
888
+ ````bash
889
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{}'
890
+ ````
891
+
892
+ **Response**
893
+
894
+ ````json
895
+ {
896
+ "declared_params": {
897
+ "user": {
898
+ "first_name": null,
899
+ "last_name": null
900
+ },
901
+ "widgets": []
902
+ }
903
+ }
904
+ ````
905
+
595
906
  The returned hash is an `ActiveSupport::HashWithIndifferentAccess`.
596
907
 
597
908
  The `#declared` method is not available to `before` filters, as those are evaluated prior to parameter coercion.
@@ -642,7 +953,7 @@ curl -X GET -H "Content-Type: application/json" localhost:9292/parent/foo/bar
642
953
  }
643
954
  ````
644
955
 
645
- ### Include missing
956
+ ### Include Missing
646
957
 
647
958
  By default `declared(params)` includes parameters that have `nil` values. If you want to return only the parameters that are not `nil`, you can use the `include_missing` option. By default, `include_missing` is set to `true`. Consider the following API:
648
959
 
@@ -650,8 +961,10 @@ By default `declared(params)` includes parameters that have `nil` values. If you
650
961
  format :json
651
962
 
652
963
  params do
653
- requires :first_name, type: String
654
- optional :last_name, type: String
964
+ requires :user, type: Hash do
965
+ requires :first_name, type: String
966
+ optional :last_name, type: String
967
+ end
655
968
  end
656
969
 
657
970
  post 'users/signup' do
@@ -682,8 +995,10 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
682
995
  ````json
683
996
  {
684
997
  "declared_params": {
685
- "first_name": "first name",
686
- "last_name": null
998
+ "user": {
999
+ "first_name": "first name",
1000
+ "last_name": null
1001
+ }
687
1002
  }
688
1003
  }
689
1004
  ````
@@ -804,13 +1119,13 @@ params do
804
1119
  end
805
1120
  ```
806
1121
 
807
- Note that default values will be passed through to any validation options specified.
808
- The following example will always fail if `:color` is not explicitly provided.
809
-
810
1122
  Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
811
1123
  number for each call to the endpoint of this `params` block. To have the default evaluate
812
1124
  lazily with each request use a lambda, like `:random_number` above.
813
1125
 
1126
+ Note that default values will be passed through to any validation options specified.
1127
+ The following example will always fail if `:color` is not explicitly provided.
1128
+
814
1129
  ```ruby
815
1130
  params do
816
1131
  optional :color, type: String, default: 'blue', values: ['red', 'green']
@@ -867,10 +1182,11 @@ get '/int' integers: { int: '45' }
867
1182
  ### Custom Types and Coercions
868
1183
 
869
1184
  Aside from the default set of supported types listed above, any class can be
870
- used as a type so long as an explicit coercion method is supplied. If the type
1185
+ used as a type as long as an explicit coercion method is supplied. If the type
871
1186
  implements a class-level `parse` method, Grape will use it automatically.
872
1187
  This method must take one string argument and return an instance of the correct
873
- type, or raise an exception to indicate the value was invalid. E.g.,
1188
+ type, or return an instance of `Grape::Types::InvalidValue` which optionally
1189
+ accepts a message to be returned in the response.
874
1190
 
875
1191
  ```ruby
876
1192
  class Color
@@ -880,15 +1196,16 @@ class Color
880
1196
  end
881
1197
 
882
1198
  def self.parse(value)
883
- fail 'Invalid color' unless %w(blue red green).include?(value)
884
- new(value)
1199
+ return new(value) if %w[blue red green]).include?(value)
1200
+
1201
+ Grape::Types::InvalidValue.new('Unsupported color')
885
1202
  end
886
1203
  end
887
1204
 
888
- # ...
889
-
890
1205
  params do
891
1206
  requires :color, type: Color, default: Color.new('blue')
1207
+ requires :more_colors, type: Array[Color] # Collections work
1208
+ optional :unique_colors, type: Set[Color] # Duplicates discarded
892
1209
  end
893
1210
 
894
1211
  get '/stuff' do
@@ -904,7 +1221,7 @@ parameter, and the return value must match the given `type`.
904
1221
 
905
1222
  ```ruby
906
1223
  params do
907
- requires :passwd, type: String, coerce_with: Base64.method(:decode)
1224
+ requires :passwd, type: String, coerce_with: Base64.method(:decode64)
908
1225
  requires :loud_color, type: Color, coerce_with: ->(c) { Color.parse(c.downcase) }
909
1226
 
910
1227
  requires :obj, type: Hash, coerce_with: JSON do
@@ -913,6 +1230,7 @@ params do
913
1230
  end
914
1231
  end
915
1232
  ```
1233
+ Note that, a `nil` value will call the custom coercion method, while a missing parameter will not.
916
1234
 
917
1235
  Example of use of `coerce_with` with a lambda (a class with a `parse` method could also have been used)
918
1236
  It will parse a string and return an Array of Integers, matching the `Array[Integer]` `type`.
@@ -923,6 +1241,26 @@ params do
923
1241
  end
924
1242
  ```
925
1243
 
1244
+ Grape will assert that coerced values match the given `type`, and will reject the request
1245
+ if they do not. To override this behaviour, custom types may implement a `parsed?` method
1246
+ that should accept a single argument and return `true` if the value passes type validation.
1247
+
1248
+ ```ruby
1249
+ class SecureUri
1250
+ def self.parse(value)
1251
+ URI.parse value
1252
+ end
1253
+
1254
+ def self.parsed?(value)
1255
+ value.is_a? URI::HTTPS
1256
+ end
1257
+ end
1258
+
1259
+ params do
1260
+ requires :secure_uri, type: SecureUri
1261
+ end
1262
+ ```
1263
+
926
1264
  ### Multipart File Parameters
927
1265
 
928
1266
  Grape makes use of `Rack::Request`'s built-in support for multipart file parameters. Such parameters can be declared with `type: File`:
@@ -933,7 +1271,7 @@ params do
933
1271
  end
934
1272
  post '/' do
935
1273
  params[:avatar][:filename] # => 'avatar.png'
936
- params[:avatar][:avatar] # => 'image/png'
1274
+ params[:avatar][:type] # => 'image/png'
937
1275
  params[:avatar][:tempfile] # => #<File>
938
1276
  end
939
1277
  ```
@@ -954,8 +1292,6 @@ get '/' do
954
1292
  params[:json].inspect
955
1293
  end
956
1294
 
957
- # ...
958
-
959
1295
  client.get('/', json: '{"int":1}') # => "{:int=>1}"
960
1296
  client.get('/', json: '[{"int":"1"}]') # => "[{:int=>1}]"
961
1297
 
@@ -991,8 +1327,6 @@ get '/' do
991
1327
  params[:status_code].inspect
992
1328
  end
993
1329
 
994
- # ...
995
-
996
1330
  client.get('/', status_code: 'OK_GOOD') # => "OK_GOOD"
997
1331
  client.get('/', status_code: 300) # => 300
998
1332
  client.get('/', status_code: %w(404 NOT FOUND)) # => [404, "NOT", "FOUND"]
@@ -1009,15 +1343,13 @@ get '/' do
1009
1343
  params[:status_codes].inspect
1010
1344
  end
1011
1345
 
1012
- # ...
1013
-
1014
1346
  client.get('/', status_codes: %w(1 two)) # => [1, "two"]
1015
1347
  ```
1016
1348
 
1017
1349
  ### Validation of Nested Parameters
1018
1350
 
1019
1351
  Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
1020
- In the above example, this means `params[:media][:url]` is required along with `params[:id]`,
1352
+ In the [above example](#parameter-validation-and-coercion), this means `params[:media][:url]` is required along with `params[:id]`,
1021
1353
  and `params[:audio][:format]` is required only if `params[:audio]` is present.
1022
1354
  With a block, `group`, `requires` and `optional` accept an additional option `type` which can
1023
1355
  be either `Array` or `Hash`, and defaults to `Array`. Depending on the value, the nested
@@ -1054,7 +1386,7 @@ end
1054
1386
 
1055
1387
  In the example above Grape will use `blank?` to check whether the `shelf_id` param is present.
1056
1388
 
1057
- Given also takes a `Proc` with custom code. Below, the param `description` is required only if the value of `category` is equal `foo`:
1389
+ `given` also takes a `Proc` with custom code. Below, the param `description` is required only if the value of `category` is equal `foo`:
1058
1390
 
1059
1391
  ```ruby
1060
1392
  params do
@@ -1065,6 +1397,18 @@ params do
1065
1397
  end
1066
1398
  ```
1067
1399
 
1400
+ You can rename parameters:
1401
+
1402
+ ```ruby
1403
+ params do
1404
+ optional :category, as: :type
1405
+ given type: ->(val) { val == 'foo' } do
1406
+ requires :description
1407
+ end
1408
+ end
1409
+ ```
1410
+
1411
+ Note: param in `given` should be the renamed one. In the example, it should be `type`, not `category`.
1068
1412
 
1069
1413
  ### Group Options
1070
1414
 
@@ -1093,6 +1437,23 @@ params do
1093
1437
  end
1094
1438
  ```
1095
1439
 
1440
+ ### Renaming
1441
+
1442
+ You can rename parameters using `as`, which can be useful when refactoring existing APIs:
1443
+
1444
+ ```ruby
1445
+ resource :users do
1446
+ params do
1447
+ requires :email_address, as: :email
1448
+ requires :password
1449
+ end
1450
+ post do
1451
+ User.create!(declared(params)) # User takes email and password
1452
+ end
1453
+ end
1454
+ ```
1455
+
1456
+ The value passed to `as` will be the key when calling `params` or `declared(params)`.
1096
1457
 
1097
1458
  ### Built-in Validators
1098
1459
 
@@ -1160,7 +1521,8 @@ end
1160
1521
 
1161
1522
  Alternatively, a Proc with arity one (i.e. taking one argument) can be used to explicitly validate
1162
1523
  each parameter value. In that case, the Proc is expected to return a truthy value if the parameter
1163
- value is valid.
1524
+ value is valid. The parameter will be considered invalid if the Proc returns a falsy value or if it
1525
+ raises a StandardError.
1164
1526
 
1165
1527
  ```ruby
1166
1528
  params do
@@ -1170,6 +1532,14 @@ end
1170
1532
 
1171
1533
  While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
1172
1534
 
1535
+ 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`.
1536
+
1537
+ ```ruby
1538
+ params do
1539
+ requires :state, type: Symbol, values: [:active, :inactive]
1540
+ end
1541
+ ```
1542
+
1173
1543
  #### `except_values`
1174
1544
 
1175
1545
  Parameters can be restricted from having a specific set of values with the `:except_values` option.
@@ -1186,6 +1556,17 @@ params do
1186
1556
  end
1187
1557
  ```
1188
1558
 
1559
+ #### `same_as`
1560
+
1561
+ A `same_as` option can be given to ensure that values of parameters match.
1562
+
1563
+ ```ruby
1564
+ params do
1565
+ requires :password
1566
+ requires :password_confirmation, same_as: :password
1567
+ end
1568
+ ```
1569
+
1189
1570
  #### `regexp`
1190
1571
 
1191
1572
  Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
@@ -1245,6 +1626,8 @@ params do
1245
1626
  end
1246
1627
  ```
1247
1628
 
1629
+ Note that using `:default` with `mutually_exclusive` will cause multiple parameters to always have a default value and raise a `Grape::Exceptions::Validation` mutually exclusive exception.
1630
+
1248
1631
  #### `at_least_one_of`
1249
1632
 
1250
1633
  Parameters can be defined as 'at_least_one_of', ensuring that at least one parameter gets selected.
@@ -1421,7 +1804,7 @@ params do
1421
1804
  end
1422
1805
  ```
1423
1806
 
1424
- Every validation will have it's own instance of the validator, which means that the validator can have a state.
1807
+ Every validation will have its own instance of the validator, which means that the validator can have a state.
1425
1808
 
1426
1809
  ### Validation Errors
1427
1810
 
@@ -1482,6 +1865,8 @@ end
1482
1865
  Grape supports I18n for parameter-related error messages, but will fallback to English if
1483
1866
  translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
1484
1867
 
1868
+ 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.
1869
+
1485
1870
  ### Custom Validation messages
1486
1871
 
1487
1872
  Grape supports custom validation messages for parameter-related and coerce-related error messages.
@@ -1493,6 +1878,16 @@ params do
1493
1878
  requires :name, values: { value: 1..10, message: 'not in range from 1 to 10' }, allow_blank: { value: false, message: 'cannot be blank' }, regexp: { value: /^[a-z]+$/, message: 'format is invalid' }, message: 'is required'
1494
1879
  end
1495
1880
  ```
1881
+
1882
+ #### `same_as`
1883
+
1884
+ ```ruby
1885
+ params do
1886
+ requires :password
1887
+ requires :password_confirmation, same_as: { value: :password, message: 'not match' }
1888
+ end
1889
+ ```
1890
+
1496
1891
  #### `all_or_none_of`
1497
1892
 
1498
1893
  ```ruby
@@ -1514,6 +1909,7 @@ params do
1514
1909
  mutually_exclusive :beer, :wine, :juice, message: "are mutually exclusive cannot pass both params"
1515
1910
  end
1516
1911
  ```
1912
+
1517
1913
  #### `exactly_one_of`
1518
1914
 
1519
1915
  ```ruby
@@ -1521,9 +1917,10 @@ params do
1521
1917
  optional :beer
1522
1918
  optional :wine
1523
1919
  optional :juice
1524
- 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"}
1920
+ 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" }
1525
1921
  end
1526
1922
  ```
1923
+
1527
1924
  #### `at_least_one_of`
1528
1925
 
1529
1926
  ```ruby
@@ -1534,13 +1931,15 @@ params do
1534
1931
  at_least_one_of :beer, :wine, :juice, message: "are missing, please specify at least one param"
1535
1932
  end
1536
1933
  ```
1934
+
1537
1935
  #### `Coerce`
1538
1936
 
1539
1937
  ```ruby
1540
1938
  params do
1541
- requires :int, type: {value: Integer, message: "type cast is invalid" }
1939
+ requires :int, type: { value: Integer, message: "type cast is invalid" }
1542
1940
  end
1543
1941
  ```
1942
+
1544
1943
  #### `With Lambdas`
1545
1944
 
1546
1945
  ```ruby
@@ -1548,6 +1947,7 @@ params do
1548
1947
  requires :name, values: { value: -> { (1..10).to_a }, message: 'not in range from 1 to 10' }
1549
1948
  end
1550
1949
  ```
1950
+
1551
1951
  #### `Pass symbols for i18n translations`
1552
1952
 
1553
1953
  You can pass a symbol if you want i18n translations for your custom validation messages.
@@ -1568,7 +1968,7 @@ en:
1568
1968
  name_required: 'must be present'
1569
1969
  ```
1570
1970
 
1571
- #### `Overriding attribute names`
1971
+ #### Overriding Attribute Names
1572
1972
 
1573
1973
  You can also override attribute names.
1574
1974
 
@@ -1586,7 +1986,7 @@ en:
1586
1986
  ```
1587
1987
  Will produce 'Oops! Name must be present'
1588
1988
 
1589
- #### `With Default`
1989
+ #### With Default
1590
1990
 
1591
1991
  You cannot set a custom message option for Default as it requires interpolation `%{option1}: %{value1} is incompatible with %{option2}: %{value2}`. You can change the default error message for Default by changing the `incompatible_option_values` message key inside [en.yml](lib/grape/locale/en.yml)
1592
1992
 
@@ -1595,8 +1995,10 @@ params do
1595
1995
  requires :name, values: { value: -> { (1..10).to_a }, message: 'not in range from 1 to 10' }, default: 5
1596
1996
  end
1597
1997
  ```
1998
+
1598
1999
  ## Headers
1599
2000
 
2001
+ ### Request
1600
2002
  Request headers are available through the `headers` helper or from `env` in their original form.
1601
2003
 
1602
2004
  ```ruby
@@ -1611,13 +2013,30 @@ get do
1611
2013
  end
1612
2014
  ```
1613
2015
 
2016
+ #### Header Case Handling
2017
+
2018
+ The above example may have been requested as follows:
2019
+
2020
+ ``` shell
2021
+ curl -H "secret_PassWord: swordfish" ...
2022
+ ```
2023
+
2024
+ The header name will have been normalized for you.
2025
+
2026
+ - In the `header` helper names will be coerced into a capitalized kebab case.
2027
+ - In the `env` collection they appear in all uppercase, in snake case, and prefixed with 'HTTP_'.
2028
+
2029
+ 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.
2030
+
2031
+ ### Response
2032
+
1614
2033
  You can set a response header with `header` inside an API.
1615
2034
 
1616
2035
  ```ruby
1617
2036
  header 'X-Robots-Tag', 'noindex'
1618
2037
  ```
1619
2038
 
1620
- When raising `error!`, pass additional headers as arguments.
2039
+ When raising `error!`, pass additional headers as arguments. Additional headers will be merged with headers set before `error!` call.
1621
2040
 
1622
2041
  ```ruby
1623
2042
  error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
@@ -1625,6 +2044,56 @@ error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
1625
2044
 
1626
2045
  ## Routes
1627
2046
 
2047
+ 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`.
2048
+ Parts of the path that are denoted with a colon will be interpreted as route parameters.
2049
+
2050
+ ```ruby
2051
+ route :get, 'status' do
2052
+ end
2053
+
2054
+ # is the same as
2055
+
2056
+ get 'status' do
2057
+ end
2058
+
2059
+ # is the same as
2060
+
2061
+ get :status do
2062
+ end
2063
+
2064
+ # is NOT the same as
2065
+
2066
+ get ':status' do # this makes params[:status] available
2067
+ end
2068
+
2069
+ # This will make both params[:status_id] and params[:id] available
2070
+
2071
+ get 'statuses/:status_id/reviews/:id' do
2072
+ end
2073
+ ```
2074
+
2075
+ 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.
2076
+
2077
+ 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.
2078
+
2079
+ ```ruby
2080
+ route_param :id, type: Integer do
2081
+ get 'status' do
2082
+ end
2083
+ end
2084
+
2085
+ # is the same as
2086
+
2087
+ namespace ':id' do
2088
+ params do
2089
+ requires :id, type: Integer
2090
+ end
2091
+
2092
+ get 'status' do
2093
+ end
2094
+ end
2095
+ ```
2096
+
1628
2097
  Optionally, you can define requirements for your named route parameters using regular
1629
2098
  expressions on namespace or endpoint. The route will match only if all requirements are met.
1630
2099
 
@@ -1745,8 +2214,8 @@ module SharedParams
1745
2214
  extend Grape::API::Helpers
1746
2215
 
1747
2216
  params :order do |options|
1748
- optional :order_by, type:Symbol, values:options[:order_by], default:options[:default_order_by]
1749
- optional :order, type:Symbol, values:%i(asc desc), default:options[:default_order]
2217
+ optional :order_by, type: Symbol, values: options[:order_by], default: options[:default_order_by]
2218
+ optional :order, type: Symbol, values: %i(asc desc), default: options[:default_order]
1750
2219
  end
1751
2220
  end
1752
2221
 
@@ -1755,7 +2224,7 @@ class API < Grape::API
1755
2224
 
1756
2225
  desc 'Get a sorted collection.'
1757
2226
  params do
1758
- use :order, order_by:%i(id created_at), default_order_by: :created_at, default_order: :asc
2227
+ use :order, order_by: %i(id created_at), default_order_by: :created_at, default_order: :asc
1759
2228
  end
1760
2229
 
1761
2230
  get do
@@ -1957,6 +2426,12 @@ instead of a message.
1957
2426
  error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
1958
2427
  ```
1959
2428
 
2429
+ You can set additional headers for the response. They will be merged with headers set before `error!` call.
2430
+
2431
+ ```ruby
2432
+ error!('Something went wrong', 500, 'X-Error-Detail' => 'Invalid token.')
2433
+ ```
2434
+
1960
2435
  You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
1961
2436
 
1962
2437
  ```ruby
@@ -2015,7 +2490,7 @@ literally accepts every request.
2015
2490
 
2016
2491
  ## Exception Handling
2017
2492
 
2018
- Grape can be told to rescue all exceptions and return them in the API format.
2493
+ Grape can be told to rescue all `StandardError` exceptions and return them in the API format.
2019
2494
 
2020
2495
  ```ruby
2021
2496
  class Twitter::API < Grape::API
@@ -2023,6 +2498,9 @@ class Twitter::API < Grape::API
2023
2498
  end
2024
2499
  ```
2025
2500
 
2501
+ This mimics [default `rescue` behaviour](https://ruby-doc.org/core/StandardError.html) when an exception type is not provided.
2502
+ Any other exception should be rescued explicitly, see [below](#exceptions-that-should-be-rescued-explicitly).
2503
+
2026
2504
  Grape can also rescue from all exceptions and still use the built-in exception handing.
2027
2505
  This will give the same behavior as `rescue_from :all` with the addition that Grape will use the exception handling defined by all Exception classes that inherit `Grape::Exceptions::Base`.
2028
2506
 
@@ -2062,7 +2540,7 @@ Custom error formatters for existing and additional types can be defined with a
2062
2540
 
2063
2541
  ```ruby
2064
2542
  class Twitter::API < Grape::API
2065
- error_formatter :txt, ->(message, backtrace, options, env) {
2543
+ error_formatter :txt, ->(message, backtrace, options, env, original_exception) {
2066
2544
  "error: #{message} from #{backtrace}"
2067
2545
  }
2068
2546
  end
@@ -2072,7 +2550,7 @@ You can also use a module or class.
2072
2550
 
2073
2551
  ```ruby
2074
2552
  module CustomFormatter
2075
- def self.call(message, backtrace, options, env)
2553
+ def self.call(message, backtrace, options, env, original_exception)
2076
2554
  { message: message, backtrace: backtrace }
2077
2555
  end
2078
2556
  end
@@ -2108,7 +2586,7 @@ You can also rescue all exceptions with a code block and handle the Rack respons
2108
2586
  ```ruby
2109
2587
  class Twitter::API < Grape::API
2110
2588
  rescue_from :all do |e|
2111
- Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
2589
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
2112
2590
  end
2113
2591
  end
2114
2592
  ```
@@ -2121,8 +2599,8 @@ class Twitter::API < Grape::API
2121
2599
  error!("ArgumentError: #{e.message}")
2122
2600
  end
2123
2601
 
2124
- rescue_from NotImplementedError do |e|
2125
- error!("NotImplementedError: #{e.message}")
2602
+ rescue_from NoMethodError do |e|
2603
+ error!("NoMethodError: #{e.message}")
2126
2604
  end
2127
2605
  end
2128
2606
  ```
@@ -2179,9 +2657,9 @@ class Twitter::API < Grape::API
2179
2657
  end
2180
2658
  ```
2181
2659
 
2182
- The `rescue_from` block must return a `Rack::Response` object, call `error!` or re-raise an exception.
2660
+ 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).
2183
2661
 
2184
- The `with` keyword is available as `rescue_from` options, it can be passed method name or Proc object.
2662
+ Alternately, use the `with` option in `rescue_from` to specify a method or a `proc`.
2185
2663
 
2186
2664
  ```ruby
2187
2665
  class Twitter::API < Grape::API
@@ -2197,6 +2675,17 @@ class Twitter::API < Grape::API
2197
2675
  end
2198
2676
  ```
2199
2677
 
2678
+ Inside the `rescue_from` block, the environment of the original controller method(`.self` receiver) is accessible through the `#context` method.
2679
+
2680
+ ```ruby
2681
+ class Twitter::API < Grape::API
2682
+ rescue_from :all do |e|
2683
+ user_id = context.params[:user_id]
2684
+ error!("error for #{user_id}")
2685
+ end
2686
+ end
2687
+ ```
2688
+
2200
2689
  #### Rescuing exceptions inside namespaces
2201
2690
 
2202
2691
  You could put `rescue_from` clauses inside a namespace and they will take precedence over ones
@@ -2219,12 +2708,18 @@ class Twitter::API < Grape::API
2219
2708
  end
2220
2709
  ```
2221
2710
 
2222
- Here `'inner'` will be result of handling occured `ArgumentError`.
2711
+ Here `'inner'` will be result of handling occurred `ArgumentError`.
2223
2712
 
2224
2713
  #### Unrescuable Exceptions
2225
2714
 
2226
2715
  `Grape::Exceptions::InvalidVersionHeader`, which is raised when the version in the request header doesn't match the currently evaluated version for the endpoint, will _never_ be rescued from a `rescue_from` block (even a `rescue_from :all`) This is because Grape relies on Rack to catch that error and try the next versioned-route for cases where there exist identical Grape endpoints with different versions.
2227
2716
 
2717
+ #### Exceptions that should be rescued explicitly
2718
+
2719
+ Any exception that is not subclass of `StandardError` should be rescued explicitly.
2720
+ Usually it is not a case for an application logic as such errors point to problems in Ruby runtime.
2721
+ This is following [standard recommendations for exceptions handling](https://ruby-doc.org/core/Exception.html).
2722
+
2228
2723
  ### Rails 3.x
2229
2724
 
2230
2725
  When mounted inside containers, such as Rails 3.x, errors such as "404 Not Found" or
@@ -2261,7 +2756,6 @@ class API < Grape::API
2261
2756
  end
2262
2757
  end
2263
2758
  post '/statuses' do
2264
- # ...
2265
2759
  logger.info "#{current_user} has statused"
2266
2760
  end
2267
2761
  end
@@ -2471,6 +2965,9 @@ Built-in formatters are the following.
2471
2965
  * `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
2472
2966
  * `:binary`: data will be returned "as is"
2473
2967
 
2968
+ If a body is present in a request to an API, with a Content-Type header value that is of an unsupported type a
2969
+ "415 Unsupported Media Type" error code will be returned by Grape.
2970
+
2474
2971
  Response statuses that indicate no content as defined by [Rack](https://github.com/rack)
2475
2972
  [here](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
2476
2973
  will bypass serialization and the body entity - though there should be none -
@@ -2742,17 +3239,19 @@ end
2742
3239
 
2743
3240
  Use `body false` to return `204 No Content` without any data or content-type.
2744
3241
 
2745
- You can also set the response to a file with `file`.
3242
+ You can also set the response to a file with `sendfile`. This works with the
3243
+ [Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send
3244
+ the file through your web server software.
2746
3245
 
2747
3246
  ```ruby
2748
3247
  class API < Grape::API
2749
3248
  get '/' do
2750
- file '/path/to/file'
3249
+ sendfile '/path/to/file'
2751
3250
  end
2752
3251
  end
2753
3252
  ```
2754
3253
 
2755
- If you want a file to be streamed using Rack::Chunked, use `stream`.
3254
+ To stream a file in chunks use `stream`
2756
3255
 
2757
3256
  ```ruby
2758
3257
  class API < Grape::API
@@ -2762,6 +3261,26 @@ class API < Grape::API
2762
3261
  end
2763
3262
  ```
2764
3263
 
3264
+ If you want to stream non-file data use the `stream` method and a `Stream` object.
3265
+ This is an object that responds to `each` and yields for each chunk to send to the client.
3266
+ Each chunk will be sent as it is yielded instead of waiting for all of the content to be available.
3267
+
3268
+ ```ruby
3269
+ class MyStream
3270
+ def each
3271
+ yield 'part 1'
3272
+ yield 'part 2'
3273
+ yield 'part 3'
3274
+ end
3275
+ end
3276
+
3277
+ class API < Grape::API
3278
+ get '/' do
3279
+ stream MyStream.new
3280
+ end
3281
+ end
3282
+ ```
3283
+
2765
3284
  ## Authentication
2766
3285
 
2767
3286
  ### Basic and Digest Auth
@@ -2773,14 +3292,13 @@ applies to the current namespace and any children, but not parents.
2773
3292
  ```ruby
2774
3293
  http_basic do |username, password|
2775
3294
  # verify user's password here
2776
- { 'test' => 'password1' }[username] == password
3295
+ # IMPORTANT: make sure you use a comparison method which isn't prone to a timing attack
2777
3296
  end
2778
3297
  ```
2779
3298
 
2780
3299
  ```ruby
2781
3300
  http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
2782
3301
  # lookup the user's password here
2783
- { 'user1' => 'password1' }[username]
2784
3302
  end
2785
3303
  ```
2786
3304
 
@@ -2811,7 +3329,9 @@ end
2811
3329
 
2812
3330
  ```
2813
3331
 
2814
- Use [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
3332
+ 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.
3333
+
3334
+ 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`.
2815
3335
 
2816
3336
  ## Describing and Inspecting an API
2817
3337
 
@@ -2880,19 +3400,22 @@ class ApiLogger < Grape::Middleware::Base
2880
3400
  end
2881
3401
  ```
2882
3402
 
2883
- ## Before and After
3403
+ ## Before, After and Finally
2884
3404
 
2885
3405
  Blocks can be executed before or after every API call, using `before`, `after`,
2886
3406
  `before_validation` and `after_validation`.
3407
+ If the API fails the `after` call will not be triggered, if you need code to execute for sure
3408
+ use the `finally`.
2887
3409
 
2888
3410
  Before and after callbacks execute in the following order:
2889
3411
 
2890
3412
  1. `before`
2891
3413
  2. `before_validation`
2892
3414
  3. _validations_
2893
- 4. `after_validation`
2894
- 5. _the API call_
2895
- 6. `after`
3415
+ 4. `after_validation` (upon successful validation)
3416
+ 5. _the API call_ (upon successful validation)
3417
+ 6. `after` (upon successful validation and API call)
3418
+ 7. `finally` (always)
2896
3419
 
2897
3420
  Steps 4, 5 and 6 only happen if validation succeeds.
2898
3421
 
@@ -2904,9 +3427,7 @@ If a request for a resource is made that triggers the built-in `OPTIONS` handler
2904
3427
  only `before` and `after` callbacks will be executed. The remaining callbacks will
2905
3428
  be bypassed.
2906
3429
 
2907
- #### Examples
2908
-
2909
- Using a simple `before` block to set a header
3430
+ For example, using a simple `before` block to set a header.
2910
3431
 
2911
3432
  ```ruby
2912
3433
  before do
@@ -2914,6 +3435,14 @@ before do
2914
3435
  end
2915
3436
  ```
2916
3437
 
3438
+ You can ensure a block of code runs after every request (including failures) with `finally`:
3439
+
3440
+ ```ruby
3441
+ finally do
3442
+ # this code will run after every request (successful or failed)
3443
+ end
3444
+ ```
3445
+
2917
3446
  **Namespaces**
2918
3447
 
2919
3448
  Callbacks apply to each API call within and below the current namespace:
@@ -2942,7 +3471,7 @@ class MyAPI < Grape::API
2942
3471
  end
2943
3472
  ```
2944
3473
 
2945
- The behaviour is then:
3474
+ The behavior is then:
2946
3475
 
2947
3476
  ```bash
2948
3477
  GET / # 'root - '
@@ -2970,7 +3499,7 @@ class MyAPI < Grape::API
2970
3499
  end
2971
3500
  ```
2972
3501
 
2973
- The behaviour is then:
3502
+ The behavior is then:
2974
3503
 
2975
3504
  ```bash
2976
3505
  GET /123 # 'Integer'
@@ -3005,7 +3534,7 @@ class Test < Grape::API
3005
3534
  end
3006
3535
  ```
3007
3536
 
3008
- The behaviour is then:
3537
+ The behavior is then:
3009
3538
 
3010
3539
  ```bash
3011
3540
  GET /foo/v1 # 'v1-hello'
@@ -3030,7 +3559,7 @@ class MyAPI < Grape::API
3030
3559
  end
3031
3560
  ```
3032
3561
 
3033
- The behaviour is then:
3562
+ The behavior is then:
3034
3563
 
3035
3564
  ```bash
3036
3565
  GET /greeting # {"greeting":"Hello!"}
@@ -3116,11 +3645,21 @@ class API < Grape::API
3116
3645
  end
3117
3646
  ```
3118
3647
 
3648
+ You can access the controller params, headers, and helpers through the context with the `#context` method inside any middleware inherited from `Grape::Middleware::Base`.
3649
+
3119
3650
  ### Rails Middleware
3120
3651
 
3121
3652
  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.
3122
3653
  You only have to implement the helpers to access the specific `env` variable.
3123
3654
 
3655
+ 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.
3656
+
3657
+ ```ruby
3658
+ class Company::Application < Rails::Application
3659
+ config.middleware.insert_before(Rack::Attack, Middleware::ApiLogger)
3660
+ end
3661
+ ```
3662
+
3124
3663
  ### Remote IP
3125
3664
 
3126
3665
  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`.
@@ -3318,7 +3857,7 @@ describe 'an endpoint that needs helpers stubbed' do
3318
3857
  end
3319
3858
 
3320
3859
  it 'stubs the helper' do
3321
- # ...
3860
+
3322
3861
  end
3323
3862
  end
3324
3863
  ```
@@ -3355,6 +3894,22 @@ if Rails.env.development?
3355
3894
  end
3356
3895
  ```
3357
3896
 
3897
+ For Rails >= 5.1.4, change this:
3898
+
3899
+ ```ruby
3900
+ ActionDispatch::Callbacks.to_prepare do
3901
+ api_reloader.execute_if_updated
3902
+ end
3903
+ ```
3904
+
3905
+ to this:
3906
+
3907
+ ```ruby
3908
+ ActiveSupport::Reloader.to_prepare do
3909
+ api_reloader.execute_if_updated
3910
+ end
3911
+ ```
3912
+
3358
3913
  See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request/4368838#4368838) for more information.
3359
3914
 
3360
3915
  ## Performance Monitoring
@@ -3391,6 +3946,13 @@ The execution of validators.
3391
3946
  * *validators* - The validators being executed
3392
3947
  * *request* - The request being validated
3393
3948
 
3949
+ #### format_response.grape
3950
+
3951
+ Serialization or template rendering.
3952
+
3953
+ * *env* - The request environment
3954
+ * *formatter* - The formatter object (e.g., `Grape::Formatter::Json`)
3955
+
3394
3956
  See the [ActiveSupport::Notifications documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) for information on how to subscribe to these events.
3395
3957
 
3396
3958
  ### Monitoring Products
@@ -3401,6 +3963,8 @@ Grape integrates with following third-party tools:
3401
3963
  * **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
3402
3964
  * **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
3403
3965
  * **[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)
3966
+ * **[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)
3967
+ * **[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)
3404
3968
 
3405
3969
  ## Contributing to Grape
3406
3970
 
@@ -3409,10 +3973,14 @@ features and discuss issues.
3409
3973
 
3410
3974
  See [CONTRIBUTING](CONTRIBUTING.md).
3411
3975
 
3976
+ ## Security
3977
+
3978
+ See [SECURITY](SECURITY.md) for details.
3979
+
3412
3980
  ## License
3413
3981
 
3414
- MIT License. See LICENSE for details.
3982
+ MIT License. See [LICENSE](LICENSE) for details.
3415
3983
 
3416
3984
  ## Copyright
3417
3985
 
3418
- Copyright (c) 2010-2017 Michael Bleigh, and Intridea, Inc.
3986
+ Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.