grape 1.1.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (306) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +370 -44
  3. data/CONTRIBUTING.md +32 -1
  4. data/LICENSE +1 -1
  5. data/README.md +683 -87
  6. data/UPGRADING.md +481 -17
  7. data/grape.gemspec +15 -4
  8. data/lib/grape/api/helpers.rb +2 -0
  9. data/lib/grape/api/instance.rb +279 -0
  10. data/lib/grape/api.rb +144 -176
  11. data/lib/grape/config.rb +34 -0
  12. data/lib/grape/content_types.rb +34 -0
  13. data/lib/grape/cookies.rb +4 -0
  14. data/lib/grape/dry_types.rb +12 -0
  15. data/lib/grape/dsl/api.rb +1 -1
  16. data/lib/grape/dsl/callbacks.rb +21 -1
  17. data/lib/grape/dsl/configuration.rb +1 -1
  18. data/lib/grape/dsl/desc.rb +41 -23
  19. data/lib/grape/dsl/headers.rb +7 -2
  20. data/lib/grape/dsl/helpers.rb +10 -7
  21. data/lib/grape/dsl/inside_route.rb +118 -62
  22. data/lib/grape/dsl/logger.rb +2 -0
  23. data/lib/grape/dsl/middleware.rb +11 -4
  24. data/lib/grape/dsl/parameters.rb +33 -19
  25. data/lib/grape/dsl/request_response.rb +12 -9
  26. data/lib/grape/dsl/routing.rb +22 -13
  27. data/lib/grape/dsl/settings.rb +10 -6
  28. data/lib/grape/dsl/validations.rb +19 -14
  29. data/lib/grape/eager_load.rb +20 -0
  30. data/lib/grape/endpoint.rb +67 -58
  31. data/lib/grape/error_formatter/base.rb +2 -0
  32. data/lib/grape/error_formatter/json.rb +11 -7
  33. data/lib/grape/error_formatter/txt.rb +2 -0
  34. data/lib/grape/error_formatter/xml.rb +4 -6
  35. data/lib/grape/error_formatter.rb +4 -2
  36. data/lib/grape/exceptions/base.rb +23 -16
  37. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  38. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  39. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  40. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  41. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  42. data/lib/grape/exceptions/invalid_response.rb +11 -0
  43. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  44. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  45. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  46. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  47. data/lib/grape/exceptions/missing_group_type.rb +10 -1
  48. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  49. data/lib/grape/exceptions/missing_option.rb +2 -0
  50. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  51. data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
  52. data/lib/grape/exceptions/unknown_options.rb +2 -0
  53. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  54. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  55. data/lib/grape/exceptions/unsupported_group_type.rb +10 -1
  56. data/lib/grape/exceptions/validation.rb +5 -8
  57. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  58. data/lib/grape/exceptions/validation_errors.rb +16 -13
  59. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  60. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  61. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  62. data/lib/grape/extensions/hash.rb +2 -0
  63. data/lib/grape/extensions/hashie/mash.rb +2 -0
  64. data/lib/grape/formatter/json.rb +3 -0
  65. data/lib/grape/formatter/serializable_hash.rb +4 -1
  66. data/lib/grape/formatter/txt.rb +2 -0
  67. data/lib/grape/formatter/xml.rb +3 -0
  68. data/lib/grape/formatter.rb +5 -3
  69. data/lib/grape/http/headers.rb +50 -18
  70. data/lib/grape/locale/en.yml +11 -8
  71. data/lib/grape/middleware/auth/base.rb +7 -7
  72. data/lib/grape/middleware/auth/dsl.rb +9 -2
  73. data/lib/grape/middleware/auth/strategies.rb +2 -0
  74. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  75. data/lib/grape/middleware/base.rb +13 -8
  76. data/lib/grape/middleware/error.rb +22 -17
  77. data/lib/grape/middleware/filter.rb +2 -0
  78. data/lib/grape/middleware/formatter.rb +12 -10
  79. data/lib/grape/middleware/globals.rb +2 -0
  80. data/lib/grape/middleware/helpers.rb +12 -0
  81. data/lib/grape/middleware/stack.rb +16 -6
  82. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
  83. data/lib/grape/middleware/versioner/header.rb +13 -9
  84. data/lib/grape/middleware/versioner/param.rb +4 -1
  85. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
  86. data/lib/grape/middleware/versioner/path.rb +5 -1
  87. data/lib/grape/middleware/versioner.rb +2 -0
  88. data/lib/grape/namespace.rb +14 -2
  89. data/lib/grape/parser/json.rb +3 -1
  90. data/lib/grape/parser/xml.rb +3 -1
  91. data/lib/grape/parser.rb +4 -2
  92. data/lib/grape/path.rb +16 -3
  93. data/lib/grape/presenters/presenter.rb +2 -0
  94. data/lib/grape/request.rb +21 -9
  95. data/lib/grape/router/attribute_translator.rb +41 -8
  96. data/lib/grape/router/pattern.rb +21 -17
  97. data/lib/grape/router/route.rb +15 -29
  98. data/lib/grape/router.rb +36 -29
  99. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  100. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  101. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  102. data/lib/grape/types/invalid_value.rb +8 -0
  103. data/lib/grape/util/base_inheritable.rb +43 -0
  104. data/lib/grape/util/cache.rb +20 -0
  105. data/lib/grape/util/endpoint_configuration.rb +8 -0
  106. data/lib/grape/util/env.rb +19 -17
  107. data/lib/grape/util/inheritable_setting.rb +3 -3
  108. data/lib/grape/util/inheritable_values.rb +7 -25
  109. data/lib/grape/util/json.rb +4 -0
  110. data/lib/grape/util/lazy_block.rb +27 -0
  111. data/lib/grape/util/lazy_object.rb +43 -0
  112. data/lib/grape/util/lazy_value.rb +99 -0
  113. data/lib/grape/util/registrable.rb +2 -0
  114. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  115. data/lib/grape/util/stackable_values.rb +21 -34
  116. data/lib/grape/util/strict_hash_configuration.rb +3 -1
  117. data/lib/grape/util/xml.rb +2 -0
  118. data/lib/grape/validations/attributes_doc.rb +58 -0
  119. data/lib/grape/validations/attributes_iterator.rb +16 -6
  120. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  121. data/lib/grape/validations/params_scope.rb +174 -94
  122. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  123. data/lib/grape/validations/types/array_coercer.rb +63 -0
  124. data/lib/grape/validations/types/build_coercer.rb +47 -49
  125. data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
  126. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  127. data/lib/grape/validations/types/dry_type_coercer.rb +72 -0
  128. data/lib/grape/validations/types/file.rb +22 -18
  129. data/lib/grape/validations/types/invalid_value.rb +17 -0
  130. data/lib/grape/validations/types/json.rb +47 -39
  131. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  132. data/lib/grape/validations/types/primitive_coercer.rb +75 -0
  133. data/lib/grape/validations/types/set_coercer.rb +38 -0
  134. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  135. data/lib/grape/validations/types.rb +106 -63
  136. data/lib/grape/validations/validator_factory.rb +8 -11
  137. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  138. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  139. data/lib/grape/validations/validators/as_validator.rb +14 -0
  140. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  141. data/lib/grape/validations/validators/base.rb +84 -68
  142. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  143. data/lib/grape/validations/validators/default_validator.rb +51 -0
  144. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  145. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  146. data/lib/grape/validations/validators/multiple_params_base.rb +27 -16
  147. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  148. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  149. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  150. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  151. data/lib/grape/validations/validators/values_validator.rb +88 -0
  152. data/lib/grape/validations.rb +18 -6
  153. data/lib/grape/version.rb +3 -1
  154. data/lib/grape.rb +175 -94
  155. data/spec/grape/api/custom_validations_spec.rb +117 -44
  156. data/spec/grape/api/deeply_included_options_spec.rb +4 -4
  157. data/spec/grape/api/defines_boolean_in_params_spec.rb +38 -0
  158. data/spec/grape/api/documentation_spec.rb +59 -0
  159. data/spec/grape/api/inherited_helpers_spec.rb +1 -1
  160. data/spec/grape/api/instance_spec.rb +103 -0
  161. data/spec/grape/api/invalid_format_spec.rb +3 -1
  162. data/spec/grape/api/namespace_parameters_in_route_spec.rb +1 -1
  163. data/spec/grape/api/nested_helpers_spec.rb +1 -1
  164. data/spec/grape/api/optional_parameters_in_route_spec.rb +1 -1
  165. data/spec/grape/api/parameters_modification_spec.rb +2 -2
  166. data/spec/grape/api/patch_method_helpers_spec.rb +1 -1
  167. data/spec/grape/api/recognize_path_spec.rb +2 -2
  168. data/spec/grape/api/required_parameters_in_route_spec.rb +1 -1
  169. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +1 -1
  170. data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
  171. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +10 -16
  172. data/spec/grape/api/shared_helpers_spec.rb +1 -1
  173. data/spec/grape/api_remount_spec.rb +473 -0
  174. data/spec/grape/api_spec.rb +995 -231
  175. data/spec/grape/config_spec.rb +17 -0
  176. data/spec/grape/dsl/callbacks_spec.rb +3 -2
  177. data/spec/grape/dsl/desc_spec.rb +43 -17
  178. data/spec/grape/dsl/headers_spec.rb +40 -10
  179. data/spec/grape/dsl/helpers_spec.rb +6 -5
  180. data/spec/grape/dsl/inside_route_spec.rb +189 -38
  181. data/spec/grape/dsl/logger_spec.rb +17 -19
  182. data/spec/grape/dsl/middleware_spec.rb +11 -2
  183. data/spec/grape/dsl/parameters_spec.rb +3 -1
  184. data/spec/grape/dsl/request_response_spec.rb +8 -7
  185. data/spec/grape/dsl/routing_spec.rb +22 -9
  186. data/spec/grape/dsl/settings_spec.rb +1 -1
  187. data/spec/grape/dsl/validations_spec.rb +1 -16
  188. data/spec/grape/endpoint/declared_spec.rb +846 -0
  189. data/spec/grape/endpoint_spec.rb +136 -577
  190. data/spec/grape/entity_spec.rb +31 -24
  191. data/spec/grape/exceptions/base_spec.rb +81 -0
  192. data/spec/grape/exceptions/body_parse_errors_spec.rb +4 -1
  193. data/spec/grape/exceptions/invalid_accept_header_spec.rb +65 -23
  194. data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
  195. data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
  196. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -2
  197. data/spec/grape/exceptions/missing_group_type_spec.rb +21 -0
  198. data/spec/grape/exceptions/missing_mime_type_spec.rb +1 -1
  199. data/spec/grape/exceptions/missing_option_spec.rb +2 -2
  200. data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
  201. data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
  202. data/spec/grape/exceptions/unsupported_group_type_spec.rb +23 -0
  203. data/spec/grape/exceptions/validation_errors_spec.rb +21 -15
  204. data/spec/grape/exceptions/validation_spec.rb +6 -4
  205. data/spec/grape/extensions/param_builders/hash_spec.rb +8 -8
  206. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +9 -9
  207. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +9 -9
  208. data/spec/grape/integration/global_namespace_function_spec.rb +2 -2
  209. data/spec/grape/integration/rack_sendfile_spec.rb +14 -10
  210. data/spec/grape/integration/rack_spec.rb +25 -8
  211. data/spec/grape/loading_spec.rb +9 -9
  212. data/spec/grape/middleware/auth/base_spec.rb +2 -1
  213. data/spec/grape/middleware/auth/dsl_spec.rb +19 -10
  214. data/spec/grape/middleware/auth/strategies_spec.rb +62 -22
  215. data/spec/grape/middleware/base_spec.rb +36 -17
  216. data/spec/grape/middleware/error_spec.rb +11 -4
  217. data/spec/grape/middleware/exception_spec.rb +112 -162
  218. data/spec/grape/middleware/formatter_spec.rb +65 -29
  219. data/spec/grape/middleware/globals_spec.rb +8 -5
  220. data/spec/grape/middleware/stack_spec.rb +25 -13
  221. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -2
  222. data/spec/grape/middleware/versioner/header_spec.rb +37 -14
  223. data/spec/grape/middleware/versioner/param_spec.rb +8 -2
  224. data/spec/grape/middleware/versioner/path_spec.rb +6 -2
  225. data/spec/grape/middleware/versioner_spec.rb +2 -2
  226. data/spec/grape/named_api_spec.rb +19 -0
  227. data/spec/grape/parser_spec.rb +10 -6
  228. data/spec/grape/path_spec.rb +53 -53
  229. data/spec/grape/presenters/presenter_spec.rb +8 -7
  230. data/spec/grape/request_spec.rb +29 -3
  231. data/spec/grape/util/inheritable_setting_spec.rb +9 -8
  232. data/spec/grape/util/inheritable_values_spec.rb +5 -3
  233. data/spec/grape/util/reverse_stackable_values_spec.rb +5 -2
  234. data/spec/grape/util/stackable_values_spec.rb +10 -7
  235. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -1
  236. data/spec/grape/validations/attributes_doc_spec.rb +153 -0
  237. data/spec/grape/validations/instance_behaivour_spec.rb +13 -14
  238. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +40 -0
  239. data/spec/grape/validations/params_scope_spec.rb +568 -99
  240. data/spec/grape/validations/single_attribute_iterator_spec.rb +57 -0
  241. data/spec/grape/validations/types/array_coercer_spec.rb +33 -0
  242. data/spec/grape/validations/types/primitive_coercer_spec.rb +150 -0
  243. data/spec/grape/validations/types/set_coercer_spec.rb +32 -0
  244. data/spec/grape/validations/types_spec.rb +44 -45
  245. data/spec/grape/validations/validators/all_or_none_spec.rb +134 -32
  246. data/spec/grape/validations/validators/allow_blank_spec.rb +137 -141
  247. data/spec/grape/validations/validators/at_least_one_of_spec.rb +169 -31
  248. data/spec/grape/validations/validators/coerce_spec.rb +491 -151
  249. data/spec/grape/validations/validators/default_spec.rb +242 -78
  250. data/spec/grape/validations/validators/exactly_one_of_spec.rb +198 -40
  251. data/spec/grape/validations/validators/except_values_spec.rb +6 -5
  252. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +181 -30
  253. data/spec/grape/validations/validators/presence_spec.rb +45 -2
  254. data/spec/grape/validations/validators/regexp_spec.rb +27 -33
  255. data/spec/grape/validations/validators/same_as_spec.rb +57 -0
  256. data/spec/grape/validations/validators/values_spec.rb +227 -180
  257. data/spec/grape/validations_spec.rb +502 -72
  258. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  259. data/spec/integration/multi_json/json_spec.rb +2 -2
  260. data/spec/integration/multi_xml/xml_spec.rb +2 -2
  261. data/spec/shared/versioning_examples.rb +34 -29
  262. data/spec/spec_helper.rb +31 -5
  263. data/spec/support/basic_auth_encode_helpers.rb +3 -1
  264. data/spec/support/chunks.rb +14 -0
  265. data/spec/support/content_type_helpers.rb +2 -0
  266. data/spec/support/endpoint_faker.rb +2 -0
  267. data/spec/support/file_streamer.rb +2 -0
  268. data/spec/support/integer_helpers.rb +2 -0
  269. data/spec/support/versioned_helpers.rb +8 -8
  270. metadata +111 -61
  271. data/Appraisals +0 -32
  272. data/Dangerfile +0 -2
  273. data/Gemfile +0 -33
  274. data/Gemfile.lock +0 -231
  275. data/Guardfile +0 -10
  276. data/RELEASING.md +0 -111
  277. data/Rakefile +0 -25
  278. data/benchmark/simple.rb +0 -27
  279. data/benchmark/simple_with_type_coercer.rb +0 -22
  280. data/gemfiles/multi_json.gemfile +0 -35
  281. data/gemfiles/multi_xml.gemfile +0 -35
  282. data/gemfiles/rack_1.5.2.gemfile +0 -35
  283. data/gemfiles/rack_edge.gemfile +0 -35
  284. data/gemfiles/rails_3.gemfile +0 -36
  285. data/gemfiles/rails_4.gemfile +0 -35
  286. data/gemfiles/rails_5.gemfile +0 -35
  287. data/gemfiles/rails_edge.gemfile +0 -35
  288. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  289. data/lib/grape/util/content_types.rb +0 -26
  290. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
  291. data/lib/grape/validations/validators/all_or_none.rb +0 -20
  292. data/lib/grape/validations/validators/allow_blank.rb +0 -16
  293. data/lib/grape/validations/validators/as.rb +0 -15
  294. data/lib/grape/validations/validators/at_least_one_of.rb +0 -20
  295. data/lib/grape/validations/validators/coerce.rb +0 -74
  296. data/lib/grape/validations/validators/default.rb +0 -48
  297. data/lib/grape/validations/validators/exactly_one_of.rb +0 -29
  298. data/lib/grape/validations/validators/except_values.rb +0 -20
  299. data/lib/grape/validations/validators/mutual_exclusion.rb +0 -25
  300. data/lib/grape/validations/validators/presence.rb +0 -10
  301. data/lib/grape/validations/validators/regexp.rb +0 -11
  302. data/lib/grape/validations/validators/values.rb +0 -71
  303. data/pkg/grape-0.17.0.gem +0 -0
  304. data/pkg/grape-0.19.0.gem +0 -0
  305. data/spec/grape/dsl/configuration_spec.rb +0 -14
  306. data/spec/grape/validations/attributes_iterator_spec.rb +0 -4
data/UPGRADING.md CHANGED
@@ -1,6 +1,481 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 1.7.0
5
+
6
+ #### Exceptions renaming
7
+
8
+ The following exceptions has been renamed for consistency through exceptions naming :
9
+
10
+ * `MissingGroupTypeError` => `MissingGroupType`
11
+ * `UnsupportedGroupTypeError` => `UnsupportedGroupType`
12
+
13
+ See [#2227](https://github.com/ruby-grape/grape/pull/2227) for more information.
14
+
15
+ #### Handling Multipart Limit Errors
16
+
17
+ Rack supports a configurable limit on the number of files created from multipart parameters (`Rack::Utils.multipart_part_limit`) and raises an error if params are received that create too many files. If you were handling the Rack error directly, Grape now wraps that error in `Grape::Execeptions::TooManyMultipartFiles`. Additionally, Grape will return a 413 status code if the exception goes unhandled.
18
+
19
+ ### Upgrading to >= 1.6.0
20
+
21
+ #### Parameter renaming with :as
22
+
23
+ Prior to 1.6.0 the [parameter renaming](https://github.com/ruby-grape/grape#renaming) with `:as` was directly touching the request payload ([`#params`](https://github.com/ruby-grape/grape#parameters)) while duplicating the old and the new key to be both available in the hash. This allowed clients to bypass any validation in case they knew the internal name of the parameter. Unfortunately, in combination with [grape-swagger](https://github.com/ruby-grape/grape-swagger) the internal name (name set with `:as`) of the parameters were documented.
24
+
25
+ This behavior was fixed. Parameter renaming is now done when using the [`#declared(params)`](https://github.com/ruby-grape/grape#declared) parameters helper. This stops confusing validation/coercion behavior.
26
+
27
+ Here comes an illustration of the old and new behaviour as code:
28
+
29
+ ```ruby
30
+ # (1) Rename a to b, while client sends +a+
31
+ optional :a, type: Integer, as: :b
32
+ params = { a: 1 }
33
+ declared(params, include_missing: false)
34
+ # expected => { b: 1 }
35
+ # actual => { b: 1 }
36
+
37
+ # (2) Rename a to b, while client sends +b+
38
+ optional :a, type: Integer, as: :b, values: [1, 2, 3]
39
+ params = { b: '5' }
40
+ declared(params, include_missing: false)
41
+ # expected => { } (>= 1.6.0)
42
+ # actual => { b: '5' } (uncasted, unvalidated, <= 1.5.3)
43
+ ```
44
+
45
+ Another implication of this change is the dependent parameter resolution. Prior to 1.6.0 the following code produced a `Grape::Exceptions::UnknownParameter` because `:a` was replaced by `:b`:
46
+
47
+ ```ruby
48
+ params do
49
+ optional :a, as: :b
50
+ given :a do # (<= 1.5.3 you had to reference +:b+ here to make it work)
51
+ requires :c
52
+ end
53
+ end
54
+ ```
55
+
56
+ This code now works without any errors, as the renaming is just an internal behaviour of the `#declared(params)` parameter helper.
57
+
58
+ See [#2189](https://github.com/ruby-grape/grape/pull/2189) for more information.
59
+
60
+ ### Upgrading to >= 1.5.3
61
+
62
+ #### Nil value and coercion
63
+
64
+ Prior to 1.2.5 version passing a `nil` value for a parameter with a custom coercer would invoke the coercer, and not passing a parameter would not invoke it.
65
+ This behavior was not tested or documented. Version 1.3.0 quietly changed this behavior, in that `nil` values skipped the coercion. Version 1.5.3 fixes and documents this as follows:
66
+
67
+ ```ruby
68
+ class Api < Grape::API
69
+ params do
70
+ optional :value, type: Integer, coerce_with: ->(val) { val || 0 }
71
+ end
72
+
73
+ get 'example' do
74
+ params[:my_param]
75
+ end
76
+ get '/example', params: { value: nil }
77
+ # 1.5.2 = nil
78
+ # 1.5.3 = 0
79
+ get '/example', params: {}
80
+ # 1.5.2 = nil
81
+ # 1.5.3 = nil
82
+ end
83
+ ```
84
+ See [#2164](https://github.com/ruby-grape/grape/pull/2164) for more information.
85
+
86
+ ### Upgrading to >= 1.5.1
87
+
88
+ #### Dependent params
89
+
90
+ If you use [dependent params](https://github.com/ruby-grape/grape#dependent-parameters) with
91
+ `Grape::Extensions::Hash::ParamBuilder`, make sure a parameter to be dependent on is set as a Symbol.
92
+ If a String is given, a parameter that other parameters depend on won't be found even if it is present.
93
+
94
+ _Correct_:
95
+ ```ruby
96
+ given :matrix do
97
+ # dependent params
98
+ end
99
+ ```
100
+
101
+ _Wrong_:
102
+ ```ruby
103
+ given 'matrix' do
104
+ # dependent params
105
+ end
106
+ ```
107
+
108
+ ### Upgrading to >= 1.5.0
109
+
110
+ Prior to 1.3.3, the `declared` helper would always return the complete params structure if `include_missing=true` was set. In 1.3.3 a regression was introduced such that a missing Hash with or without nested parameters would always resolve to `{}`.
111
+
112
+ In 1.5.0 this behavior is reverted, so the whole params structure will always be available via `declared`, regardless of whether any params are passed.
113
+
114
+ The following rules now apply to the `declared` helper when params are missing and `include_missing=true`:
115
+
116
+ * Hash params with children will resolve to a Hash with keys for each declared child.
117
+ * Hash params with no children will resolve to `{}`.
118
+ * Set params will resolve to `Set.new`.
119
+ * Array params will resolve to `[]`.
120
+ * All other params will resolve to `nil`.
121
+
122
+ #### Example
123
+
124
+ ```ruby
125
+ class Api < Grape::API
126
+ params do
127
+ optional :outer, type: Hash do
128
+ optional :inner, type: Hash do
129
+ optional :value, type: String
130
+ end
131
+ end
132
+ end
133
+ get 'example' do
134
+ declared(params, include_missing: true)
135
+ end
136
+ end
137
+ ```
138
+
139
+ ```
140
+ get '/example'
141
+ # 1.3.3 = {}
142
+ # 1.5.0 = {outer: {inner: {value:null}}}
143
+ ```
144
+
145
+ For more information see [#2103](https://github.com/ruby-grape/grape/pull/2103).
146
+
147
+ ### Upgrading to >= 1.4.0
148
+
149
+ #### Reworking stream and file and un-deprecating stream like-objects
150
+
151
+ Previously in 0.16 stream-like objects were deprecated. This release restores their functionality for use-cases other than file streaming.
152
+
153
+ This release deprecated `file` in favor of `sendfile` to better document its purpose.
154
+
155
+ To deliver a file via the Sendfile support in your web server and have the Rack::Sendfile middleware enabled. See [`Rack::Sendfile`](https://www.rubydoc.info/gems/rack/Rack/Sendfile).
156
+ ```ruby
157
+ class API < Grape::API
158
+ get '/' do
159
+ sendfile '/path/to/file'
160
+ end
161
+ end
162
+ ```
163
+
164
+ Use `stream` to stream file content in chunks.
165
+
166
+ ```ruby
167
+ class API < Grape::API
168
+ get '/' do
169
+ stream '/path/to/file'
170
+ end
171
+ end
172
+ ```
173
+
174
+ Or use `stream` to stream other kinds of content. In the following example a streamer class
175
+ streams paginated data from a database.
176
+
177
+ ```ruby
178
+ class MyObject
179
+ attr_accessor :result
180
+
181
+ def initialize(query)
182
+ @result = query
183
+ end
184
+
185
+ def each
186
+ yield '['
187
+ # Do paginated DB fetches and return each page formatted
188
+ first = false
189
+ result.find_in_batches do |records|
190
+ yield process_records(records, first)
191
+ first = false
192
+ end
193
+ yield ']'
194
+ end
195
+
196
+ def process_records(records, first)
197
+ buffer = +''
198
+ buffer << ',' unless first
199
+ buffer << records.map(&:to_json).join(',')
200
+ buffer
201
+ end
202
+ end
203
+
204
+ class API < Grape::API
205
+ get '/' do
206
+ stream MyObject.new(Sprocket.all)
207
+ end
208
+ end
209
+ ```
210
+
211
+ ### Upgrading to >= 1.3.3
212
+
213
+ #### Nil values for structures
214
+
215
+ Nil values have always been a special case when dealing with types, especially with the following structures:
216
+
217
+ - Array
218
+ - Hash
219
+ - Set
220
+
221
+ The behavior for these structures has changed throughout the latest releases. For example:
222
+
223
+ ```ruby
224
+ class Api < Grape::API
225
+ params do
226
+ require :my_param, type: Array[Integer]
227
+ end
228
+
229
+ get 'example' do
230
+ params[:my_param]
231
+ end
232
+ get '/example', params: { my_param: nil }
233
+ # 1.3.1 = []
234
+ # 1.3.2 = nil
235
+ end
236
+ ```
237
+
238
+ For now on, `nil` values stay `nil` values for all types, including arrays, sets and hashes.
239
+
240
+ If you want to have the same behavior as 1.3.1, apply a `default` validator:
241
+
242
+ ```ruby
243
+ class Api < Grape::API
244
+ params do
245
+ require :my_param, type: Array[Integer], default: []
246
+ end
247
+
248
+ get 'example' do
249
+ params[:my_param]
250
+ end
251
+ get '/example', params: { my_param: nil } # => []
252
+ end
253
+ ```
254
+
255
+ #### Default validator
256
+
257
+ Default validator is now applied for `nil` values.
258
+
259
+ ```ruby
260
+ class Api < Grape::API
261
+ params do
262
+ requires :my_param, type: Integer, default: 0
263
+ end
264
+
265
+ get 'example' do
266
+ params[:my_param]
267
+ end
268
+ get '/example', params: { my_param: nil } #=> before: nil, after: 0
269
+ end
270
+ ```
271
+
272
+ ### Upgrading to >= 1.3.0
273
+
274
+ You will need to upgrade to this version if you depend on `rack >= 2.1.0`.
275
+
276
+ #### Ruby
277
+
278
+ After adding dry-types, Ruby 2.4 or newer is required.
279
+
280
+ #### Coercion
281
+
282
+ [Virtus](https://github.com/solnic/virtus) has been replaced by [dry-types](https://dry-rb.org/gems/dry-types/1.2/) for parameter coercion. If your project depends on Virtus outside of Grape, explicitly add it to your `Gemfile`.
283
+
284
+ Here's an example of how to migrate a custom type from Virtus to dry-types:
285
+
286
+ ```ruby
287
+ # Legacy Grape parser
288
+ class SecureUriType < Virtus::Attribute
289
+ def coerce(input)
290
+ URI.parse value
291
+ end
292
+
293
+ def value_coerced?(input)
294
+ value.is_a? String
295
+ end
296
+ end
297
+
298
+ params do
299
+ requires :secure_uri, type: SecureUri
300
+ end
301
+ ```
302
+
303
+ To use dry-types, we need to:
304
+
305
+ 1. Remove the inheritance of `Virtus::Attribute`
306
+ 1. Rename `coerce` to `self.parse`
307
+ 1. Rename `value_coerced?` to `self.parsed?`
308
+
309
+ The custom type must have a class-level `parse` method to the model. A class-level `parsed?` is needed if the parsed type differs from the defined type. In the example below, since `SecureUri` is not the same as `URI::HTTPS`, `self.parsed?` is needed:
310
+
311
+ ```ruby
312
+ # New dry-types parser
313
+ class SecureUri
314
+ def self.parse(value)
315
+ URI.parse value
316
+ end
317
+
318
+ def self.parsed?(value)
319
+ value.is_a? URI::HTTPS
320
+ end
321
+ end
322
+
323
+ params do
324
+ requires :secure_uri, type: SecureUri
325
+ end
326
+ ```
327
+
328
+ #### Coercing to `FalseClass` or `TrueClass` no longer works
329
+
330
+ Previous Grape versions allowed this, though it wasn't documented:
331
+
332
+ ```ruby
333
+ requires :true_value, type: TrueClass
334
+ requires :bool_value, types: [FalseClass, TrueClass]
335
+ ```
336
+
337
+ This is no longer supported, if you do this, your values will never be valid. Instead you should do this:
338
+
339
+ ```ruby
340
+ requires :true_value, type: Boolean # in your endpoint you should validate if this is actually `true`
341
+ requires :bool_value, type: Boolean
342
+ ```
343
+
344
+ #### Ensure that Array types have explicit coercions
345
+
346
+ Unlike Virtus, dry-types does not perform any implict coercions. If you have any uses of `Array[String]`, `Array[Integer]`, etc. be sure they use a `coerce_with` block. For example:
347
+
348
+ ```ruby
349
+ requires :values, type: Array[String]
350
+ ```
351
+
352
+ It's quite common to pass a comma-separated list, such as `tag1,tag2` as `values`. Previously Virtus would implicitly coerce this to `Array(values)` so that `["tag1,tag2"]` would pass the type checks, but with `dry-types` the values are no longer coerced for you. To fix this, you might do:
353
+
354
+ ```ruby
355
+ requires :values, type: Array[String], coerce_with: ->(val) { val.split(',').map(&:strip) }
356
+ ```
357
+
358
+ Likewise, for `Array[Integer]`, you might do:
359
+
360
+ ```ruby
361
+ requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(',').map(&:strip).map(&:to_i) }
362
+ ```
363
+
364
+ For more information see [#1920](https://github.com/ruby-grape/grape/pull/1920).
365
+
366
+ ### Upgrading to >= 1.2.4
367
+
368
+ #### Headers in `error!` call
369
+
370
+ Headers in `error!` will be merged with `headers` hash. If any header need to be cleared on `error!` call, make sure to move it to the `after` block.
371
+
372
+ ```ruby
373
+ class SampleApi < Grape::API
374
+ before do
375
+ header 'X-Before-Header', 'before_call'
376
+ end
377
+
378
+ get 'ping' do
379
+ header 'X-App-Header', 'on_call'
380
+ error! :pong, 400, 'X-Error-Details' => 'Invalid token'
381
+ end
382
+ end
383
+ ```
384
+ **Former behaviour**
385
+ ```ruby
386
+ response.headers['X-Before-Header'] # => nil
387
+ response.headers['X-App-Header'] # => nil
388
+ response.headers['X-Error-Details'] # => Invalid token
389
+ ```
390
+
391
+ **Current behaviour**
392
+ ```ruby
393
+ response.headers['X-Before-Header'] # => 'before_call'
394
+ response.headers['X-App-Header'] # => 'on_call'
395
+ response.headers['X-Error-Details'] # => Invalid token
396
+ ```
397
+
398
+ ### Upgrading to >= 1.2.1
399
+
400
+ #### Obtaining the name of a mounted class
401
+
402
+ In order to make obtaining the name of a mounted class simpler, we've delegated `.to_s` to `base.name`
403
+
404
+ **Deprecated in 1.2.0**
405
+ ```ruby
406
+ payload[:endpoint].options[:for].name
407
+ ```
408
+ **New**
409
+ ```ruby
410
+ payload[:endpoint].options[:for].to_s
411
+ ```
412
+
413
+ ### Upgrading to >= 1.2.0
414
+
415
+ #### Changes in the Grape::API class
416
+
417
+ ##### Patching the class
418
+
419
+ In an effort to make APIs re-mountable, The class `Grape::API` no longer refers to an API instance, rather, what used to be `Grape::API` is now `Grape::API::Instance` and `Grape::API` was replaced with a class that can contain several instances of `Grape::API`.
420
+
421
+ This changes were done in such a way that no code-changes should be required. However, if experiencing problems, or relying on private methods and internal behaviour too deeply, it is possible to restore the prior behaviour by replacing the references from `Grape::API` to `Grape::API::Instance`.
422
+
423
+ Note, this is particularly relevant if you are opening the class `Grape::API` for modification.
424
+
425
+ **Deprecated**
426
+ ```ruby
427
+ class Grape::API
428
+ # your patched logic
429
+ ...
430
+ end
431
+ ```
432
+ **New**
433
+ ```ruby
434
+ class Grape::API::Instance
435
+ # your patched logic
436
+ ...
437
+ end
438
+ ```
439
+
440
+ ##### `name` (and other caveats) of the mounted API
441
+
442
+ After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
443
+ which inherit from `Grape::API::Instance`.
444
+
445
+ What this means in practice, is:
446
+
447
+ - Generally: you can access the named class from the instance calling the getter `base`.
448
+ - In particular: If you need the `name`, you can use `base`.`name`.
449
+
450
+ **Deprecated**
451
+
452
+ ```ruby
453
+ payload[:endpoint].options[:for].name
454
+ ```
455
+
456
+ **New**
457
+
458
+ ```ruby
459
+ payload[:endpoint].options[:for].base.name
460
+ ```
461
+
462
+ #### Changes in rescue_from returned object
463
+
464
+ Grape will now check the object returned from `rescue_from` and ensure that it is a `Rack::Response`. That makes sure response is valid and avoids exposing service information. Change any code that invoked `Rack::Response.new(...).finish` in a custom `rescue_from` block to `Rack::Response.new(...)` to comply with the validation.
465
+
466
+ ```ruby
467
+ class Twitter::API < Grape::API
468
+ rescue_from :all do |e|
469
+ # version prior to 1.2.0
470
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
471
+ # 1.2.0 version
472
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
473
+ end
474
+ end
475
+ ```
476
+
477
+ See [#1757](https://github.com/ruby-grape/grape/pull/1757) and [#1776](https://github.com/ruby-grape/grape/pull/1776) for more information.
478
+
4
479
  ### Upgrading to >= 1.1.0
5
480
 
6
481
  #### Changes in HTTP Response Code for Unsupported Content Type
@@ -70,8 +545,7 @@ See [#1610](https://github.com/ruby-grape/grape/pull/1610) for more information.
70
545
 
71
546
  #### The `except`, `except_message`, and `proc` options of the `values` validator are deprecated.
72
547
 
73
- The new `except_values` validator should be used in place of the `except` and `except_message` options of
74
- the `values` validator.
548
+ The new `except_values` validator should be used in place of the `except` and `except_message` options of the `values` validator.
75
549
 
76
550
  Arity one Procs may now be used directly as the `values` option to explicitly test param values.
77
551
 
@@ -147,9 +621,7 @@ get '/example' #=> before: 405, after: 404
147
621
 
148
622
  #### Removed param processing from built-in OPTIONS handler
149
623
 
150
- When a request is made to the built-in `OPTIONS` handler, only the `before` and `after`
151
- callbacks associated with the resource will be run. The `before_validation` and
152
- `after_validation` callbacks and parameter validations will be skipped.
624
+ When a request is made to the built-in `OPTIONS` handler, only the `before` and `after` callbacks associated with the resource will be run. The `before_validation` and `after_validation` callbacks and parameter validations will be skipped.
153
625
 
154
626
  See [#1505](https://github.com/ruby-grape/grape/pull/1505) for more information.
155
627
 
@@ -170,8 +642,7 @@ See [#1510](https://github.com/ruby-grape/grape/pull/1510) for more information.
170
642
 
171
643
  #### The default status code for DELETE is now 204 instead of 200.
172
644
 
173
- Breaking change: Sets the default response status code for a delete request to 204.
174
- A status of 204 makes the response more distinguishable and therefore easier to handle on the client side, particularly because a DELETE request typically returns an empty body as the resource was deleted or voided.
645
+ Breaking change: Sets the default response status code for a delete request to 204. A status of 204 makes the response more distinguishable and therefore easier to handle on the client side, particularly because a DELETE request typically returns an empty body as the resource was deleted or voided.
175
646
 
176
647
  To achieve the old behavior, one has to set it explicitly:
177
648
  ```ruby
@@ -349,18 +820,14 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
349
820
 
350
821
  #### Bypasses formatters when status code indicates no content
351
822
 
352
- To be consistent with rack and it's handling of standard responses
353
- associated with no content, both default and custom formatters will now
823
+ To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now
354
824
  be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
355
825
 
356
826
  See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
357
827
 
358
828
  #### Redirects respond as plain text with message
359
829
 
360
- `#redirect` now uses `text/plain` regardless of whether that format has
361
- been enabled. This prevents formatters from attempting to serialize the
362
- message body and allows for a descriptive message body to be provided - and
363
- optionally overridden - that better fulfills the theme of the HTTP spec.
830
+ `#redirect` now uses `text/plain` regardless of whether that format has been enabled. This prevents formatters from attempting to serialize the message body and allows for a descriptive message body to be provided - and optionally overridden - that better fulfills the theme of the HTTP spec.
364
831
 
365
832
  See [#1194](https://github.com/ruby-grape/grape/pull/1194) for more information.
366
833
 
@@ -394,10 +861,7 @@ end
394
861
 
395
862
  See [#1029](https://github.com/ruby-grape/grape/pull/1029) for more information.
396
863
 
397
- There is a known issue because of this change. When Grape is used with an older
398
- than 1.2.4 version of [warden](https://github.com/hassox/warden) there may be raised
399
- the following exception having the [rack-mount](https://github.com/jm/rack-mount) gem's
400
- lines as last ones in the backtrace:
864
+ There is a known issue because of this change. When Grape is used with an older than 1.2.4 version of [warden](https://github.com/hassox/warden) there may be raised the following exception having the [rack-mount](https://github.com/jm/rack-mount) gem's lines as last ones in the backtrace:
401
865
 
402
866
  ```
403
867
  NoMethodError: undefined method `[]' for nil:NilClass
data/grape.gemspec CHANGED
@@ -1,4 +1,6 @@
1
- $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path('lib', __dir__)
2
4
  require 'grape/version'
3
5
 
4
6
  Gem::Specification.new do |s|
@@ -11,15 +13,24 @@ Gem::Specification.new do |s|
11
13
  s.summary = 'A simple Ruby framework for building REST-like APIs.'
12
14
  s.description = 'A Ruby framework for rapid API development with great conventions.'
13
15
  s.license = 'MIT'
16
+ s.metadata = {
17
+ 'bug_tracker_uri' => 'https://github.com/ruby-grape/grape/issues',
18
+ 'changelog_uri' => "https://github.com/ruby-grape/grape/blob/v#{s.version}/CHANGELOG.md",
19
+ 'documentation_uri' => "https://www.rubydoc.info/gems/grape/#{s.version}",
20
+ 'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}"
21
+ }
14
22
 
15
23
  s.add_runtime_dependency 'activesupport'
16
24
  s.add_runtime_dependency 'builder'
25
+ s.add_runtime_dependency 'dry-types', '>= 1.1'
17
26
  s.add_runtime_dependency 'mustermann-grape', '~> 1.0.0'
18
- s.add_runtime_dependency 'rack', '>= 1.3.0'
27
+ s.add_runtime_dependency 'rack', '>= 1.3.0', '< 3'
19
28
  s.add_runtime_dependency 'rack-accept'
20
- s.add_runtime_dependency 'virtus', '>= 1.0.0'
21
29
 
22
- s.files = Dir['**/*'].keep_if { |file| File.file?(file) }
30
+ s.files = %w[CHANGELOG.md CONTRIBUTING.md README.md grape.png UPGRADING.md LICENSE]
31
+ s.files += %w[grape.gemspec]
32
+ s.files += Dir['lib/**/*']
23
33
  s.test_files = Dir['spec/**/*']
24
34
  s.require_paths = ['lib']
35
+ s.required_ruby_version = '>= 2.6.0'
25
36
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  class API
3
5
  module Helpers