grape 1.0.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (284) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +346 -39
  3. data/LICENSE +1 -1
  4. data/README.md +658 -90
  5. data/UPGRADING.md +472 -17
  6. data/grape.gemspec +17 -6
  7. data/lib/grape/api/helpers.rb +2 -0
  8. data/lib/grape/api/instance.rb +279 -0
  9. data/lib/grape/api.rb +132 -176
  10. data/lib/grape/config.rb +34 -0
  11. data/lib/grape/content_types.rb +34 -0
  12. data/lib/grape/cookies.rb +4 -0
  13. data/lib/grape/dsl/api.rb +2 -0
  14. data/lib/grape/dsl/callbacks.rb +22 -0
  15. data/lib/grape/dsl/configuration.rb +2 -0
  16. data/lib/grape/dsl/desc.rb +44 -12
  17. data/lib/grape/dsl/headers.rb +2 -0
  18. data/lib/grape/dsl/helpers.rb +11 -6
  19. data/lib/grape/dsl/inside_route.rb +116 -36
  20. data/lib/grape/dsl/logger.rb +2 -0
  21. data/lib/grape/dsl/middleware.rb +12 -3
  22. data/lib/grape/dsl/parameters.rb +34 -16
  23. data/lib/grape/dsl/request_response.rb +13 -8
  24. data/lib/grape/dsl/routing.rb +19 -12
  25. data/lib/grape/dsl/settings.rb +22 -4
  26. data/lib/grape/dsl/validations.rb +24 -4
  27. data/lib/grape/eager_load.rb +20 -0
  28. data/lib/grape/endpoint.rb +66 -57
  29. data/lib/grape/error_formatter/base.rb +2 -0
  30. data/lib/grape/error_formatter/json.rb +6 -4
  31. data/lib/grape/error_formatter/txt.rb +10 -3
  32. data/lib/grape/error_formatter/xml.rb +6 -4
  33. data/lib/grape/error_formatter.rb +4 -2
  34. data/lib/grape/exceptions/base.rb +22 -14
  35. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  36. data/lib/grape/exceptions/incompatible_option_values.rb +2 -1
  37. data/lib/grape/exceptions/invalid_accept_header.rb +2 -1
  38. data/lib/grape/exceptions/invalid_formatter.rb +2 -1
  39. data/lib/grape/exceptions/invalid_message_body.rb +2 -1
  40. data/lib/grape/exceptions/invalid_response.rb +11 -0
  41. data/lib/grape/exceptions/invalid_version_header.rb +2 -1
  42. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -1
  43. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -1
  44. data/lib/grape/exceptions/method_not_allowed.rb +2 -1
  45. data/lib/grape/exceptions/missing_group_type.rb +2 -1
  46. data/lib/grape/exceptions/missing_mime_type.rb +2 -1
  47. data/lib/grape/exceptions/missing_option.rb +2 -1
  48. data/lib/grape/exceptions/missing_vendor_option.rb +2 -1
  49. data/lib/grape/exceptions/unknown_options.rb +2 -1
  50. data/lib/grape/exceptions/unknown_parameter.rb +2 -1
  51. data/lib/grape/exceptions/unknown_validator.rb +2 -1
  52. data/lib/grape/exceptions/unsupported_group_type.rb +2 -1
  53. data/lib/grape/exceptions/validation.rb +5 -4
  54. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  55. data/lib/grape/exceptions/validation_errors.rb +16 -13
  56. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  57. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  58. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  59. data/lib/grape/extensions/hash.rb +2 -0
  60. data/lib/grape/extensions/hashie/mash.rb +2 -0
  61. data/lib/grape/formatter/json.rb +3 -0
  62. data/lib/grape/formatter/serializable_hash.rb +4 -1
  63. data/lib/grape/formatter/txt.rb +2 -0
  64. data/lib/grape/formatter/xml.rb +3 -0
  65. data/lib/grape/formatter.rb +5 -3
  66. data/lib/grape/http/headers.rb +50 -18
  67. data/lib/grape/locale/en.yml +3 -1
  68. data/lib/grape/middleware/auth/base.rb +7 -7
  69. data/lib/grape/middleware/auth/dsl.rb +2 -0
  70. data/lib/grape/middleware/auth/strategies.rb +2 -0
  71. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  72. data/lib/grape/middleware/base.rb +12 -7
  73. data/lib/grape/middleware/error.rb +75 -61
  74. data/lib/grape/middleware/filter.rb +2 -0
  75. data/lib/grape/middleware/formatter.rb +17 -13
  76. data/lib/grape/middleware/globals.rb +2 -0
  77. data/lib/grape/middleware/helpers.rb +12 -0
  78. data/lib/grape/middleware/stack.rb +15 -5
  79. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
  80. data/lib/grape/middleware/versioner/header.rb +13 -9
  81. data/lib/grape/middleware/versioner/param.rb +4 -1
  82. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
  83. data/lib/grape/middleware/versioner/path.rb +5 -1
  84. data/lib/grape/middleware/versioner.rb +2 -0
  85. data/lib/grape/namespace.rb +15 -3
  86. data/lib/grape/parser/json.rb +3 -1
  87. data/lib/grape/parser/xml.rb +3 -1
  88. data/lib/grape/parser.rb +4 -2
  89. data/lib/grape/path.rb +16 -3
  90. data/lib/grape/presenters/presenter.rb +2 -0
  91. data/lib/grape/request.rb +19 -9
  92. data/lib/grape/router/attribute_translator.rb +41 -8
  93. data/lib/grape/router/pattern.rb +22 -18
  94. data/lib/grape/router/route.rb +16 -30
  95. data/lib/grape/router.rb +37 -28
  96. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  97. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  98. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  99. data/lib/grape/util/base_inheritable.rb +43 -0
  100. data/lib/grape/util/cache.rb +20 -0
  101. data/lib/grape/util/endpoint_configuration.rb +8 -0
  102. data/lib/grape/util/env.rb +19 -17
  103. data/lib/grape/util/inheritable_setting.rb +3 -3
  104. data/lib/grape/util/inheritable_values.rb +7 -25
  105. data/lib/grape/util/json.rb +2 -0
  106. data/lib/grape/util/lazy_block.rb +27 -0
  107. data/lib/grape/util/lazy_object.rb +43 -0
  108. data/lib/grape/util/lazy_value.rb +99 -0
  109. data/lib/grape/util/registrable.rb +2 -0
  110. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  111. data/lib/grape/util/stackable_values.rb +21 -34
  112. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  113. data/lib/grape/util/xml.rb +2 -0
  114. data/lib/grape/validations/attributes_iterator.rb +16 -6
  115. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  116. data/lib/grape/validations/params_scope.rb +128 -66
  117. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  118. data/lib/grape/validations/types/array_coercer.rb +65 -0
  119. data/lib/grape/validations/types/build_coercer.rb +52 -46
  120. data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
  121. data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
  122. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  123. data/lib/grape/validations/types/file.rb +22 -18
  124. data/lib/grape/validations/types/invalid_value.rb +24 -0
  125. data/lib/grape/validations/types/json.rb +47 -39
  126. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  127. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  128. data/lib/grape/validations/types/set_coercer.rb +40 -0
  129. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  130. data/lib/grape/validations/types.rb +26 -38
  131. data/lib/grape/validations/validator_factory.rb +8 -11
  132. data/lib/grape/validations/validators/all_or_none.rb +8 -12
  133. data/lib/grape/validations/validators/allow_blank.rb +4 -2
  134. data/lib/grape/validations/validators/as.rb +12 -0
  135. data/lib/grape/validations/validators/at_least_one_of.rb +7 -12
  136. data/lib/grape/validations/validators/base.rb +23 -15
  137. data/lib/grape/validations/validators/coerce.rb +47 -28
  138. data/lib/grape/validations/validators/default.rb +7 -6
  139. data/lib/grape/validations/validators/exactly_one_of.rb +10 -22
  140. data/lib/grape/validations/validators/except_values.rb +4 -2
  141. data/lib/grape/validations/validators/multiple_params_base.rb +19 -10
  142. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -17
  143. data/lib/grape/validations/validators/presence.rb +4 -1
  144. data/lib/grape/validations/validators/regexp.rb +5 -2
  145. data/lib/grape/validations/validators/same_as.rb +27 -0
  146. data/lib/grape/validations/validators/values.rb +29 -9
  147. data/lib/grape/validations.rb +2 -0
  148. data/lib/grape/version.rb +3 -1
  149. data/lib/grape.rb +107 -73
  150. data/spec/grape/api/custom_validations_spec.rb +6 -3
  151. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  152. data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
  153. data/spec/grape/api/inherited_helpers_spec.rb +116 -0
  154. data/spec/grape/api/instance_spec.rb +104 -0
  155. data/spec/grape/api/invalid_format_spec.rb +2 -0
  156. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  157. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  158. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  160. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  161. data/spec/grape/api/recognize_path_spec.rb +2 -0
  162. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  163. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  164. data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
  165. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +49 -0
  166. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  167. data/spec/grape/api_remount_spec.rb +473 -0
  168. data/spec/grape/api_spec.rb +775 -65
  169. data/spec/grape/config_spec.rb +19 -0
  170. data/spec/grape/dsl/callbacks_spec.rb +3 -1
  171. data/spec/grape/dsl/configuration_spec.rb +2 -0
  172. data/spec/grape/dsl/desc_spec.rb +42 -16
  173. data/spec/grape/dsl/headers_spec.rb +2 -0
  174. data/spec/grape/dsl/helpers_spec.rb +23 -2
  175. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  176. data/spec/grape/dsl/logger_spec.rb +2 -0
  177. data/spec/grape/dsl/middleware_spec.rb +11 -1
  178. data/spec/grape/dsl/parameters_spec.rb +12 -9
  179. data/spec/grape/dsl/request_response_spec.rb +2 -0
  180. data/spec/grape/dsl/routing_spec.rb +13 -1
  181. data/spec/grape/dsl/settings_spec.rb +4 -2
  182. data/spec/grape/dsl/validations_spec.rb +2 -0
  183. data/spec/grape/endpoint/declared_spec.rb +848 -0
  184. data/spec/grape/endpoint_spec.rb +75 -515
  185. data/spec/grape/entity_spec.rb +19 -11
  186. data/spec/grape/exceptions/base_spec.rb +67 -0
  187. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  188. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  189. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -1
  190. data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
  191. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -1
  192. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  193. data/spec/grape/exceptions/missing_option_spec.rb +2 -1
  194. data/spec/grape/exceptions/unknown_options_spec.rb +3 -2
  195. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -1
  196. data/spec/grape/exceptions/validation_errors_spec.rb +17 -4
  197. data/spec/grape/exceptions/validation_spec.rb +3 -1
  198. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  199. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  200. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  201. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  202. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  203. data/spec/grape/integration/rack_spec.rb +25 -7
  204. data/spec/grape/loading_spec.rb +2 -0
  205. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  206. data/spec/grape/middleware/auth/dsl_spec.rb +6 -4
  207. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  208. data/spec/grape/middleware/base_spec.rb +10 -0
  209. data/spec/grape/middleware/error_spec.rb +4 -3
  210. data/spec/grape/middleware/exception_spec.rb +205 -55
  211. data/spec/grape/middleware/formatter_spec.rb +130 -15
  212. data/spec/grape/middleware/globals_spec.rb +2 -0
  213. data/spec/grape/middleware/stack_spec.rb +15 -1
  214. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  215. data/spec/grape/middleware/versioner/header_spec.rb +10 -2
  216. data/spec/grape/middleware/versioner/param_spec.rb +4 -2
  217. data/spec/grape/middleware/versioner/path_spec.rb +4 -2
  218. data/spec/grape/middleware/versioner_spec.rb +2 -0
  219. data/spec/grape/named_api_spec.rb +21 -0
  220. data/spec/grape/parser_spec.rb +7 -5
  221. data/spec/grape/path_spec.rb +9 -7
  222. data/spec/grape/presenters/presenter_spec.rb +2 -0
  223. data/spec/grape/request_spec.rb +26 -0
  224. data/spec/grape/util/inheritable_setting_spec.rb +4 -2
  225. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  226. data/spec/grape/util/reverse_stackable_values_spec.rb +15 -13
  227. data/spec/grape/util/stackable_values_spec.rb +16 -14
  228. data/spec/grape/util/strict_hash_configuration_spec.rb +3 -1
  229. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  230. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  231. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
  232. data/spec/grape/validations/params_scope_spec.rb +364 -7
  233. data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
  234. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  235. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  236. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  237. data/spec/grape/validations/types_spec.rb +9 -36
  238. data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
  239. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  240. data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
  241. data/spec/grape/validations/validators/coerce_spec.rb +558 -133
  242. data/spec/grape/validations/validators/default_spec.rb +212 -0
  243. data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
  244. data/spec/grape/validations/validators/except_values_spec.rb +6 -3
  245. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
  246. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  247. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  248. data/spec/grape/validations/validators/same_as_spec.rb +65 -0
  249. data/spec/grape/validations/validators/values_spec.rb +73 -19
  250. data/spec/grape/validations_spec.rb +403 -53
  251. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  252. data/spec/integration/multi_json/json_spec.rb +2 -0
  253. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  254. data/spec/shared/versioning_examples.rb +78 -18
  255. data/spec/spec_helper.rb +13 -2
  256. data/spec/support/basic_auth_encode_helpers.rb +3 -1
  257. data/spec/support/chunks.rb +14 -0
  258. data/spec/support/content_type_helpers.rb +3 -1
  259. data/spec/support/eager_load.rb +19 -0
  260. data/spec/support/endpoint_faker.rb +2 -0
  261. data/spec/support/file_streamer.rb +2 -0
  262. data/spec/support/integer_helpers.rb +2 -0
  263. data/spec/support/versioned_helpers.rb +8 -8
  264. metadata +176 -130
  265. data/Appraisals +0 -32
  266. data/Dangerfile +0 -1
  267. data/Gemfile +0 -34
  268. data/Gemfile.lock +0 -229
  269. data/Guardfile +0 -10
  270. data/RELEASING.md +0 -111
  271. data/Rakefile +0 -70
  272. data/benchmark/simple.rb +0 -27
  273. data/benchmark/simple_with_type_coercer.rb +0 -22
  274. data/gemfiles/multi_json.gemfile +0 -36
  275. data/gemfiles/multi_xml.gemfile +0 -36
  276. data/gemfiles/rack_1.5.2.gemfile +0 -36
  277. data/gemfiles/rack_edge.gemfile +0 -36
  278. data/gemfiles/rails_3.gemfile +0 -37
  279. data/gemfiles/rails_4.gemfile +0 -36
  280. data/gemfiles/rails_5.gemfile +0 -36
  281. data/gemfiles/rails_edge.gemfile +0 -36
  282. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  283. data/lib/grape/util/content_types.rb +0 -26
  284. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
data/UPGRADING.md CHANGED
@@ -1,6 +1,472 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 1.6.0
5
+
6
+ #### Parameter renaming with :as
7
+
8
+ 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.
9
+
10
+ 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.
11
+
12
+ Here comes an illustration of the old and new behaviour as code:
13
+
14
+ ```ruby
15
+ # (1) Rename a to b, while client sends +a+
16
+ optional :a, type: Integer, as: :b
17
+ params = { a: 1 }
18
+ declared(params, include_missing: false)
19
+ # expected => { b: 1 }
20
+ # actual => { b: 1 }
21
+
22
+ # (2) Rename a to b, while client sends +b+
23
+ optional :a, type: Integer, as: :b, values: [1, 2, 3]
24
+ params = { b: '5' }
25
+ declared(params, include_missing: false)
26
+ # expected => { } (>= 1.6.0)
27
+ # actual => { b: '5' } (uncasted, unvalidated, <= 1.5.3)
28
+ ```
29
+
30
+ Another implication of this change is the dependent parameter resolution. Prior to 1.6.0 the following code produced an `Grape::Exceptions::UnknownParameter` because `:a` was replace by `:b`:
31
+
32
+ ```ruby
33
+ params do
34
+ optional :a, as: :b
35
+ given :a do # (<= 1.5.3 you had to reference +:b+ here to make it work)
36
+ requires :c
37
+ end
38
+ end
39
+ ```
40
+
41
+ This code now works without any errors, as the renaming is just an internal behaviour of the `#declared(params)` parameter helper.
42
+
43
+ See [#2189](https://github.com/ruby-grape/grape/pull/2189) for more information.
44
+
45
+ ### Upgrading to >= 1.5.3
46
+
47
+ #### Nil value and coercion
48
+
49
+ 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.
50
+ This behavior was not tested or documented. Version 1.3.0 quietly changed this behavior, in such that `nil` values skipped the coercion. Version 1.5.3 fixes and documents this as follows:
51
+
52
+ ```ruby
53
+ class Api < Grape::API
54
+ params do
55
+ optional :value, type: Integer, coerce_with: ->(val) { val || 0 }
56
+ end
57
+
58
+ get 'example' do
59
+ params[:my_param]
60
+ end
61
+ get '/example', params: { value: nil }
62
+ # 1.5.2 = nil
63
+ # 1.5.3 = 0
64
+ get '/example', params: {}
65
+ # 1.5.2 = nil
66
+ # 1.5.3 = nil
67
+ end
68
+ ```
69
+ See [#2164](https://github.com/ruby-grape/grape/pull/2164) for more information.
70
+
71
+ ### Upgrading to >= 1.5.1
72
+
73
+ #### Dependent params
74
+
75
+ If you use [dependent params](https://github.com/ruby-grape/grape#dependent-parameters) with
76
+ `Grape::Extensions::Hash::ParamBuilder`, make sure a parameter to be dependent on is set as a Symbol.
77
+ If a String is given, a parameter that other parameters depend on won't be found even if it is present.
78
+
79
+ _Correct_:
80
+ ```ruby
81
+ given :matrix do
82
+ # dependent params
83
+ end
84
+ ```
85
+
86
+ _Wrong_:
87
+ ```ruby
88
+ given 'matrix' do
89
+ # dependent params
90
+ end
91
+ ```
92
+
93
+ ### Upgrading to >= 1.5.0
94
+
95
+ 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 `{}`.
96
+
97
+ 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.
98
+
99
+ The following rules now apply to the `declared` helper when params are missing and `include_missing=true`:
100
+
101
+ * Hash params with children will resolve to a Hash with keys for each declared child.
102
+ * Hash params with no children will resolve to `{}`.
103
+ * Set params will resolve to `Set.new`.
104
+ * Array params will resolve to `[]`.
105
+ * All other params will resolve to `nil`.
106
+
107
+ #### Example
108
+
109
+ ```ruby
110
+ class Api < Grape::API
111
+ params do
112
+ optional :outer, type: Hash do
113
+ optional :inner, type: Hash do
114
+ optional :value, type: String
115
+ end
116
+ end
117
+ end
118
+ get 'example' do
119
+ declared(params, include_missing: true)
120
+ end
121
+ end
122
+ ```
123
+
124
+ ```
125
+ get '/example'
126
+ # 1.3.3 = {}
127
+ # 1.5.0 = {outer: {inner: {value:null}}}
128
+ ```
129
+
130
+ For more information see [#2103](https://github.com/ruby-grape/grape/pull/2103).
131
+
132
+ ### Upgrading to >= 1.4.0
133
+
134
+ #### Reworking stream and file and un-deprecating stream like-objects
135
+
136
+ Previously in 0.16 stream-like objects were deprecated. This release restores their functionality for use-cases other than file streaming.
137
+
138
+ This release deprecated `file` in favor of `sendfile` to better document its purpose.
139
+
140
+ 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).
141
+ ```ruby
142
+ class API < Grape::API
143
+ get '/' do
144
+ sendfile '/path/to/file'
145
+ end
146
+ end
147
+ ```
148
+
149
+ Use `stream` to stream file content in chunks.
150
+
151
+ ```ruby
152
+ class API < Grape::API
153
+ get '/' do
154
+ stream '/path/to/file'
155
+ end
156
+ end
157
+ ```
158
+
159
+ Or use `stream` to stream other kinds of content. In the following example a streamer class
160
+ streams paginated data from a database.
161
+
162
+ ```ruby
163
+ class MyObject
164
+ attr_accessor :result
165
+
166
+ def initialize(query)
167
+ @result = query
168
+ end
169
+
170
+ def each
171
+ yield '['
172
+ # Do paginated DB fetches and return each page formatted
173
+ first = false
174
+ result.find_in_batches do |records|
175
+ yield process_records(records, first)
176
+ first = false
177
+ end
178
+ yield ']'
179
+ end
180
+
181
+ def process_records(records, first)
182
+ buffer = +''
183
+ buffer << ',' unless first
184
+ buffer << records.map(&:to_json).join(',')
185
+ buffer
186
+ end
187
+ end
188
+
189
+ class API < Grape::API
190
+ get '/' do
191
+ stream MyObject.new(Sprocket.all)
192
+ end
193
+ end
194
+ ```
195
+
196
+ ### Upgrading to >= 1.3.3
197
+
198
+ #### Nil values for structures
199
+
200
+ Nil values always been a special case when dealing with types especially with the following structures:
201
+
202
+ - Array
203
+ - Hash
204
+ - Set
205
+
206
+ The behavior for these structures has change through out the latest releases. For example:
207
+
208
+ ```ruby
209
+ class Api < Grape::API
210
+ params do
211
+ require :my_param, type: Array[Integer]
212
+ end
213
+
214
+ get 'example' do
215
+ params[:my_param]
216
+ end
217
+ get '/example', params: { my_param: nil }
218
+ # 1.3.1 = []
219
+ # 1.3.2 = nil
220
+ end
221
+ ```
222
+
223
+ For now on, `nil` values stay `nil` values for all types, including arrays, sets and hashes.
224
+
225
+ If you want to have the same behavior as 1.3.1, apply a `default` validator:
226
+
227
+ ```ruby
228
+ class Api < Grape::API
229
+ params do
230
+ require :my_param, type: Array[Integer], default: []
231
+ end
232
+
233
+ get 'example' do
234
+ params[:my_param]
235
+ end
236
+ get '/example', params: { my_param: nil } # => []
237
+ end
238
+ ```
239
+
240
+ #### Default validator
241
+
242
+ Default validator is now applied for `nil` values.
243
+
244
+ ```ruby
245
+ class Api < Grape::API
246
+ params do
247
+ requires :my_param, type: Integer, default: 0
248
+ end
249
+
250
+ get 'example' do
251
+ params[:my_param]
252
+ end
253
+ get '/example', params: { my_param: nil } #=> before: nil, after: 0
254
+ end
255
+ ```
256
+
257
+ ### Upgrading to >= 1.3.0
258
+
259
+ You will need to upgrade to this version if you depend on `rack >= 2.1.0`.
260
+
261
+ #### Ruby
262
+
263
+ After adding dry-types, Ruby 2.4 or newer is required.
264
+
265
+ #### Coercion
266
+
267
+ [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`.
268
+
269
+ Here's an example of how to migrate a custom type from Virtus to dry-types:
270
+
271
+ ```ruby
272
+ # Legacy Grape parser
273
+ class SecureUriType < Virtus::Attribute
274
+ def coerce(input)
275
+ URI.parse value
276
+ end
277
+
278
+ def value_coerced?(input)
279
+ value.is_a? String
280
+ end
281
+ end
282
+
283
+ params do
284
+ requires :secure_uri, type: SecureUri
285
+ end
286
+ ```
287
+
288
+ To use dry-types, we need to:
289
+
290
+ 1. Remove the inheritance of `Virtus::Attribute`
291
+ 1. Rename `coerce` to `self.parse`
292
+ 1. Rename `value_coerced?` to `self.parsed?`
293
+
294
+ 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:
295
+
296
+ ```ruby
297
+ # New dry-types parser
298
+ class SecureUri
299
+ def self.parse(value)
300
+ URI.parse value
301
+ end
302
+
303
+ def self.parsed?(value)
304
+ value.is_a? URI::HTTPS
305
+ end
306
+ end
307
+
308
+ params do
309
+ requires :secure_uri, type: SecureUri
310
+ end
311
+ ```
312
+
313
+ #### Coercing to `FalseClass` or `TrueClass` no longer works
314
+
315
+ Previous Grape versions allowed this, though it wasn't documented:
316
+
317
+ ```ruby
318
+ requires :true_value, type: TrueClass
319
+ requires :bool_value, types: [FalseClass, TrueClass]
320
+ ```
321
+
322
+ This is no longer supported, if you do this, your values will never be valid. Instead you should do this:
323
+
324
+ ```ruby
325
+ requires :true_value, type: Boolean # in your endpoint you should validate if this is actually `true`
326
+ requires :bool_value, type: Boolean
327
+ ```
328
+
329
+ #### Ensure that Array types have explicit coercions
330
+
331
+ 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:
332
+
333
+ ```ruby
334
+ requires :values, type: Array[String]
335
+ ```
336
+
337
+ 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:
338
+
339
+ ```ruby
340
+ requires :values, type: Array[String], coerce_with: ->(val) { val.split(',').map(&:strip) }
341
+ ```
342
+
343
+ Likewise, for `Array[Integer]`, you might do:
344
+
345
+ ```ruby
346
+ requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(',').map(&:strip).map(&:to_i) }
347
+ ```
348
+
349
+ For more information see [#1920](https://github.com/ruby-grape/grape/pull/1920).
350
+
351
+ ### Upgrading to >= 1.2.4
352
+
353
+ #### Headers in `error!` call
354
+
355
+ 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.
356
+
357
+ ```ruby
358
+ class SampleApi < Grape::API
359
+ before do
360
+ header 'X-Before-Header', 'before_call'
361
+ end
362
+
363
+ get 'ping' do
364
+ header 'X-App-Header', 'on_call'
365
+ error! :pong, 400, 'X-Error-Details' => 'Invalid token'
366
+ end
367
+ end
368
+ ```
369
+ **Former behaviour**
370
+ ```ruby
371
+ response.headers['X-Before-Header'] # => nil
372
+ response.headers['X-App-Header'] # => nil
373
+ response.headers['X-Error-Details'] # => Invalid token
374
+ ```
375
+
376
+ **Current behaviour**
377
+ ```ruby
378
+ response.headers['X-Before-Header'] # => 'before_call'
379
+ response.headers['X-App-Header'] # => 'on_call'
380
+ response.headers['X-Error-Details'] # => Invalid token
381
+ ```
382
+
383
+ ### Upgrading to >= 1.2.1
384
+
385
+ #### Obtaining the name of a mounted class
386
+
387
+ In order to make obtaining the name of a mounted class simpler, we've delegated `.to_s` to `base.name`
388
+
389
+ **Deprecated in 1.2.0**
390
+ ```ruby
391
+ payload[:endpoint].options[:for].name
392
+ ```
393
+ **New**
394
+ ```ruby
395
+ payload[:endpoint].options[:for].to_s
396
+ ```
397
+
398
+ ### Upgrading to >= 1.2.0
399
+
400
+ #### Changes in the Grape::API class
401
+
402
+ ##### Patching the class
403
+
404
+ 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`.
405
+
406
+ 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`.
407
+
408
+ Note, this is particularly relevant if you are opening the class `Grape::API` for modification.
409
+
410
+ **Deprecated**
411
+ ```ruby
412
+ class Grape::API
413
+ # your patched logic
414
+ ...
415
+ end
416
+ ```
417
+ **New**
418
+ ```ruby
419
+ class Grape::API::Instance
420
+ # your patched logic
421
+ ...
422
+ end
423
+ ```
424
+
425
+ ##### `name` (and other caveats) of the mounted API
426
+
427
+ After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
428
+ which inherit from `Grape::API::Instance`.
429
+
430
+ What this means in practice, is:
431
+
432
+ - Generally: you can access the named class from the instance calling the getter `base`.
433
+ - In particular: If you need the `name`, you can use `base`.`name`.
434
+
435
+ **Deprecated**
436
+
437
+ ```ruby
438
+ payload[:endpoint].options[:for].name
439
+ ```
440
+
441
+ **New**
442
+
443
+ ```ruby
444
+ payload[:endpoint].options[:for].base.name
445
+ ```
446
+
447
+ #### Changes in rescue_from returned object
448
+
449
+ 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.
450
+
451
+ ```ruby
452
+ class Twitter::API < Grape::API
453
+ rescue_from :all do |e|
454
+ # version prior to 1.2.0
455
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
456
+ # 1.2.0 version
457
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
458
+ end
459
+ end
460
+ ```
461
+
462
+ See [#1757](https://github.com/ruby-grape/grape/pull/1757) and [#1776](https://github.com/ruby-grape/grape/pull/1776) for more information.
463
+
464
+ ### Upgrading to >= 1.1.0
465
+
466
+ #### Changes in HTTP Response Code for Unsupported Content Type
467
+
468
+ For PUT, POST, PATCH, and DELETE requests where a non-empty body and a "Content-Type" header is supplied that is not supported by the Grape API, Grape will no longer return a 406 "Not Acceptable" HTTP status code and will instead return a 415 "Unsupported Media Type" so that the usage of HTTP status code falls more in line with the specification of [RFC 2616](https://www.ietf.org/rfc/rfc2616.txt).
469
+
4
470
  ### Upgrading to >= 1.0.0
5
471
 
6
472
  #### Changes in XML and JSON Parsers
@@ -64,8 +530,7 @@ See [#1610](https://github.com/ruby-grape/grape/pull/1610) for more information.
64
530
 
65
531
  #### The `except`, `except_message`, and `proc` options of the `values` validator are deprecated.
66
532
 
67
- The new `except_values` validator should be used in place of the `except` and `except_message` options of
68
- the `values` validator.
533
+ The new `except_values` validator should be used in place of the `except` and `except_message` options of the `values` validator.
69
534
 
70
535
  Arity one Procs may now be used directly as the `values` option to explicitly test param values.
71
536
 
@@ -141,9 +606,7 @@ get '/example' #=> before: 405, after: 404
141
606
 
142
607
  #### Removed param processing from built-in OPTIONS handler
143
608
 
144
- When a request is made to the built-in `OPTIONS` handler, only the `before` and `after`
145
- callbacks associated with the resource will be run. The `before_validation` and
146
- `after_validation` callbacks and parameter validations will be skipped.
609
+ 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.
147
610
 
148
611
  See [#1505](https://github.com/ruby-grape/grape/pull/1505) for more information.
149
612
 
@@ -164,8 +627,7 @@ See [#1510](https://github.com/ruby-grape/grape/pull/1510) for more information.
164
627
 
165
628
  #### The default status code for DELETE is now 204 instead of 200.
166
629
 
167
- Breaking change: Sets the default response status code for a delete request to 204.
168
- 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.
630
+ 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.
169
631
 
170
632
  To achieve the old behavior, one has to set it explicitly:
171
633
  ```ruby
@@ -343,18 +805,14 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
343
805
 
344
806
  #### Bypasses formatters when status code indicates no content
345
807
 
346
- To be consistent with rack and it's handling of standard responses
347
- associated with no content, both default and custom formatters will now
808
+ To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now
348
809
  be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
349
810
 
350
811
  See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
351
812
 
352
813
  #### Redirects respond as plain text with message
353
814
 
354
- `#redirect` now uses `text/plain` regardless of whether that format has
355
- been enabled. This prevents formatters from attempting to serialize the
356
- message body and allows for a descriptive message body to be provided - and
357
- optionally overridden - that better fulfills the theme of the HTTP spec.
815
+ `#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.
358
816
 
359
817
  See [#1194](https://github.com/ruby-grape/grape/pull/1194) for more information.
360
818
 
@@ -388,10 +846,7 @@ end
388
846
 
389
847
  See [#1029](https://github.com/ruby-grape/grape/pull/1029) for more information.
390
848
 
391
- There is a known issue because of this change. When Grape is used with an older
392
- than 1.2.4 version of [warden](https://github.com/hassox/warden) there may be raised
393
- the following exception having the [rack-mount](https://github.com/jm/rack-mount) gem's
394
- lines as last ones in the backtrace:
849
+ 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:
395
850
 
396
851
  ```
397
852
  NoMethodError: undefined method `[]' for nil:NilClass
data/grape.gemspec CHANGED
@@ -1,4 +1,6 @@
1
- $LOAD_PATH.push 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
- s.add_runtime_dependency 'rack', '>= 1.3.0'
16
- s.add_runtime_dependency 'mustermann-grape', '~> 1.0.0'
17
- s.add_runtime_dependency 'rack-accept'
18
23
  s.add_runtime_dependency 'activesupport'
19
- s.add_runtime_dependency 'virtus', '>= 1.0.0'
20
24
  s.add_runtime_dependency 'builder'
25
+ s.add_runtime_dependency 'dry-types', '>= 1.1'
26
+ s.add_runtime_dependency 'mustermann-grape', '~> 1.0.0'
27
+ s.add_runtime_dependency 'rack', '>= 1.3.0'
28
+ s.add_runtime_dependency 'rack-accept'
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.5.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