grape 1.3.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d63fb79e412ead32064ad4994e171e65962131a04afb20d12957942c744f4df8
4
- data.tar.gz: 9a9f4fb654e346eabb8a0b902d5e49ae54dc567797789c98c76f2a2fe2e2daa9
3
+ metadata.gz: 342f290903dcc993f0e60d0d15bee151f3425abf2b68a191da459d9ac47ac943
4
+ data.tar.gz: 5b03c2c50e3b485787fd9d7ae64130b7cfe56ca9a9128d51133e46e58562d151
5
5
  SHA512:
6
- metadata.gz: b75afa355e6a2b8200d72a2c4407bc78646648832a94d576bff06bdebde90d54b1897df09a560665bd9f58e735f9832a110b63bbcaabe14a4e2ee3c97e743b57
7
- data.tar.gz: 18a4ea057230ae0e6cfe16f92e5043339b66026a94d29bd8c897d2482577cb279ec4e202f41a643605a3ea09c748fa159fddc36977a3c4828b5b64daef080272
6
+ metadata.gz: c6f78f911d63d73b8cd2422b8918d53fadfd963e4b212e555f9c8fac8d1dfe801ca2e29a9e1ca236e41a82c1b75a0e503062d5a87430c1821817fae83cfd8ffd
7
+ data.tar.gz: c856baf9f14a2eebb371f69bd35f6b7a555113e92eb9e35384901df09ac86dfcea691753ef9990123e54a335a0b9dca66b97170fb27a377cf0bb3f4f6e5233ae
@@ -1,3 +1,20 @@
1
+ ### 1.4.0 (2020/07/10)
2
+
3
+ #### Features
4
+
5
+ * [#1520](https://github.com/ruby-grape/grape/pull/1520): Un-deprecate stream-like objects - [@urkle](https://github.com/urkle).
6
+ * [#2060](https://github.com/ruby-grape/grape/pull/2060): Drop support for Ruby 2.4 - [@dblock](https://github.com/dblock).
7
+ * [#2060](https://github.com/ruby-grape/grape/pull/2060): Upgraded Rubocop to 0.84.0 - [@dblock](https://github.com/dblock).
8
+ * [#2077](https://github.com/ruby-grape/grape/pull/2077): Simplify logic for defining declared params - [@dnesteryuk](https://github.com/dnesteryuk).
9
+ * [#2084](https://github.com/ruby-grape/grape/pull/2084): Fix memory leak in path normalization - [@fcheung](https://github.com/fcheung).
10
+
11
+ #### Fixes
12
+
13
+ * [#2067](https://github.com/ruby-grape/grape/pull/2067): Coerce empty string to nil for all primitive types except String - [@petekinnecom](https://github.com/petekinnecom).
14
+ * [#2064](https://github.com/ruby-grape/grape/pull/2064): Fix Ruby 2.7 deprecation warning in `Grape::Middleware::Base#initialize` - [@skarger](https://github.com/skarger).
15
+ * [#2072](https://github.com/ruby-grape/grape/pull/2072): Fix `Grape.eager_load!` and `compile!` - [@stanhu](https://github.com/stanhu).
16
+ * [#2076](https://github.com/ruby-grape/grape/pull/2076): Make route information available for hooks when the automatically generated endpoints are invoked - [@anakinj](https://github.com/anakinj).
17
+
1
18
  ### 1.3.3 (2020/05/23)
2
19
 
3
20
  #### Features
@@ -52,7 +69,8 @@
52
69
  * [#1976](https://github.com/ruby-grape/grape/pull/1976): Ensure classes/modules listed for autoload really exist - [@dnesteryuk](https://github.com/dnesteryuk).
53
70
  * [#1971](https://github.com/ruby-grape/grape/pull/1971): Fix BigDecimal coercion - [@FlickStuart](https://github.com/FlickStuart).
54
71
  * [#1968](https://github.com/ruby-grape/grape/pull/1968): Fix args forwarding in Grape::Middleware::Stack#merge_with for ruby 2.7.0 - [@dm1try](https://github.com/dm1try).
55
- * [#1988](https://github.com/ruby-grape/grape/pull/1988): Refactored the full_messages method and stop overriding full_message - [@hosseintoussi](https://github.com/hosseintoussi).
72
+ * [#1988](https://github.com/ruby-grape/grape/pull/1988): Refactor the full_messages method and stop overriding full_message - [@hosseintoussi](https://github.com/hosseintoussi).
73
+ * [#1956](https://github.com/ruby-grape/grape/pull/1956): Comply with Rack spec, fix `undefined method [] for nil:NilClass` error when upgrading Rack - [@ioquatix](https://github.com/ioquatix).
56
74
 
57
75
  ### 1.3.0 (2020/01/11)
58
76
 
data/README.md CHANGED
@@ -156,7 +156,8 @@ content negotiation, versioning and much more.
156
156
 
157
157
  ## Stable Release
158
158
 
159
- You're reading the documentation for the stable release of Grape, **1.3.3**.
159
+ You're reading the documentation for the stable release of Grape, 1.4.0.
160
+ Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
160
161
 
161
162
  ## Project Resources
162
163
 
@@ -1057,13 +1058,13 @@ params do
1057
1058
  end
1058
1059
  ```
1059
1060
 
1060
- Note that default values will be passed through to any validation options specified.
1061
- The following example will always fail if `:color` is not explicitly provided.
1062
-
1063
1061
  Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
1064
1062
  number for each call to the endpoint of this `params` block. To have the default evaluate
1065
1063
  lazily with each request use a lambda, like `:random_number` above.
1066
1064
 
1065
+ Note that default values will be passed through to any validation options specified.
1066
+ The following example will always fail if `:color` is not explicitly provided.
1067
+
1067
1068
  ```ruby
1068
1069
  params do
1069
1070
  optional :color, type: String, default: 'blue', values: ['red', 'green']
@@ -3166,17 +3167,19 @@ end
3166
3167
 
3167
3168
  Use `body false` to return `204 No Content` without any data or content-type.
3168
3169
 
3169
- You can also set the response to a file with `file`.
3170
+ You can also set the response to a file with `sendfile`. This works with the
3171
+ [Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send
3172
+ the file through your web server software.
3170
3173
 
3171
3174
  ```ruby
3172
3175
  class API < Grape::API
3173
3176
  get '/' do
3174
- file '/path/to/file'
3177
+ sendfile '/path/to/file'
3175
3178
  end
3176
3179
  end
3177
3180
  ```
3178
3181
 
3179
- If you want a file to be streamed using Rack::Chunked, use `stream`.
3182
+ To stream a file in chunks use `stream`
3180
3183
 
3181
3184
  ```ruby
3182
3185
  class API < Grape::API
@@ -3186,6 +3189,26 @@ class API < Grape::API
3186
3189
  end
3187
3190
  ```
3188
3191
 
3192
+ If you want to stream non-file data use the `stream` method and a `Stream` object.
3193
+ This is an object that responds to `each` and yields for each chunk to send to the client.
3194
+ Each chunk will be sent as it is yielded instead of waiting for all of the content to be available.
3195
+
3196
+ ```ruby
3197
+ class MyStream
3198
+ def each
3199
+ yield 'part 1'
3200
+ yield 'part 2'
3201
+ yield 'part 3'
3202
+ end
3203
+ end
3204
+
3205
+ class API < Grape::API
3206
+ get '/' do
3207
+ stream MyStream.new
3208
+ end
3209
+ end
3210
+ ```
3211
+
3189
3212
  ## Authentication
3190
3213
 
3191
3214
  ### Basic and Digest Auth
@@ -3,14 +3,79 @@ Upgrading Grape
3
3
 
4
4
  ### Upgrading to >= 1.4.0
5
5
 
6
+ #### Reworking stream and file and un-deprecating stream like-objects
7
+
8
+ Previously in 0.16 stream-like objects were deprecated. This release restores their functionality for use-cases other than file streaming.
9
+
10
+ This release deprecated `file` in favor of `sendfile` to better document its purpose.
11
+
12
+ 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).
13
+ ```ruby
14
+ class API < Grape::API
15
+ get '/' do
16
+ sendfile '/path/to/file'
17
+ end
18
+ end
19
+ ```
20
+
21
+ Use `stream` to stream file content in chunks.
22
+
23
+ ```ruby
24
+ class API < Grape::API
25
+ get '/' do
26
+ stream '/path/to/file'
27
+ end
28
+ end
29
+ ```
30
+
31
+ Or use `stream` to stream other kinds of content. In the following example a streamer class
32
+ streams paginated data from a database.
33
+
34
+ ```ruby
35
+ class MyObject
36
+ attr_accessor :result
37
+
38
+ def initialize(query)
39
+ @result = query
40
+ end
41
+
42
+ def each
43
+ yield '['
44
+ # Do paginated DB fetches and return each page formatted
45
+ first = false
46
+ result.find_in_batches do |records|
47
+ yield process_records(records, first)
48
+ first = false
49
+ end
50
+ yield ']'
51
+ end
52
+
53
+ def process_records(records, first)
54
+ buffer = +''
55
+ buffer << ',' unless first
56
+ buffer << records.map(&:to_json).join(',')
57
+ buffer
58
+ end
59
+ end
60
+
61
+ class API < Grape::API
62
+ get '/' do
63
+ stream MyObject.new(Sprocket.all)
64
+ end
65
+ end
66
+ ```
67
+
68
+ ### Upgrading to >= 1.3.3
69
+
6
70
  #### Nil values for structures
7
71
 
8
72
  Nil values always been a special case when dealing with types especially with the following structures:
9
- - Array
10
- - Hash
11
- - Set
12
-
13
- The behaviour for these structures has change through out the latest releases. For instance:
73
+
74
+ - Array
75
+ - Hash
76
+ - Set
77
+
78
+ The behavior for these structures has change through out the latest releases. For example:
14
79
 
15
80
  ```ruby
16
81
  class Api < Grape::API
@@ -26,9 +91,10 @@ class Api < Grape::API
26
91
  # 1.3.2 = nil
27
92
  end
28
93
  ```
94
+
29
95
  For now on, `nil` values stay `nil` values for all types, including arrays, sets and hashes.
30
96
 
31
- If you want to have the same behavior as 1.3.1, apply a `default` validator
97
+ If you want to have the same behavior as 1.3.1, apply a `default` validator:
32
98
 
33
99
  ```ruby
34
100
  class Api < Grape::API
@@ -68,10 +134,7 @@ After adding dry-types, Ruby 2.4 or newer is required.
68
134
 
69
135
  #### Coercion
70
136
 
71
- [Virtus](https://github.com/solnic/virtus) has been replaced by
72
- [dry-types](https://dry-rb.org/gems/dry-types/1.2/) for parameter
73
- coercion. If your project depends on Virtus outside of Grape, explicitly
74
- add it to your `Gemfile`.
137
+ [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`.
75
138
 
76
139
  Here's an example of how to migrate a custom type from Virtus to dry-types:
77
140
 
@@ -98,10 +161,7 @@ To use dry-types, we need to:
98
161
  1. Rename `coerce` to `self.parse`
99
162
  1. Rename `value_coerced?` to `self.parsed?`
100
163
 
101
- The custom type must have a class-level `parse` method to the model. A
102
- class-level `parsed?` is needed if the parsed type differs from the
103
- defined type. In the example below, since `SecureUri` is not the same
104
- as `URI::HTTPS`, `self.parsed?` is needed:
164
+ 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:
105
165
 
106
166
  ```ruby
107
167
  # New dry-types parser
@@ -120,21 +180,31 @@ params do
120
180
  end
121
181
  ```
122
182
 
183
+ #### Coercing to `FalseClass` or `TrueClass` no longer works
184
+
185
+ Previous Grape versions allowed this, though it wasn't documented:
186
+
187
+ ```ruby
188
+ requires :true_value, type: TrueClass
189
+ requires :bool_value, types: [FalseClass, TrueClass]
190
+ ```
191
+
192
+ This is no longer supported, if you do this, your values will never be valid. Instead you should do this:
193
+
194
+ ```ruby
195
+ requires :true_value, type: Boolean # in your endpoint you should validate if this is actually `true`
196
+ requires :bool_value, type: Boolean
197
+ ```
198
+
123
199
  #### Ensure that Array types have explicit coercions
124
200
 
125
- Unlike Virtus, dry-types does not perform any implict coercions. If you
126
- have any uses of `Array[String]`, `Array[Integer]`, etc. be sure they
127
- use a `coerce_with` block. For example:
201
+ 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:
128
202
 
129
203
  ```ruby
130
204
  requires :values, type: Array[String]
131
205
  ```
132
206
 
133
- It's quite common to pass a comma-separated list, such as `tag1,tag2` as
134
- `values`. Previously Virtus would implicitly coerce this to
135
- `Array(values)` so that `["tag1,tag2"]` would pass the type checks, but
136
- with `dry-types` the values are no longer coerced for you. To fix this,
137
- you might do:
207
+ 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:
138
208
 
139
209
  ```ruby
140
210
  requires :values, type: Array[String], coerce_with: ->(val) { val.split(',').map(&:strip) }
@@ -201,12 +271,9 @@ In order to make obtaining the name of a mounted class simpler, we've delegated
201
271
 
202
272
  ##### Patching the class
203
273
 
204
- In an effort to make APIs re-mountable, The class `Grape::API` no longer refers to an API instance,
205
- rather, what used to be `Grape::API` is now `Grape::API::Instance` and `Grape::API` was replaced
206
- with a class that can contain several instances of `Grape::API`.
274
+ 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`.
207
275
 
208
- This changes were done in such a way that no code-changes should be required.
209
- 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`.
276
+ 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`.
210
277
 
211
278
  Note, this is particularly relevant if you are opening the class `Grape::API` for modification.
212
279
 
@@ -229,15 +296,20 @@ end
229
296
 
230
297
  After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
231
298
  which inherit from `Grape::API::Instance`.
299
+
232
300
  What this means in practice, is:
301
+
233
302
  - Generally: you can access the named class from the instance calling the getter `base`.
234
- - In particular: If you need the `name`, you can use `base`.`name`
303
+ - In particular: If you need the `name`, you can use `base`.`name`.
235
304
 
236
305
  **Deprecated**
306
+
237
307
  ```ruby
238
308
  payload[:endpoint].options[:for].name
239
309
  ```
310
+
240
311
  **New**
312
+
241
313
  ```ruby
242
314
  payload[:endpoint].options[:for].base.name
243
315
  ```
@@ -328,8 +400,7 @@ See [#1610](https://github.com/ruby-grape/grape/pull/1610) for more information.
328
400
 
329
401
  #### The `except`, `except_message`, and `proc` options of the `values` validator are deprecated.
330
402
 
331
- The new `except_values` validator should be used in place of the `except` and `except_message` options of
332
- the `values` validator.
403
+ The new `except_values` validator should be used in place of the `except` and `except_message` options of the `values` validator.
333
404
 
334
405
  Arity one Procs may now be used directly as the `values` option to explicitly test param values.
335
406
 
@@ -405,9 +476,7 @@ get '/example' #=> before: 405, after: 404
405
476
 
406
477
  #### Removed param processing from built-in OPTIONS handler
407
478
 
408
- When a request is made to the built-in `OPTIONS` handler, only the `before` and `after`
409
- callbacks associated with the resource will be run. The `before_validation` and
410
- `after_validation` callbacks and parameter validations will be skipped.
479
+ 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.
411
480
 
412
481
  See [#1505](https://github.com/ruby-grape/grape/pull/1505) for more information.
413
482
 
@@ -428,8 +497,7 @@ See [#1510](https://github.com/ruby-grape/grape/pull/1510) for more information.
428
497
 
429
498
  #### The default status code for DELETE is now 204 instead of 200.
430
499
 
431
- Breaking change: Sets the default response status code for a delete request to 204.
432
- 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.
500
+ 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.
433
501
 
434
502
  To achieve the old behavior, one has to set it explicitly:
435
503
  ```ruby
@@ -607,18 +675,14 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
607
675
 
608
676
  #### Bypasses formatters when status code indicates no content
609
677
 
610
- To be consistent with rack and it's handling of standard responses
611
- associated with no content, both default and custom formatters will now
678
+ To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now
612
679
  be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
613
680
 
614
681
  See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
615
682
 
616
683
  #### Redirects respond as plain text with message
617
684
 
618
- `#redirect` now uses `text/plain` regardless of whether that format has
619
- been enabled. This prevents formatters from attempting to serialize the
620
- message body and allows for a descriptive message body to be provided - and
621
- optionally overridden - that better fulfills the theme of the HTTP spec.
685
+ `#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.
622
686
 
623
687
  See [#1194](https://github.com/ruby-grape/grape/pull/1194) for more information.
624
688
 
@@ -652,10 +716,7 @@ end
652
716
 
653
717
  See [#1029](https://github.com/ruby-grape/grape/pull/1029) for more information.
654
718
 
655
- There is a known issue because of this change. When Grape is used with an older
656
- than 1.2.4 version of [warden](https://github.com/hassox/warden) there may be raised
657
- the following exception having the [rack-mount](https://github.com/jm/rack-mount) gem's
658
- lines as last ones in the backtrace:
719
+ 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:
659
720
 
660
721
  ```
661
722
  NoMethodError: undefined method `[]' for nil:NilClass
@@ -206,12 +206,12 @@ module Grape
206
206
  end
207
207
  end
208
208
 
209
- module ServeFile
209
+ module ServeStream
210
210
  extend ::ActiveSupport::Autoload
211
211
  eager_autoload do
212
- autoload :FileResponse
213
212
  autoload :FileBody
214
213
  autoload :SendfileResponse
214
+ autoload :StreamResponse
215
215
  end
216
216
  end
217
217
  end
@@ -192,37 +192,15 @@ module Grape
192
192
  # will return an HTTP 405 response for any HTTP method that the resource
193
193
  # cannot handle.
194
194
  def add_head_not_allowed_methods_and_options_methods
195
- routes_map = {}
196
-
197
- self.class.endpoints.each do |endpoint|
198
- routes = endpoint.routes
199
- routes.each do |route|
200
- # using the :any shorthand produces [nil] for route methods, substitute all manually
201
- route_key = route.pattern.to_regexp
202
- routes_map[route_key] ||= {}
203
- route_settings = routes_map[route_key]
204
- route_settings[:pattern] = route.pattern
205
- route_settings[:requirements] = route.requirements
206
- route_settings[:path] = route.origin
207
- route_settings[:methods] ||= []
208
- if route.request_method == '*' || route_settings[:methods].include?('*')
209
- route_settings[:methods] = Grape::Http::Headers::SUPPORTED_METHODS
210
- else
211
- route_settings[:methods] << route.request_method
212
- end
213
- route_settings[:endpoint] = route.app
214
- end
215
- end
216
-
195
+ versioned_route_configs = collect_route_config_per_pattern
217
196
  # The paths we collected are prepared (cf. Path#prepare), so they
218
197
  # contain already versioning information when using path versioning.
219
198
  # Disable versioning so adding a route won't prepend versioning
220
199
  # informations again.
221
200
  without_root_prefix do
222
201
  without_versioning do
223
- routes_map.each_value do |config|
224
- methods = config[:methods]
225
- allowed_methods = methods.dup
202
+ versioned_route_configs.each do |config|
203
+ allowed_methods = config[:methods].dup
226
204
 
227
205
  unless self.class.namespace_inheritable(:do_not_route_head)
228
206
  allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
@@ -241,6 +219,25 @@ module Grape
241
219
  end
242
220
  end
243
221
 
222
+ def collect_route_config_per_pattern
223
+ all_routes = self.class.endpoints.map(&:routes).flatten
224
+ routes_by_regexp = all_routes.group_by { |route| route.pattern.to_regexp }
225
+
226
+ # Build the configuration based on the first endpoint and the collection of methods supported.
227
+ routes_by_regexp.values.map do |routes|
228
+ last_route = routes.last # Most of the configuration is taken from the last endpoint
229
+ matching_wildchar = routes.any? { |route| route.request_method == '*' }
230
+ {
231
+ options: {},
232
+ pattern: last_route.pattern,
233
+ requirements: last_route.requirements,
234
+ path: last_route.origin,
235
+ endpoint: last_route.app,
236
+ methods: matching_wildchar ? Grape::Http::Headers::SUPPORTED_METHODS : routes.map(&:request_method)
237
+ }
238
+ end
239
+ end
240
+
244
241
  # Generate a route that returns an HTTP 405 response for a user defined
245
242
  # path on methods not specified
246
243
  def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
@@ -252,7 +249,6 @@ module Grape
252
249
  end
253
250
  not_allowed_methods = supported_methods - allowed_methods
254
251
  return if not_allowed_methods.empty?
255
-
256
252
  @router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
257
253
  end
258
254