grape 1.1.0 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +278 -44
  3. data/LICENSE +1 -1
  4. data/README.md +514 -69
  5. data/UPGRADING.md +424 -17
  6. data/grape.gemspec +13 -2
  7. data/lib/grape.rb +104 -71
  8. data/lib/grape/api.rb +138 -175
  9. data/lib/grape/api/helpers.rb +2 -0
  10. data/lib/grape/api/instance.rb +283 -0
  11. data/lib/grape/config.rb +34 -0
  12. data/lib/grape/content_types.rb +34 -0
  13. data/lib/grape/cookies.rb +2 -0
  14. data/lib/grape/dsl/api.rb +2 -0
  15. data/lib/grape/dsl/callbacks.rb +22 -0
  16. data/lib/grape/dsl/configuration.rb +2 -0
  17. data/lib/grape/dsl/desc.rb +41 -7
  18. data/lib/grape/dsl/headers.rb +2 -0
  19. data/lib/grape/dsl/helpers.rb +5 -2
  20. data/lib/grape/dsl/inside_route.rb +92 -49
  21. data/lib/grape/dsl/logger.rb +2 -0
  22. data/lib/grape/dsl/middleware.rb +9 -0
  23. data/lib/grape/dsl/parameters.rb +25 -14
  24. data/lib/grape/dsl/request_response.rb +4 -2
  25. data/lib/grape/dsl/routing.rb +17 -10
  26. data/lib/grape/dsl/settings.rb +7 -1
  27. data/lib/grape/dsl/validations.rb +24 -4
  28. data/lib/grape/eager_load.rb +20 -0
  29. data/lib/grape/endpoint.rb +59 -35
  30. data/lib/grape/error_formatter.rb +4 -2
  31. data/lib/grape/error_formatter/base.rb +2 -0
  32. data/lib/grape/error_formatter/json.rb +2 -0
  33. data/lib/grape/error_formatter/txt.rb +2 -0
  34. data/lib/grape/error_formatter/xml.rb +2 -0
  35. data/lib/grape/exceptions/base.rb +20 -14
  36. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  37. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  38. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  39. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  40. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  41. data/lib/grape/exceptions/invalid_response.rb +11 -0
  42. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  43. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  44. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  45. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  46. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  47. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  48. data/lib/grape/exceptions/missing_option.rb +2 -0
  49. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  50. data/lib/grape/exceptions/unknown_options.rb +2 -0
  51. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  52. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  53. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  54. data/lib/grape/exceptions/validation.rb +4 -2
  55. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  56. data/lib/grape/exceptions/validation_errors.rb +16 -13
  57. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  58. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  59. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  60. data/lib/grape/extensions/hash.rb +2 -0
  61. data/lib/grape/extensions/hashie/mash.rb +2 -0
  62. data/lib/grape/formatter.rb +5 -3
  63. data/lib/grape/formatter/json.rb +2 -0
  64. data/lib/grape/formatter/serializable_hash.rb +2 -0
  65. data/lib/grape/formatter/txt.rb +2 -0
  66. data/lib/grape/formatter/xml.rb +2 -0
  67. data/lib/grape/http/headers.rb +50 -18
  68. data/lib/grape/locale/en.yml +3 -1
  69. data/lib/grape/middleware/auth/base.rb +7 -7
  70. data/lib/grape/middleware/auth/dsl.rb +2 -0
  71. data/lib/grape/middleware/auth/strategies.rb +2 -0
  72. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  73. data/lib/grape/middleware/base.rb +10 -7
  74. data/lib/grape/middleware/error.rb +21 -16
  75. data/lib/grape/middleware/filter.rb +2 -0
  76. data/lib/grape/middleware/formatter.rb +8 -6
  77. data/lib/grape/middleware/globals.rb +2 -0
  78. data/lib/grape/middleware/helpers.rb +12 -0
  79. data/lib/grape/middleware/stack.rb +13 -3
  80. data/lib/grape/middleware/versioner.rb +2 -0
  81. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  82. data/lib/grape/middleware/versioner/header.rb +10 -8
  83. data/lib/grape/middleware/versioner/param.rb +3 -1
  84. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
  85. data/lib/grape/middleware/versioner/path.rb +3 -1
  86. data/lib/grape/namespace.rb +14 -2
  87. data/lib/grape/parser.rb +4 -2
  88. data/lib/grape/parser/json.rb +3 -1
  89. data/lib/grape/parser/xml.rb +3 -1
  90. data/lib/grape/path.rb +15 -3
  91. data/lib/grape/presenters/presenter.rb +2 -0
  92. data/lib/grape/request.rb +19 -10
  93. data/lib/grape/router.rb +30 -29
  94. data/lib/grape/router/attribute_translator.rb +41 -8
  95. data/lib/grape/router/pattern.rb +20 -16
  96. data/lib/grape/router/route.rb +14 -28
  97. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  98. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  99. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  100. data/lib/grape/util/base_inheritable.rb +43 -0
  101. data/lib/grape/util/cache.rb +20 -0
  102. data/lib/grape/util/endpoint_configuration.rb +8 -0
  103. data/lib/grape/util/env.rb +19 -17
  104. data/lib/grape/util/inheritable_setting.rb +2 -0
  105. data/lib/grape/util/inheritable_values.rb +7 -25
  106. data/lib/grape/util/json.rb +2 -0
  107. data/lib/grape/util/lazy_block.rb +27 -0
  108. data/lib/grape/util/lazy_object.rb +43 -0
  109. data/lib/grape/util/lazy_value.rb +98 -0
  110. data/lib/grape/util/registrable.rb +2 -0
  111. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  112. data/lib/grape/util/stackable_values.rb +21 -34
  113. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  114. data/lib/grape/util/xml.rb +2 -0
  115. data/lib/grape/validations.rb +2 -0
  116. data/lib/grape/validations/attributes_iterator.rb +16 -6
  117. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  118. data/lib/grape/validations/params_scope.rb +51 -30
  119. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  120. data/lib/grape/validations/types.rb +13 -38
  121. data/lib/grape/validations/types/array_coercer.rb +65 -0
  122. data/lib/grape/validations/types/build_coercer.rb +47 -49
  123. data/lib/grape/validations/types/custom_type_coercer.rb +29 -51
  124. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  125. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  126. data/lib/grape/validations/types/file.rb +22 -18
  127. data/lib/grape/validations/types/invalid_value.rb +24 -0
  128. data/lib/grape/validations/types/json.rb +46 -39
  129. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  130. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  131. data/lib/grape/validations/types/set_coercer.rb +40 -0
  132. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  133. data/lib/grape/validations/validator_factory.rb +8 -11
  134. data/lib/grape/validations/validators/all_or_none.rb +8 -13
  135. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  136. data/lib/grape/validations/validators/as.rb +5 -4
  137. data/lib/grape/validations/validators/at_least_one_of.rb +7 -13
  138. data/lib/grape/validations/validators/base.rb +20 -16
  139. data/lib/grape/validations/validators/coerce.rb +46 -29
  140. data/lib/grape/validations/validators/default.rb +6 -6
  141. data/lib/grape/validations/validators/exactly_one_of.rb +10 -23
  142. data/lib/grape/validations/validators/except_values.rb +4 -2
  143. data/lib/grape/validations/validators/multiple_params_base.rb +17 -10
  144. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -18
  145. data/lib/grape/validations/validators/presence.rb +3 -1
  146. data/lib/grape/validations/validators/regexp.rb +4 -2
  147. data/lib/grape/validations/validators/same_as.rb +26 -0
  148. data/lib/grape/validations/validators/values.rb +18 -6
  149. data/lib/grape/version.rb +3 -1
  150. data/spec/grape/api/custom_validations_spec.rb +5 -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 +2 -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 +2 -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 +565 -12
  169. data/spec/grape/config_spec.rb +19 -0
  170. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  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 +4 -2
  175. data/spec/grape/dsl/inside_route_spec.rb +184 -33
  176. data/spec/grape/dsl/logger_spec.rb +2 -0
  177. data/spec/grape/dsl/middleware_spec.rb +10 -0
  178. data/spec/grape/dsl/parameters_spec.rb +2 -0
  179. data/spec/grape/dsl/request_response_spec.rb +2 -0
  180. data/spec/grape/dsl/routing_spec.rb +12 -0
  181. data/spec/grape/dsl/settings_spec.rb +2 -0
  182. data/spec/grape/dsl/validations_spec.rb +2 -0
  183. data/spec/grape/endpoint/declared_spec.rb +601 -0
  184. data/spec/grape/endpoint_spec.rb +53 -523
  185. data/spec/grape/entity_spec.rb +9 -1
  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 -0
  190. data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
  191. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  192. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  193. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  194. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  195. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  196. data/spec/grape/exceptions/validation_errors_spec.rb +8 -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 +5 -3
  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 +3 -1
  210. data/spec/grape/middleware/exception_spec.rb +4 -2
  211. data/spec/grape/middleware/formatter_spec.rb +33 -16
  212. data/spec/grape/middleware/globals_spec.rb +2 -0
  213. data/spec/grape/middleware/stack_spec.rb +12 -0
  214. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  215. data/spec/grape/middleware/versioner/header_spec.rb +9 -1
  216. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  217. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  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 +6 -4
  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 +2 -0
  225. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  226. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  227. data/spec/grape/util/stackable_values_spec.rb +3 -1
  228. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  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 +213 -9
  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 +476 -135
  242. data/spec/grape/validations/validators/default_spec.rb +172 -0
  243. data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
  244. data/spec/grape/validations/validators/except_values_spec.rb +4 -1
  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 +30 -5
  250. data/spec/grape/validations_spec.rb +388 -50
  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 +22 -20
  255. data/spec/spec_helper.rb +12 -1
  256. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  257. data/spec/support/chunks.rb +14 -0
  258. data/spec/support/content_type_helpers.rb +2 -0
  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 +86 -48
  265. data/Appraisals +0 -32
  266. data/Dangerfile +0 -2
  267. data/Gemfile +0 -33
  268. data/Gemfile.lock +0 -231
  269. data/Guardfile +0 -10
  270. data/RELEASING.md +0 -111
  271. data/Rakefile +0 -25
  272. data/benchmark/simple.rb +0 -27
  273. data/benchmark/simple_with_type_coercer.rb +0 -22
  274. data/gemfiles/multi_json.gemfile +0 -35
  275. data/gemfiles/multi_xml.gemfile +0 -35
  276. data/gemfiles/rack_1.5.2.gemfile +0 -35
  277. data/gemfiles/rack_edge.gemfile +0 -35
  278. data/gemfiles/rails_3.gemfile +0 -36
  279. data/gemfiles/rails_4.gemfile +0 -35
  280. data/gemfiles/rails_5.gemfile +0 -35
  281. data/gemfiles/rails_edge.gemfile +0 -35
  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
  285. data/pkg/grape-0.17.0.gem +0 -0
  286. data/pkg/grape-0.19.0.gem +0 -0
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010-2018 Michael Bleigh, Intridea Inc. and Contributors.
1
+ Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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,20 +12,29 @@
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)
@@ -38,38 +45,43 @@
38
45
  - [Integer/Fixnum and Coercions](#integerfixnum-and-coercions)
39
46
  - [Custom Types and Coercions](#custom-types-and-coercions)
40
47
  - [Multipart File Parameters](#multipart-file-parameters)
41
- - [First-Class `JSON` Types](#first-class-json-types)
48
+ - [First-Class JSON Types](#first-class-json-types)
42
49
  - [Multiple Allowed Types](#multiple-allowed-types)
43
50
  - [Validation of Nested Parameters](#validation-of-nested-parameters)
44
51
  - [Dependent Parameters](#dependent-parameters)
45
52
  - [Group Options](#group-options)
46
- - [Alias](#alias)
53
+ - [Renaming](#renaming)
47
54
  - [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)
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)
57
65
  - [Namespace Validation and Coercion](#namespace-validation-and-coercion)
58
66
  - [Custom Validators](#custom-validators)
59
67
  - [Validation Errors](#validation-errors)
60
68
  - [I18n](#i18n)
61
69
  - [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)
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)
70
79
  - [Overriding Attribute Names](#overriding-attribute-names)
71
80
  - [With Default](#with-default)
72
81
  - [Headers](#headers)
82
+ - [Request](#request)
83
+ - [Header Case Handling](#header-case-handling)
84
+ - [Response](#response)
73
85
  - [Routes](#routes)
74
86
  - [Helpers](#helpers)
75
87
  - [Path Helpers](#path-helpers)
@@ -105,7 +117,7 @@
105
117
  - [Register custom middleware for authentication](#register-custom-middleware-for-authentication)
106
118
  - [Describing and Inspecting an API](#describing-and-inspecting-an-api)
107
119
  - [Current Route and Endpoint](#current-route-and-endpoint)
108
- - [Before and After](#before-and-after)
120
+ - [Before, After and Finally](#before-after-and-finally)
109
121
  - [Anchoring](#anchoring)
110
122
  - [Using Custom Middleware](#using-custom-middleware)
111
123
  - [Grape Middleware](#grape-middleware)
@@ -132,6 +144,7 @@
132
144
  - [format_response.grape](#format_responsegrape)
133
145
  - [Monitoring Products](#monitoring-products)
134
146
  - [Contributing to Grape](#contributing-to-grape)
147
+ - [Security](#security)
135
148
  - [License](#license)
136
149
  - [Copyright](#copyright)
137
150
 
@@ -145,7 +158,7 @@ content negotiation, versioning and much more.
145
158
 
146
159
  ## Stable Release
147
160
 
148
- You're reading the documentation for the stable release of Grape, **1.1.0**.
161
+ You're reading the documentation for the stable release of Grape, **v1.5.3**.
149
162
  Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
150
163
 
151
164
  ## Project Resources
@@ -155,8 +168,18 @@ Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
155
168
  * Need help? Try [Grape Google Group](http://groups.google.com/group/ruby-grape) or [Gitter](https://gitter.im/ruby-grape/grape)
156
169
  * [Follow us on Twitter](https://twitter.com/grapeframework)
157
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
+
158
179
  ## Installation
159
180
 
181
+ Ruby 2.4 or newer is required.
182
+
160
183
  Grape is available as a gem, to install it just install the gem:
161
184
 
162
185
  gem install grape
@@ -204,7 +227,7 @@ module Twitter
204
227
 
205
228
  desc 'Return a status.'
206
229
  params do
207
- requires :id, type: Integer, desc: 'Status id.'
230
+ requires :id, type: Integer, desc: 'Status ID.'
208
231
  end
209
232
  route_param :id do
210
233
  get do
@@ -252,6 +275,17 @@ end
252
275
 
253
276
  ## Mounting
254
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
+
255
289
  ### Rack
256
290
 
257
291
  The above sample creates a Rack application that can be run from a rackup `config.ru` file
@@ -261,6 +295,13 @@ with `rackup`:
261
295
  run Twitter::API
262
296
  ```
263
297
 
298
+ (With pre-loading you can use)
299
+
300
+ ```ruby
301
+ Twitter::API.compile!
302
+ run Twitter::API
303
+ ```
304
+
264
305
  And would respond to the following routes:
265
306
 
266
307
  GET /api/statuses/public_timeline
@@ -277,13 +318,21 @@ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just
277
318
  If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
278
319
  is handled correctly.
279
320
 
321
+ #### Rails 4
322
+
280
323
  The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
281
324
  `config.ru` before mounting Grape, e.g.:
282
325
 
283
326
  ```ruby
284
327
  use ActiveRecord::ConnectionAdapters::ConnectionManagement
328
+ ```
285
329
 
286
- 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
287
336
  ```
288
337
 
289
338
  ### Alongside Sinatra (or other frameworks)
@@ -310,13 +359,24 @@ class Web < Sinatra::Base
310
359
  end
311
360
 
312
361
  use Rack::Session::Cookie
313
- run Rack::Cascade.new [API, Web]
362
+ run Rack::Cascade.new [Web, API]
314
363
  ```
315
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
+
316
368
  ### Rails
317
369
 
318
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`.
319
371
 
372
+ Modify `config/routes`:
373
+
374
+ ```ruby
375
+ mount Twitter::API => '/'
376
+ ```
377
+
378
+ #### Rails < 5.2
379
+
320
380
  Modify `application.rb`:
321
381
 
322
382
  ```ruby
@@ -324,14 +384,18 @@ config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
324
384
  config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
325
385
  ```
326
386
 
327
- Modify `config/routes`:
387
+ See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
388
+
389
+ #### Rails 6.0
390
+
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:
328
392
 
329
393
  ```ruby
330
- mount Twitter::API => '/'
394
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
395
+ inflect.acronym 'API'
396
+ end
331
397
  ```
332
398
 
333
- See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
334
-
335
399
  ### Modules
336
400
 
337
401
  You can mount multiple API implementations inside another one. These don't have to be
@@ -365,6 +429,131 @@ class Twitter::API < Grape::API
365
429
  end
366
430
  ```
367
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
+
368
557
  ## Versioning
369
558
 
370
559
  There are four strategies in which clients can reach your API's endpoints: `:path`,
@@ -445,10 +634,13 @@ version 'v1', using: :param, parameter: 'v'
445
634
 
446
635
  ## Describing Methods
447
636
 
448
- 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.
449
640
 
450
641
  ```ruby
451
642
  desc 'Returns your public timeline.' do
643
+ summary 'summary'
452
644
  detail 'more details'
453
645
  params API::Entities::Status.documentation
454
646
  success API::Entities::Entity
@@ -462,7 +654,13 @@ desc 'Returns your public timeline.' do
462
654
  description: 'Not really needed',
463
655
  required: false
464
656
  }
465
-
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']
466
664
  end
467
665
  get :public_timeline do
468
666
  Status.limit(20)
@@ -475,6 +673,43 @@ end
475
673
  * `failure`: (former http_codes) A definition of the used failure HTTP Codes and Entities
476
674
  * `named`: A helper to give a route a name and find it with this name in the documentation Hash
477
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).
478
713
 
479
714
  ## Parameters
480
715
 
@@ -553,13 +788,20 @@ params do
553
788
  end
554
789
  ```
555
790
 
791
+ Or globally with the [Configuration](#configuration) `Grape.configure.param_builder`.
792
+
556
793
  In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.
557
794
 
558
795
  Available parameter builders are `Grape::Extensions::Hash::ParamBuilder`, `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder` and `Grape::Extensions::Hashie::Mash::ParamBuilder`.
559
796
 
560
797
  ### Declared
561
798
 
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:
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
+
804
+ Consider the following API endpoint:
563
805
 
564
806
  ````ruby
565
807
  format :json
@@ -592,9 +834,9 @@ Once we add parameters requirements, grape will start returning only the declare
592
834
  format :json
593
835
 
594
836
  params do
595
- requires :user, type: Hash do
596
- requires :first_name, type: String
597
- requires :last_name, type: String
837
+ optional :user, type: Hash do
838
+ optional :first_name, type: String
839
+ optional :last_name, type: String
598
840
  end
599
841
  end
600
842
 
@@ -622,6 +864,44 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
622
864
  }
623
865
  ````
624
866
 
867
+ Missing params that are declared as type `Hash` or `Array` will be included.
868
+
869
+ ````ruby
870
+ format :json
871
+
872
+ params do
873
+ optional :user, type: Hash do
874
+ optional :first_name, type: String
875
+ optional :last_name, type: String
876
+ end
877
+ optional :widgets, type: Array
878
+ end
879
+
880
+ post 'users/signup' do
881
+ { 'declared_params' => declared(params) }
882
+ end
883
+ ````
884
+
885
+ **Request**
886
+
887
+ ````bash
888
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{}'
889
+ ````
890
+
891
+ **Response**
892
+
893
+ ````json
894
+ {
895
+ "declared_params": {
896
+ "user": {
897
+ "first_name": null,
898
+ "last_name": null
899
+ },
900
+ "widgets": []
901
+ }
902
+ }
903
+ ````
904
+
625
905
  The returned hash is an `ActiveSupport::HashWithIndifferentAccess`.
626
906
 
627
907
  The `#declared` method is not available to `before` filters, as those are evaluated prior to parameter coercion.
@@ -680,8 +960,10 @@ By default `declared(params)` includes parameters that have `nil` values. If you
680
960
  format :json
681
961
 
682
962
  params do
683
- requires :first_name, type: String
684
- optional :last_name, type: String
963
+ requires :user, type: Hash do
964
+ requires :first_name, type: String
965
+ optional :last_name, type: String
966
+ end
685
967
  end
686
968
 
687
969
  post 'users/signup' do
@@ -834,13 +1116,13 @@ params do
834
1116
  end
835
1117
  ```
836
1118
 
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
1119
  Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
841
1120
  number for each call to the endpoint of this `params` block. To have the default evaluate
842
1121
  lazily with each request use a lambda, like `:random_number` above.
843
1122
 
1123
+ Note that default values will be passed through to any validation options specified.
1124
+ The following example will always fail if `:color` is not explicitly provided.
1125
+
844
1126
  ```ruby
845
1127
  params do
846
1128
  optional :color, type: String, default: 'blue', values: ['red', 'green']
@@ -900,7 +1182,8 @@ Aside from the default set of supported types listed above, any class can be
900
1182
  used as a type as long as an explicit coercion method is supplied. If the type
901
1183
  implements a class-level `parse` method, Grape will use it automatically.
902
1184
  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.,
1185
+ type, or return an instance of `Grape::Types::InvalidValue` which optionally
1186
+ accepts a message to be returned in the response.
904
1187
 
905
1188
  ```ruby
906
1189
  class Color
@@ -910,8 +1193,9 @@ class Color
910
1193
  end
911
1194
 
912
1195
  def self.parse(value)
913
- fail 'Invalid color' unless %w(blue red green).include?(value)
914
- new(value)
1196
+ return new(value) if %w[blue red green]).include?(value)
1197
+
1198
+ Grape::Types::InvalidValue.new('Unsupported color')
915
1199
  end
916
1200
  end
917
1201
 
@@ -934,7 +1218,7 @@ parameter, and the return value must match the given `type`.
934
1218
 
935
1219
  ```ruby
936
1220
  params do
937
- requires :passwd, type: String, coerce_with: Base64.method(:decode)
1221
+ requires :passwd, type: String, coerce_with: Base64.method(:decode64)
938
1222
  requires :loud_color, type: Color, coerce_with: ->(c) { Color.parse(c.downcase) }
939
1223
 
940
1224
  requires :obj, type: Hash, coerce_with: JSON do
@@ -943,6 +1227,7 @@ params do
943
1227
  end
944
1228
  end
945
1229
  ```
1230
+ Note that, a `nil` value will call the custom coercion method, while a missing parameter will not.
946
1231
 
947
1232
  Example of use of `coerce_with` with a lambda (a class with a `parse` method could also have been used)
948
1233
  It will parse a string and return an Array of Integers, matching the `Array[Integer]` `type`.
@@ -1109,6 +1394,18 @@ params do
1109
1394
  end
1110
1395
  ```
1111
1396
 
1397
+ You can rename parameters:
1398
+
1399
+ ```ruby
1400
+ params do
1401
+ optional :category, as: :type
1402
+ given type: ->(val) { val == 'foo' } do
1403
+ requires :description
1404
+ end
1405
+ end
1406
+ ```
1407
+
1408
+ Note: param in `given` should be the renamed one. In the example, it should be `type`, not `category`.
1112
1409
 
1113
1410
  ### Group Options
1114
1411
 
@@ -1137,9 +1434,9 @@ params do
1137
1434
  end
1138
1435
  ```
1139
1436
 
1140
- ### Alias
1437
+ ### Renaming
1141
1438
 
1142
- You can set an alias for parameters using `as`, which can be useful when refactoring existing APIs:
1439
+ You can rename parameters using `as`, which can be useful when refactoring existing APIs:
1143
1440
 
1144
1441
  ```ruby
1145
1442
  resource :users do
@@ -1248,6 +1545,17 @@ params do
1248
1545
  end
1249
1546
  ```
1250
1547
 
1548
+ #### `same_as`
1549
+
1550
+ A `same_as` option can be given to ensure that values of parameters match.
1551
+
1552
+ ```ruby
1553
+ params do
1554
+ requires :password
1555
+ requires :password_confirmation, same_as: :password
1556
+ end
1557
+ ```
1558
+
1251
1559
  #### `regexp`
1252
1560
 
1253
1561
  Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
@@ -1485,7 +1793,7 @@ params do
1485
1793
  end
1486
1794
  ```
1487
1795
 
1488
- Every validation will have it's own instance of the validator, which means that the validator can have a state.
1796
+ Every validation will have its own instance of the validator, which means that the validator can have a state.
1489
1797
 
1490
1798
  ### Validation Errors
1491
1799
 
@@ -1546,6 +1854,8 @@ end
1546
1854
  Grape supports I18n for parameter-related error messages, but will fallback to English if
1547
1855
  translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
1548
1856
 
1857
+ 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.
1858
+
1549
1859
  ### Custom Validation messages
1550
1860
 
1551
1861
  Grape supports custom validation messages for parameter-related and coerce-related error messages.
@@ -1558,6 +1868,15 @@ params do
1558
1868
  end
1559
1869
  ```
1560
1870
 
1871
+ #### `same_as`
1872
+
1873
+ ```ruby
1874
+ params do
1875
+ requires :password
1876
+ requires :password_confirmation, same_as: { value: :password, message: 'not match' }
1877
+ end
1878
+ ```
1879
+
1561
1880
  #### `all_or_none_of`
1562
1881
 
1563
1882
  ```ruby
@@ -1587,7 +1906,7 @@ params do
1587
1906
  optional :beer
1588
1907
  optional :wine
1589
1908
  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"}
1909
+ 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
1910
  end
1592
1911
  ```
1593
1912
 
@@ -1606,7 +1925,7 @@ end
1606
1925
 
1607
1926
  ```ruby
1608
1927
  params do
1609
- requires :int, type: {value: Integer, message: "type cast is invalid" }
1928
+ requires :int, type: { value: Integer, message: "type cast is invalid" }
1610
1929
  end
1611
1930
  ```
1612
1931
 
@@ -1668,6 +1987,7 @@ end
1668
1987
 
1669
1988
  ## Headers
1670
1989
 
1990
+ ### Request
1671
1991
  Request headers are available through the `headers` helper or from `env` in their original form.
1672
1992
 
1673
1993
  ```ruby
@@ -1682,13 +2002,30 @@ get do
1682
2002
  end
1683
2003
  ```
1684
2004
 
2005
+ #### Header Case Handling
2006
+
2007
+ The above example may have been requested as follows:
2008
+
2009
+ ``` shell
2010
+ curl -H "secret_PassWord: swordfish" ...
2011
+ ```
2012
+
2013
+ The header name will have been normalized for you.
2014
+
2015
+ - In the `header` helper names will be coerced into a capitalized kebab case.
2016
+ - In the `env` collection they appear in all uppercase, in snake case, and prefixed with 'HTTP_'.
2017
+
2018
+ 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.
2019
+
2020
+ ### Response
2021
+
1685
2022
  You can set a response header with `header` inside an API.
1686
2023
 
1687
2024
  ```ruby
1688
2025
  header 'X-Robots-Tag', 'noindex'
1689
2026
  ```
1690
2027
 
1691
- When raising `error!`, pass additional headers as arguments.
2028
+ When raising `error!`, pass additional headers as arguments. Additional headers will be merged with headers set before `error!` call.
1692
2029
 
1693
2030
  ```ruby
1694
2031
  error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
@@ -1696,6 +2033,56 @@ error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
1696
2033
 
1697
2034
  ## Routes
1698
2035
 
2036
+ 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`.
2037
+ Parts of the path that are denoted with a colon will be interpreted as route parameters.
2038
+
2039
+ ```ruby
2040
+ route :get, 'status' do
2041
+ end
2042
+
2043
+ # is the same as
2044
+
2045
+ get 'status' do
2046
+ end
2047
+
2048
+ # is the same as
2049
+
2050
+ get :status do
2051
+ end
2052
+
2053
+ # is NOT the same as
2054
+
2055
+ get ':status' do # this makes params[:status] available
2056
+ end
2057
+
2058
+ # This will make both params[:status_id] and params[:id] available
2059
+
2060
+ get 'statuses/:status_id/reviews/:id' do
2061
+ end
2062
+ ```
2063
+
2064
+ 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.
2065
+
2066
+ 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.
2067
+
2068
+ ```ruby
2069
+ route_param :id, type: Integer do
2070
+ get 'status' do
2071
+ end
2072
+ end
2073
+
2074
+ # is the same as
2075
+
2076
+ namespace ':id' do
2077
+ params do
2078
+ requires :id, type: Integer
2079
+ end
2080
+
2081
+ get 'status' do
2082
+ end
2083
+ end
2084
+ ```
2085
+
1699
2086
  Optionally, you can define requirements for your named route parameters using regular
1700
2087
  expressions on namespace or endpoint. The route will match only if all requirements are met.
1701
2088
 
@@ -2028,6 +2415,12 @@ instead of a message.
2028
2415
  error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
2029
2416
  ```
2030
2417
 
2418
+ You can set additional headers for the response. They will be merged with headers set before `error!` call.
2419
+
2420
+ ```ruby
2421
+ error!('Something went wrong', 500, 'X-Error-Detail' => 'Invalid token.')
2422
+ ```
2423
+
2031
2424
  You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
2032
2425
 
2033
2426
  ```ruby
@@ -2182,7 +2575,7 @@ You can also rescue all exceptions with a code block and handle the Rack respons
2182
2575
  ```ruby
2183
2576
  class Twitter::API < Grape::API
2184
2577
  rescue_from :all do |e|
2185
- Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
2578
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
2186
2579
  end
2187
2580
  end
2188
2581
  ```
@@ -2253,9 +2646,9 @@ class Twitter::API < Grape::API
2253
2646
  end
2254
2647
  ```
2255
2648
 
2256
- The `rescue_from` block must return a `Rack::Response` object, call `error!` or re-raise an exception.
2649
+ 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
2650
 
2258
- The `with` keyword is available as `rescue_from` options, it can be passed method name or Proc object.
2651
+ Alternately, use the `with` option in `rescue_from` to specify a method or a `proc`.
2259
2652
 
2260
2653
  ```ruby
2261
2654
  class Twitter::API < Grape::API
@@ -2271,6 +2664,17 @@ class Twitter::API < Grape::API
2271
2664
  end
2272
2665
  ```
2273
2666
 
2667
+ Inside the `rescue_from` block, the environment of the original controller method(`.self` receiver) is accessible through the `#context` method.
2668
+
2669
+ ```ruby
2670
+ class Twitter::API < Grape::API
2671
+ rescue_from :all do |e|
2672
+ user_id = context.params[:user_id]
2673
+ error!("error for #{user_id}")
2674
+ end
2675
+ end
2676
+ ```
2677
+
2274
2678
  #### Rescuing exceptions inside namespaces
2275
2679
 
2276
2680
  You could put `rescue_from` clauses inside a namespace and they will take precedence over ones
@@ -2293,7 +2697,7 @@ class Twitter::API < Grape::API
2293
2697
  end
2294
2698
  ```
2295
2699
 
2296
- Here `'inner'` will be result of handling occured `ArgumentError`.
2700
+ Here `'inner'` will be result of handling occurred `ArgumentError`.
2297
2701
 
2298
2702
  #### Unrescuable Exceptions
2299
2703
 
@@ -2824,17 +3228,19 @@ end
2824
3228
 
2825
3229
  Use `body false` to return `204 No Content` without any data or content-type.
2826
3230
 
2827
- You can also set the response to a file with `file`.
3231
+ You can also set the response to a file with `sendfile`. This works with the
3232
+ [Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send
3233
+ the file through your web server software.
2828
3234
 
2829
3235
  ```ruby
2830
3236
  class API < Grape::API
2831
3237
  get '/' do
2832
- file '/path/to/file'
3238
+ sendfile '/path/to/file'
2833
3239
  end
2834
3240
  end
2835
3241
  ```
2836
3242
 
2837
- If you want a file to be streamed using Rack::Chunked, use `stream`.
3243
+ To stream a file in chunks use `stream`
2838
3244
 
2839
3245
  ```ruby
2840
3246
  class API < Grape::API
@@ -2844,6 +3250,26 @@ class API < Grape::API
2844
3250
  end
2845
3251
  ```
2846
3252
 
3253
+ If you want to stream non-file data use the `stream` method and a `Stream` object.
3254
+ This is an object that responds to `each` and yields for each chunk to send to the client.
3255
+ Each chunk will be sent as it is yielded instead of waiting for all of the content to be available.
3256
+
3257
+ ```ruby
3258
+ class MyStream
3259
+ def each
3260
+ yield 'part 1'
3261
+ yield 'part 2'
3262
+ yield 'part 3'
3263
+ end
3264
+ end
3265
+
3266
+ class API < Grape::API
3267
+ get '/' do
3268
+ stream MyStream.new
3269
+ end
3270
+ end
3271
+ ```
3272
+
2847
3273
  ## Authentication
2848
3274
 
2849
3275
  ### Basic and Digest Auth
@@ -2855,14 +3281,13 @@ applies to the current namespace and any children, but not parents.
2855
3281
  ```ruby
2856
3282
  http_basic do |username, password|
2857
3283
  # verify user's password here
2858
- { 'test' => 'password1' }[username] == password
3284
+ # IMPORTANT: make sure you use a comparison method which isn't prone to a timing attack
2859
3285
  end
2860
3286
  ```
2861
3287
 
2862
3288
  ```ruby
2863
3289
  http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
2864
3290
  # lookup the user's password here
2865
- { 'user1' => 'password1' }[username]
2866
3291
  end
2867
3292
  ```
2868
3293
 
@@ -2893,7 +3318,9 @@ end
2893
3318
 
2894
3319
  ```
2895
3320
 
2896
- Use [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
3321
+ 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.
3322
+
3323
+ 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
3324
 
2898
3325
  ## Describing and Inspecting an API
2899
3326
 
@@ -2962,19 +3389,22 @@ class ApiLogger < Grape::Middleware::Base
2962
3389
  end
2963
3390
  ```
2964
3391
 
2965
- ## Before and After
3392
+ ## Before, After and Finally
2966
3393
 
2967
3394
  Blocks can be executed before or after every API call, using `before`, `after`,
2968
3395
  `before_validation` and `after_validation`.
3396
+ If the API fails the `after` call will not be triggered, if you need code to execute for sure
3397
+ use the `finally`.
2969
3398
 
2970
3399
  Before and after callbacks execute in the following order:
2971
3400
 
2972
3401
  1. `before`
2973
3402
  2. `before_validation`
2974
3403
  3. _validations_
2975
- 4. `after_validation`
2976
- 5. _the API call_
2977
- 6. `after`
3404
+ 4. `after_validation` (upon successful validation)
3405
+ 5. _the API call_ (upon successful validation)
3406
+ 6. `after` (upon successful validation and API call)
3407
+ 7. `finally` (always)
2978
3408
 
2979
3409
  Steps 4, 5 and 6 only happen if validation succeeds.
2980
3410
 
@@ -2994,6 +3424,14 @@ before do
2994
3424
  end
2995
3425
  ```
2996
3426
 
3427
+ You can ensure a block of code runs after every request (including failures) with `finally`:
3428
+
3429
+ ```ruby
3430
+ finally do
3431
+ # this code will run after every request (successful or failed)
3432
+ end
3433
+ ```
3434
+
2997
3435
  **Namespaces**
2998
3436
 
2999
3437
  Callbacks apply to each API call within and below the current namespace:
@@ -3196,6 +3634,8 @@ class API < Grape::API
3196
3634
  end
3197
3635
  ```
3198
3636
 
3637
+ You can access the controller params, headers, and helpers through the context with the `#context` method inside any middleware inherited from `Grape::Middleware::Base`.
3638
+
3199
3639
  ### Rails Middleware
3200
3640
 
3201
3641
  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.
@@ -3504,6 +3944,7 @@ Grape integrates with following third-party tools:
3504
3944
  * **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
3505
3945
  * **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
3506
3946
  * **[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)
3947
+ * **[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)
3507
3948
 
3508
3949
  ## Contributing to Grape
3509
3950
 
@@ -3512,10 +3953,14 @@ features and discuss issues.
3512
3953
 
3513
3954
  See [CONTRIBUTING](CONTRIBUTING.md).
3514
3955
 
3956
+ ## Security
3957
+
3958
+ See [SECURITY](SECURITY.md) for details.
3959
+
3515
3960
  ## License
3516
3961
 
3517
- MIT License. See LICENSE for details.
3962
+ MIT License. See [LICENSE](LICENSE) for details.
3518
3963
 
3519
3964
  ## Copyright
3520
3965
 
3521
- Copyright (c) 2010-2018 Michael Bleigh, Intridea Inc. and Contributors.
3966
+ Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.