grape 1.1.0 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
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/UPGRADING.md CHANGED
@@ -1,6 +1,424 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+
5
+ ### Upgrading to >= 1.5.3
6
+
7
+ ### Nil value and coercion
8
+
9
+ 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.
10
+ 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:
11
+
12
+ ```ruby
13
+ class Api < Grape::API
14
+ params do
15
+ optional :value, type: Integer, coerce_with: ->(val) { val || 0 }
16
+ end
17
+
18
+ get 'example' do
19
+ params[:my_param]
20
+ end
21
+ get '/example', params: { value: nil }
22
+ # 1.5.2 = nil
23
+ # 1.5.3 = 0
24
+ get '/example', params: {}
25
+ # 1.5.2 = nil
26
+ # 1.5.3 = nil
27
+ end
28
+ ```
29
+ See [#2164](https://github.com/ruby-grape/grape/pull/2164) for more information.
30
+
31
+ ### Upgrading to >= 1.5.1
32
+
33
+ #### Dependent params
34
+
35
+ If you use [dependent params](https://github.com/ruby-grape/grape#dependent-parameters) with
36
+ `Grape::Extensions::Hash::ParamBuilder`, make sure a parameter to be dependent on is set as a Symbol.
37
+ If a String is given, a parameter that other parameters depend on won't be found even if it is present.
38
+
39
+ _Correct_:
40
+ ```ruby
41
+ given :matrix do
42
+ # dependent params
43
+ end
44
+ ```
45
+
46
+ _Wrong_:
47
+ ```ruby
48
+ given 'matrix' do
49
+ # dependent params
50
+ end
51
+ ```
52
+
53
+ ### Upgrading to >= 1.5.0
54
+
55
+ 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 `{}`.
56
+
57
+ 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.
58
+
59
+ The following rules now apply to the `declared` helper when params are missing and `include_missing=true`:
60
+
61
+ * Hash params with children will resolve to a Hash with keys for each declared child.
62
+ * Hash params with no children will resolve to `{}`.
63
+ * Set params will resolve to `Set.new`.
64
+ * Array params will resolve to `[]`.
65
+ * All other params will resolve to `nil`.
66
+
67
+ #### Example
68
+
69
+ ```ruby
70
+ class Api < Grape::API
71
+ params do
72
+ optional :outer, type: Hash do
73
+ optional :inner, type: Hash do
74
+ optional :value, type: String
75
+ end
76
+ end
77
+ end
78
+ get 'example' do
79
+ declared(params, include_missing: true)
80
+ end
81
+ end
82
+ ```
83
+
84
+ ```
85
+ get '/example'
86
+ # 1.3.3 = {}
87
+ # 1.5.0 = {outer: {inner: {value:null}}}
88
+ ```
89
+
90
+ For more information see [#2103](https://github.com/ruby-grape/grape/pull/2103).
91
+
92
+ ### Upgrading to >= 1.4.0
93
+
94
+ #### Reworking stream and file and un-deprecating stream like-objects
95
+
96
+ Previously in 0.16 stream-like objects were deprecated. This release restores their functionality for use-cases other than file streaming.
97
+
98
+ This release deprecated `file` in favor of `sendfile` to better document its purpose.
99
+
100
+ 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).
101
+ ```ruby
102
+ class API < Grape::API
103
+ get '/' do
104
+ sendfile '/path/to/file'
105
+ end
106
+ end
107
+ ```
108
+
109
+ Use `stream` to stream file content in chunks.
110
+
111
+ ```ruby
112
+ class API < Grape::API
113
+ get '/' do
114
+ stream '/path/to/file'
115
+ end
116
+ end
117
+ ```
118
+
119
+ Or use `stream` to stream other kinds of content. In the following example a streamer class
120
+ streams paginated data from a database.
121
+
122
+ ```ruby
123
+ class MyObject
124
+ attr_accessor :result
125
+
126
+ def initialize(query)
127
+ @result = query
128
+ end
129
+
130
+ def each
131
+ yield '['
132
+ # Do paginated DB fetches and return each page formatted
133
+ first = false
134
+ result.find_in_batches do |records|
135
+ yield process_records(records, first)
136
+ first = false
137
+ end
138
+ yield ']'
139
+ end
140
+
141
+ def process_records(records, first)
142
+ buffer = +''
143
+ buffer << ',' unless first
144
+ buffer << records.map(&:to_json).join(',')
145
+ buffer
146
+ end
147
+ end
148
+
149
+ class API < Grape::API
150
+ get '/' do
151
+ stream MyObject.new(Sprocket.all)
152
+ end
153
+ end
154
+ ```
155
+
156
+ ### Upgrading to >= 1.3.3
157
+
158
+ #### Nil values for structures
159
+
160
+ Nil values always been a special case when dealing with types especially with the following structures:
161
+
162
+ - Array
163
+ - Hash
164
+ - Set
165
+
166
+ The behavior for these structures has change through out the latest releases. For example:
167
+
168
+ ```ruby
169
+ class Api < Grape::API
170
+ params do
171
+ require :my_param, type: Array[Integer]
172
+ end
173
+
174
+ get 'example' do
175
+ params[:my_param]
176
+ end
177
+ get '/example', params: { my_param: nil }
178
+ # 1.3.1 = []
179
+ # 1.3.2 = nil
180
+ end
181
+ ```
182
+
183
+ For now on, `nil` values stay `nil` values for all types, including arrays, sets and hashes.
184
+
185
+ If you want to have the same behavior as 1.3.1, apply a `default` validator:
186
+
187
+ ```ruby
188
+ class Api < Grape::API
189
+ params do
190
+ require :my_param, type: Array[Integer], default: []
191
+ end
192
+
193
+ get 'example' do
194
+ params[:my_param]
195
+ end
196
+ get '/example', params: { my_param: nil } # => []
197
+ end
198
+ ```
199
+
200
+ #### Default validator
201
+
202
+ Default validator is now applied for `nil` values.
203
+
204
+ ```ruby
205
+ class Api < Grape::API
206
+ params do
207
+ requires :my_param, type: Integer, default: 0
208
+ end
209
+
210
+ get 'example' do
211
+ params[:my_param]
212
+ end
213
+ get '/example', params: { my_param: nil } #=> before: nil, after: 0
214
+ end
215
+ ```
216
+
217
+ ### Upgrading to >= 1.3.0
218
+
219
+ #### Ruby
220
+
221
+ After adding dry-types, Ruby 2.4 or newer is required.
222
+
223
+ #### Coercion
224
+
225
+ [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`.
226
+
227
+ Here's an example of how to migrate a custom type from Virtus to dry-types:
228
+
229
+ ```ruby
230
+ # Legacy Grape parser
231
+ class SecureUriType < Virtus::Attribute
232
+ def coerce(input)
233
+ URI.parse value
234
+ end
235
+
236
+ def value_coerced?(input)
237
+ value.is_a? String
238
+ end
239
+ end
240
+
241
+ params do
242
+ requires :secure_uri, type: SecureUri
243
+ end
244
+ ```
245
+
246
+ To use dry-types, we need to:
247
+
248
+ 1. Remove the inheritance of `Virtus::Attribute`
249
+ 1. Rename `coerce` to `self.parse`
250
+ 1. Rename `value_coerced?` to `self.parsed?`
251
+
252
+ 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:
253
+
254
+ ```ruby
255
+ # New dry-types parser
256
+ class SecureUri
257
+ def self.parse(value)
258
+ URI.parse value
259
+ end
260
+
261
+ def self.parsed?(value)
262
+ value.is_a? URI::HTTPS
263
+ end
264
+ end
265
+
266
+ params do
267
+ requires :secure_uri, type: SecureUri
268
+ end
269
+ ```
270
+
271
+ #### Coercing to `FalseClass` or `TrueClass` no longer works
272
+
273
+ Previous Grape versions allowed this, though it wasn't documented:
274
+
275
+ ```ruby
276
+ requires :true_value, type: TrueClass
277
+ requires :bool_value, types: [FalseClass, TrueClass]
278
+ ```
279
+
280
+ This is no longer supported, if you do this, your values will never be valid. Instead you should do this:
281
+
282
+ ```ruby
283
+ requires :true_value, type: Boolean # in your endpoint you should validate if this is actually `true`
284
+ requires :bool_value, type: Boolean
285
+ ```
286
+
287
+ #### Ensure that Array types have explicit coercions
288
+
289
+ 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:
290
+
291
+ ```ruby
292
+ requires :values, type: Array[String]
293
+ ```
294
+
295
+ 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:
296
+
297
+ ```ruby
298
+ requires :values, type: Array[String], coerce_with: ->(val) { val.split(',').map(&:strip) }
299
+ ```
300
+
301
+ Likewise, for `Array[Integer]`, you might do:
302
+
303
+ ```ruby
304
+ requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(',').map(&:strip).map(&:to_i) }
305
+ ```
306
+
307
+ For more information see [#1920](https://github.com/ruby-grape/grape/pull/1920).
308
+
309
+ ### Upgrading to >= 1.2.4
310
+
311
+ #### Headers in `error!` call
312
+
313
+ 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.
314
+
315
+ ```ruby
316
+ class SampleApi < Grape::API
317
+ before do
318
+ header 'X-Before-Header', 'before_call'
319
+ end
320
+
321
+ get 'ping' do
322
+ header 'X-App-Header', 'on_call'
323
+ error! :pong, 400, 'X-Error-Details' => 'Invalid token'
324
+ end
325
+ end
326
+ ```
327
+ **Former behaviour**
328
+ ```ruby
329
+ response.headers['X-Before-Header'] # => nil
330
+ response.headers['X-App-Header'] # => nil
331
+ response.headers['X-Error-Details'] # => Invalid token
332
+ ```
333
+
334
+ **Current behaviour**
335
+ ```ruby
336
+ response.headers['X-Before-Header'] # => 'before_call'
337
+ response.headers['X-App-Header'] # => 'on_call'
338
+ response.headers['X-Error-Details'] # => Invalid token
339
+ ```
340
+
341
+ ### Upgrading to >= 1.2.1
342
+
343
+ #### Obtaining the name of a mounted class
344
+
345
+ In order to make obtaining the name of a mounted class simpler, we've delegated `.to_s` to `base.name`
346
+
347
+ **Deprecated in 1.2.0**
348
+ ```ruby
349
+ payload[:endpoint].options[:for].name
350
+ ```
351
+ **New**
352
+ ```ruby
353
+ payload[:endpoint].options[:for].to_s
354
+ ```
355
+
356
+ ### Upgrading to >= 1.2.0
357
+
358
+ #### Changes in the Grape::API class
359
+
360
+ ##### Patching the class
361
+
362
+ 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`.
363
+
364
+ 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`.
365
+
366
+ Note, this is particularly relevant if you are opening the class `Grape::API` for modification.
367
+
368
+ **Deprecated**
369
+ ```ruby
370
+ class Grape::API
371
+ # your patched logic
372
+ ...
373
+ end
374
+ ```
375
+ **New**
376
+ ```ruby
377
+ class Grape::API::Instance
378
+ # your patched logic
379
+ ...
380
+ end
381
+ ```
382
+
383
+ ##### `name` (and other caveats) of the mounted API
384
+
385
+ After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
386
+ which inherit from `Grape::API::Instance`.
387
+
388
+ What this means in practice, is:
389
+
390
+ - Generally: you can access the named class from the instance calling the getter `base`.
391
+ - In particular: If you need the `name`, you can use `base`.`name`.
392
+
393
+ **Deprecated**
394
+
395
+ ```ruby
396
+ payload[:endpoint].options[:for].name
397
+ ```
398
+
399
+ **New**
400
+
401
+ ```ruby
402
+ payload[:endpoint].options[:for].base.name
403
+ ```
404
+
405
+ #### Changes in rescue_from returned object
406
+
407
+ 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.
408
+
409
+ ```ruby
410
+ class Twitter::API < Grape::API
411
+ rescue_from :all do |e|
412
+ # version prior to 1.2.0
413
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
414
+ # 1.2.0 version
415
+ Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
416
+ end
417
+ end
418
+ ```
419
+
420
+ See [#1757](https://github.com/ruby-grape/grape/pull/1757) and [#1776](https://github.com/ruby-grape/grape/pull/1776) for more information.
421
+
4
422
  ### Upgrading to >= 1.1.0
5
423
 
6
424
  #### Changes in HTTP Response Code for Unsupported Content Type
@@ -70,8 +488,7 @@ See [#1610](https://github.com/ruby-grape/grape/pull/1610) for more information.
70
488
 
71
489
  #### The `except`, `except_message`, and `proc` options of the `values` validator are deprecated.
72
490
 
73
- The new `except_values` validator should be used in place of the `except` and `except_message` options of
74
- the `values` validator.
491
+ The new `except_values` validator should be used in place of the `except` and `except_message` options of the `values` validator.
75
492
 
76
493
  Arity one Procs may now be used directly as the `values` option to explicitly test param values.
77
494
 
@@ -147,9 +564,7 @@ get '/example' #=> before: 405, after: 404
147
564
 
148
565
  #### Removed param processing from built-in OPTIONS handler
149
566
 
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.
567
+ 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
568
 
154
569
  See [#1505](https://github.com/ruby-grape/grape/pull/1505) for more information.
155
570
 
@@ -170,8 +585,7 @@ See [#1510](https://github.com/ruby-grape/grape/pull/1510) for more information.
170
585
 
171
586
  #### The default status code for DELETE is now 204 instead of 200.
172
587
 
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.
588
+ 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
589
 
176
590
  To achieve the old behavior, one has to set it explicitly:
177
591
  ```ruby
@@ -349,18 +763,14 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
349
763
 
350
764
  #### Bypasses formatters when status code indicates no content
351
765
 
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
766
+ To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now
354
767
  be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
355
768
 
356
769
  See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
357
770
 
358
771
  #### Redirects respond as plain text with message
359
772
 
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.
773
+ `#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
774
 
365
775
  See [#1194](https://github.com/ruby-grape/grape/pull/1194) for more information.
366
776
 
@@ -394,10 +804,7 @@ end
394
804
 
395
805
  See [#1029](https://github.com/ruby-grape/grape/pull/1029) for more information.
396
806
 
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:
807
+ 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
808
 
402
809
  ```
403
810
  NoMethodError: undefined method `[]' for nil:NilClass