grape 2.0.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +151 -1
- data/CONTRIBUTING.md +1 -1
- data/README.md +404 -334
- data/UPGRADING.md +279 -7
- data/grape.gemspec +8 -8
- data/lib/grape/api/instance.rb +34 -66
- data/lib/grape/api.rb +47 -70
- data/lib/grape/content_types.rb +13 -10
- data/lib/grape/cookies.rb +31 -24
- data/lib/grape/dry_types.rb +0 -2
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/desc.rb +49 -44
- data/lib/grape/dsl/headers.rb +2 -2
- data/lib/grape/dsl/helpers.rb +8 -4
- data/lib/grape/dsl/inside_route.rb +67 -54
- data/lib/grape/dsl/parameters.rb +10 -9
- data/lib/grape/dsl/request_response.rb +14 -18
- data/lib/grape/dsl/routing.rb +34 -17
- data/lib/grape/dsl/validations.rb +13 -0
- data/lib/grape/endpoint.rb +120 -118
- data/lib/grape/{util/env.rb → env.rb} +0 -5
- data/lib/grape/error_formatter/base.rb +51 -21
- data/lib/grape/error_formatter/json.rb +7 -15
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +11 -17
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +5 -25
- data/lib/grape/exceptions/base.rb +18 -30
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/validation.rb +5 -6
- data/lib/grape/exceptions/validation_array_errors.rb +1 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -6
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
- data/lib/grape/extensions/hash.rb +7 -2
- data/lib/grape/extensions/hashie/mash.rb +3 -5
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +4 -6
- data/lib/grape/formatter/serializable_hash.rb +1 -1
- data/lib/grape/formatter/txt.rb +3 -5
- data/lib/grape/formatter/xml.rb +4 -6
- data/lib/grape/formatter.rb +7 -25
- data/lib/grape/{util/json.rb → json.rb} +1 -3
- data/lib/grape/locale/en.yml +46 -42
- data/lib/grape/middleware/auth/base.rb +11 -34
- data/lib/grape/middleware/auth/dsl.rb +23 -31
- data/lib/grape/middleware/base.rb +41 -23
- data/lib/grape/middleware/error.rb +77 -76
- data/lib/grape/middleware/formatter.rb +48 -79
- data/lib/grape/middleware/globals.rb +1 -3
- data/lib/grape/middleware/stack.rb +26 -37
- data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
- data/lib/grape/middleware/versioner/base.rb +74 -0
- data/lib/grape/middleware/versioner/header.rb +59 -126
- data/lib/grape/middleware/versioner/param.rb +4 -25
- data/lib/grape/middleware/versioner/path.rb +10 -34
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/namespace.rb +4 -5
- data/lib/grape/params_builder/base.rb +18 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -23
- data/lib/grape/path.rb +38 -60
- data/lib/grape/request.rb +161 -30
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +45 -31
- data/lib/grape/router/route.rb +28 -57
- data/lib/grape/router.rb +56 -43
- data/lib/grape/util/base_inheritable.rb +4 -4
- data/lib/grape/util/cache.rb +0 -3
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_values.rb +0 -2
- data/lib/grape/util/lazy/block.rb +29 -0
- data/lib/grape/util/lazy/value.rb +38 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +34 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +70 -0
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/util/reverse_stackable_values.rb +1 -6
- data/lib/grape/util/stackable_values.rb +1 -6
- data/lib/grape/util/strict_hash_configuration.rb +3 -3
- data/lib/grape/validations/attributes_doc.rb +38 -36
- data/lib/grape/validations/attributes_iterator.rb +1 -0
- data/lib/grape/validations/contract_scope.rb +34 -0
- data/lib/grape/validations/params_scope.rb +36 -32
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +9 -15
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +0 -2
- data/lib/grape/validations/types/set_coercer.rb +0 -3
- data/lib/grape/validations/types.rb +0 -3
- data/lib/grape/validations/validator_factory.rb +2 -2
- data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
- data/lib/grape/validations/validators/base.rb +8 -11
- data/lib/grape/validations/validators/coerce_validator.rb +1 -1
- data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
- data/lib/grape/validations/validators/default_validator.rb +6 -2
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +1 -1
- data/lib/grape/validations/validators/except_values_validator.rb +2 -2
- data/lib/grape/validations/validators/length_validator.rb +49 -0
- data/lib/grape/validations/validators/presence_validator.rb +1 -1
- data/lib/grape/validations/validators/regexp_validator.rb +2 -2
- data/lib/grape/validations/validators/values_validator.rb +20 -57
- data/lib/grape/validations.rb +8 -21
- data/lib/grape/version.rb +1 -1
- data/lib/grape/{util/xml.rb → xml.rb} +1 -1
- data/lib/grape.rb +42 -274
- metadata +45 -44
- data/lib/grape/eager_load.rb +0 -20
- data/lib/grape/http/headers.rb +0 -71
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
- data/lib/grape/router/attribute_translator.rb +0 -63
- data/lib/grape/util/lazy_block.rb +0 -27
- data/lib/grape/util/lazy_object.rb +0 -43
- data/lib/grape/util/lazy_value.rb +0 -91
- data/lib/grape/util/registrable.rb +0 -15
- data/lib/grape/validations/types/build_coercer.rb +0 -94
data/UPGRADING.md
CHANGED
@@ -1,6 +1,281 @@
|
|
1
1
|
Upgrading Grape
|
2
2
|
===============
|
3
3
|
|
4
|
+
### Upgrading to >= 2.4.0
|
5
|
+
|
6
|
+
#### Grape::Middleware::Auth::Base
|
7
|
+
`type` is now validated at compile time and will raise a `Grape::Exceptions::UnknownAuthStrategy` if unknown.
|
8
|
+
|
9
|
+
#### Grape::Middleware::Base
|
10
|
+
|
11
|
+
- Second argument `options` is now a double splat (**) instead of single splat (*). If you're redefining `initialize` in your middleware and/or calling `super` in it, you might have to adapt the signature and the `super` call. Also, you might have to remove `{}` if you're pass `options` as a literal `Hash` or add `**` if you're using a variable.
|
12
|
+
- `Grape::Middleware::Helpers` has been removed. The equivalent method `context` is now part of `Grape::Middleware::Base`.
|
13
|
+
|
14
|
+
#### Grape::Http::Headers, Grape::Util::Lazy::Object
|
15
|
+
|
16
|
+
Both have been removed. See [2554](https://github.com/ruby-grape/grape/pull/2554).
|
17
|
+
Here are the notable changes:
|
18
|
+
|
19
|
+
- Constants like `HTTP_ACCEPT` have been replaced by their literal value.
|
20
|
+
- `SUPPORTED_METHODS` has been moved to `Grape` module.
|
21
|
+
- `HTTP_HEADERS` has been moved to `Grape::Request` and renamed `KNOWN_HEADERS`. The last has been refreshed with new headers, and it's not lazy anymore.
|
22
|
+
- `SUPPORTED_METHODS_WITHOUT_OPTIONS` and `find_supported_method` have been removed.
|
23
|
+
|
24
|
+
#### Grape::Middleware::Base
|
25
|
+
|
26
|
+
- Constant `TEXT_HTML` has been removed in favor of using literal string 'text/html'.
|
27
|
+
- `rack_request` and `query_params` have been added. Feel free to call these in your middlewares.
|
28
|
+
|
29
|
+
#### Params Builder
|
30
|
+
|
31
|
+
- Passing a class to `build_with` or `Grape.config.param_builder` has been deprecated in favor of a symbolized short_name. See `SHORTNAME_LOOKUP` in [params_builder](lib/grape/params_builder.rb).
|
32
|
+
- Including Grape's extensions like `Grape::Extensions::Hashie::Mash::ParamBuilder` has been deprecated in favor of using `build_with` at the route level.
|
33
|
+
|
34
|
+
#### Accept Header Negotiation Harmonized
|
35
|
+
|
36
|
+
[Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept) header is now fully interpreted through `Rack::Utils.best_q_match` which is following [RFC2616 14.1](https://datatracker.ietf.org/doc/html/rfc2616#section-14.1). Since [Grape 2.1.0](https://github.com/ruby-grape/grape/blob/master/CHANGELOG.md#210-20240615), the [header versioning strategy](https://github.com/ruby-grape/grape?tab=readme-ov-file#header) was adhering to it, but `Grape::Middleware::Formatter` never did.
|
37
|
+
|
38
|
+
Your API might act differently since it will strictly follow the [RFC2616 14.1](https://datatracker.ietf.org/doc/html/rfc2616#section-14.1) when interpreting the `Accept` header. Here are the differences:
|
39
|
+
|
40
|
+
##### Invalid or missing quality ranking
|
41
|
+
The following used to yield `application/xml` and now will yield `application/json` as the preferred media type:
|
42
|
+
- `application/json;q=invalid,application/xml;q=0.5`
|
43
|
+
- `application/json,application/xml;q=1.0`
|
44
|
+
|
45
|
+
For the invalid case, the value `invalid` was automatically `to_f` and `invalid.to_f` equals `0.0`. Now, since it doesn't match [Rack's regex](https://github.com/rack/rack/blob/3-1-stable/lib/rack/utils.rb#L138), its interpreted as non provided and its quality ranking equals 1.0.
|
46
|
+
|
47
|
+
For the non provided case, 1.0 was automatically assigned and in a case of multiple best matches, the first was returned based on Ruby's sort_by `quality`. Now, 1.0 is still assigned and the last is returned in case of multiple best matches. See [Rack's implementation](https://github.com/rack/rack/blob/e8f47608668d507e0f231a932fa37c9ca551c0a5/lib/rack/utils.rb#L167) of the RFC.
|
48
|
+
|
49
|
+
##### Considering the closest generic when vendor tree
|
50
|
+
Excluding the [header versioning strategy](https://github.com/ruby-grape/grape?tab=readme-ov-file#header), whenever a media type with the [vendor tree](https://datatracker.ietf.org/doc/html/rfc6838#section-3.2) leading facet `vnd.` like `application/vnd.api+json` was provided, Grape would also consider its closest generic when negotiating. In that case, `application/json` was added to the negotiation. Now, it will just consider the provided media types without considering any closest generics, and you'll need to [register](https://github.com/ruby-grape/grape?tab=readme-ov-file#api-formats) it.
|
51
|
+
You can find the official vendor tree registrations on [IANA](https://www.iana.org/assignments/media-types/media-types.xhtml)
|
52
|
+
|
53
|
+
#### Custom Validators
|
54
|
+
|
55
|
+
If you now receive an error of `'Grape::Validations.require_validator': unknown validator: your_custom_validation (Grape::Exceptions::UnknownValidator)` after upgrading to 2.4.0 then you will need to ensure that you require the `your_custom_validation` file before your Grape API code is loaded.
|
56
|
+
|
57
|
+
See [2533](https://github.com/ruby-grape/grape/issues/2533) for more information.
|
58
|
+
|
59
|
+
### Upgrading to >= 2.3.0
|
60
|
+
|
61
|
+
### `content_type` vs `api.format` inside API
|
62
|
+
|
63
|
+
Before 2.3.0, `content_type` had priority over `env['api.format']` when set in an API, which was incorrect. The priority has been flipped and `env['api.format']` will be checked first.
|
64
|
+
In addition, the function `api_format` has been added. Instead of setting `env['api.format']` directly, you can call `api_format`.
|
65
|
+
See [#2506](https://github.com/ruby-grape/grape/pull/2506) for more information.
|
66
|
+
|
67
|
+
#### Remove Deprecated Methods and Options
|
68
|
+
|
69
|
+
- Deprecated `file` method has been removed. Use `send_file` or `stream`.
|
70
|
+
See [#2500](https://github.com/ruby-grape/grape/pull/2500) for more information.
|
71
|
+
|
72
|
+
- The `except` and `proc` options have been removed from the `values` validator. Use `except_values` validator or assign `proc` directly to `values`.
|
73
|
+
See [#2501](https://github.com/ruby-grape/grape/pull/2501) for more information.
|
74
|
+
|
75
|
+
- `Passing an options hash and a block to 'desc'` deprecation has been removed. Move all hash options to block instead.
|
76
|
+
See [#2502](https://github.com/ruby-grape/grape/pull/2502) for more information.
|
77
|
+
|
78
|
+
### Upgrading to >= 2.2.0
|
79
|
+
|
80
|
+
### `Length` validator
|
81
|
+
|
82
|
+
After Grape 2.2.0, `length` validator will only take effect for parameters with types that support `#length` method, will not throw `ArgumentError` exception.
|
83
|
+
|
84
|
+
See [#2464](https://github.com/ruby-grape/grape/pull/2464) for more information.
|
85
|
+
|
86
|
+
### Upgrading to >= 2.1.0
|
87
|
+
|
88
|
+
#### Optional Builder
|
89
|
+
|
90
|
+
The `builder` gem dependency has been made optional as it's only used when generating XML. If your code does, add `builder` to your `Gemfile`.
|
91
|
+
|
92
|
+
See [#2445](https://github.com/ruby-grape/grape/pull/2445) for more information.
|
93
|
+
|
94
|
+
#### Deep Merging of Parameter Attributes
|
95
|
+
|
96
|
+
Grape now uses `deep_merge` to combine parameter attributes within the `with` method. Previously, attributes defined at the parameter level would override those defined at the group level.
|
97
|
+
With deep merge, attributes are now combined, allowing for more detailed and nuanced API specifications.
|
98
|
+
|
99
|
+
For example:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
with(documentation: { in: 'body' }) do
|
103
|
+
optional :vault, documentation: { default: 33 }
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
Before it was equivalent to:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
optional :vault, documentation: { default: 33 }
|
111
|
+
```
|
112
|
+
|
113
|
+
After it is an equivalent of:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
optional :vault, documentation: { in: 'body', default: 33 }
|
117
|
+
```
|
118
|
+
|
119
|
+
See [#2432](https://github.com/ruby-grape/grape/pull/2432) for more information.
|
120
|
+
|
121
|
+
#### Zeitwerk
|
122
|
+
|
123
|
+
Grape's autoloader has been updated and it's now based on [Zeitwerk](https://github.com/fxn/zeitwerk).
|
124
|
+
If you MP (Monkey Patch) some files and you're not following the [file structure](https://github.com/fxn/zeitwerk?tab=readme-ov-file#file-structure), you might end up with a Zeitwerk error.
|
125
|
+
|
126
|
+
See [#2363](https://github.com/ruby-grape/grape/pull/2363) for more information.
|
127
|
+
|
128
|
+
#### Changes in rescue_from
|
129
|
+
|
130
|
+
The `rack_response` method has been deprecated and the `error_response` method has been removed. Use `error!` instead.
|
131
|
+
|
132
|
+
See [#2414](https://github.com/ruby-grape/grape/pull/2414) for more information.
|
133
|
+
|
134
|
+
#### Change in parameters precedence
|
135
|
+
|
136
|
+
When using together with `Grape::Extensions::Hash::ParamBuilder`, `route_param` takes higher precedence over a regular parameter defined with same name, which now matches the default param builder behavior.
|
137
|
+
|
138
|
+
This was a regression introduced by [#2326](https://github.com/ruby-grape/grape/pull/2326) in Grape v1.8.0.
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
Grape.configure do |config|
|
142
|
+
config.param_builder = Grape::Extensions::Hash::ParamBuilder
|
143
|
+
end
|
144
|
+
|
145
|
+
params do
|
146
|
+
requires :foo, type: String
|
147
|
+
end
|
148
|
+
route_param :foo do
|
149
|
+
get do
|
150
|
+
{ value: params[:foo] }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
Request:
|
156
|
+
|
157
|
+
```bash
|
158
|
+
curl -X POST -H "Content-Type: application/json" localhost:9292/bar -d '{"foo": "baz"}'
|
159
|
+
```
|
160
|
+
|
161
|
+
Response prior to v1.8.0:
|
162
|
+
|
163
|
+
```json
|
164
|
+
{
|
165
|
+
"value": "bar"
|
166
|
+
}
|
167
|
+
```
|
168
|
+
|
169
|
+
v1.8.0..v2.0.0:
|
170
|
+
|
171
|
+
```json
|
172
|
+
{
|
173
|
+
"value": "baz"
|
174
|
+
}
|
175
|
+
```
|
176
|
+
|
177
|
+
v2.1.0+:
|
178
|
+
|
179
|
+
```json
|
180
|
+
{
|
181
|
+
"value": "bar"
|
182
|
+
}
|
183
|
+
```
|
184
|
+
|
185
|
+
See [#2378](https://github.com/ruby-grape/grape/pull/2378) for details.
|
186
|
+
|
187
|
+
#### Grape::Router::Route.route_xxx methods have been removed
|
188
|
+
|
189
|
+
- `route_method` is accessible through `request_method`
|
190
|
+
- `route_path` is accessible through `path`
|
191
|
+
- Any other `route_xyz` are accessible through `options[xyz]`
|
192
|
+
|
193
|
+
#### Instance variables scope
|
194
|
+
|
195
|
+
Due to the changes done in [#2377](https://github.com/ruby-grape/grape/pull/2377), the instance variables defined inside each of the endpoints (or inside a `before` validator) are now accessible inside the `rescue_from`. The behavior of the instance variables was undefined until `2.1.0`.
|
196
|
+
|
197
|
+
If you were using the same variable name defined inside an endpoint or `before` validator inside a `rescue_from` handler, you need to take in mind that you can start getting different values or you can be overriding values.
|
198
|
+
|
199
|
+
Before:
|
200
|
+
```ruby
|
201
|
+
class TwitterAPI < Grape::API
|
202
|
+
before do
|
203
|
+
@var = 1
|
204
|
+
end
|
205
|
+
|
206
|
+
get '/' do
|
207
|
+
puts @var # => 1
|
208
|
+
raise
|
209
|
+
end
|
210
|
+
|
211
|
+
rescue_from :all do
|
212
|
+
puts @var # => nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
```
|
216
|
+
|
217
|
+
After:
|
218
|
+
```ruby
|
219
|
+
class TwitterAPI < Grape::API
|
220
|
+
before do
|
221
|
+
@var = 1
|
222
|
+
end
|
223
|
+
|
224
|
+
get '/' do
|
225
|
+
puts @var # => 1
|
226
|
+
raise
|
227
|
+
end
|
228
|
+
|
229
|
+
rescue_from :all do
|
230
|
+
puts @var # => 1
|
231
|
+
end
|
232
|
+
end
|
233
|
+
```
|
234
|
+
|
235
|
+
#### Recognizing Path
|
236
|
+
|
237
|
+
Grape now considers the types of the configured `route_params` in order to determine the endpoint that matches with the performed request.
|
238
|
+
|
239
|
+
So taking into account this `Grape::API` class
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
class Books < Grape::API
|
243
|
+
resource :books do
|
244
|
+
route_param :id, type: Integer do
|
245
|
+
# GET /books/:id
|
246
|
+
get do
|
247
|
+
#...
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
resource :share do
|
252
|
+
# POST /books/share
|
253
|
+
post do
|
254
|
+
# ....
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
```
|
260
|
+
|
261
|
+
Before:
|
262
|
+
```ruby
|
263
|
+
API.recognize_path '/books/1' # => /books/:id
|
264
|
+
API.recognize_path '/books/share' # => /books/:id
|
265
|
+
API.recognize_path '/books/other' # => /books/:id
|
266
|
+
```
|
267
|
+
|
268
|
+
After:
|
269
|
+
```ruby
|
270
|
+
API.recognize_path '/books/1' # => /books/:id
|
271
|
+
API.recognize_path '/books/share' # => /books/share
|
272
|
+
API.recognize_path '/books/other' # => nil
|
273
|
+
```
|
274
|
+
|
275
|
+
This implies that before this changes, when you performed `/books/other` and it matched with the `/books/:id` endpoint, you get a `400 Bad Request` response because the type of the provided `:id` param was not an `Integer`. However, after upgrading to version `2.1.0` you will get a `404 Not Found` response, because there is not a defined endpoint that matches with `/books/other`.
|
276
|
+
|
277
|
+
See [#2379](https://github.com/ruby-grape/grape/pull/2379) for more information.
|
278
|
+
|
4
279
|
### Upgrading to >= 2.0.0
|
5
280
|
|
6
281
|
#### Headers
|
@@ -19,7 +294,7 @@ If you are using Rack 3 in your application then the headers will be set to:
|
|
19
294
|
{ "content-type" => "application/json", "secret-password" => "foo"}
|
20
295
|
```
|
21
296
|
|
22
|
-
This means if you are checking for header values in your application, you would need to change your code to use downcased keys.
|
297
|
+
This means if you are checking for header values in your application, you would need to change your code to use downcased keys.
|
23
298
|
|
24
299
|
```ruby
|
25
300
|
get do
|
@@ -474,8 +749,7 @@ end
|
|
474
749
|
|
475
750
|
##### `name` (and other caveats) of the mounted API
|
476
751
|
|
477
|
-
After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
|
478
|
-
which inherit from `Grape::API::Instance`.
|
752
|
+
After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class which inherit from `Grape::API::Instance`.
|
479
753
|
|
480
754
|
What this means in practice, is:
|
481
755
|
|
@@ -855,8 +1129,7 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
|
|
855
1129
|
|
856
1130
|
#### Bypasses formatters when status code indicates no content
|
857
1131
|
|
858
|
-
To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now
|
859
|
-
be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
|
1132
|
+
To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
|
860
1133
|
|
861
1134
|
See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
|
862
1135
|
|
@@ -1297,8 +1570,7 @@ As replacement can be used
|
|
1297
1570
|
* `Grape::Middleware::Auth::Digest` => [`Rack::Auth::Digest::MD5`](https://github.com/rack/rack/blob/master/lib/rack/auth/digest/md5.rb)
|
1298
1571
|
* `Grape::Middleware::Auth::OAuth2` => [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2)
|
1299
1572
|
|
1300
|
-
If this is not possible you can extract the middleware files from [grape v0.7.0](https://github.com/ruby-grape/grape/tree/v0.7.0/lib/grape/middleware/auth)
|
1301
|
-
and host these files within your application
|
1573
|
+
If this is not possible you can extract the middleware files from [grape v0.7.0](https://github.com/ruby-grape/grape/tree/v0.7.0/lib/grape/middleware/auth) and host these files within your application
|
1302
1574
|
|
1303
1575
|
See [#703](https://github.com/ruby-grape/Grape/pull/703) for more information.
|
1304
1576
|
|
data/grape.gemspec
CHANGED
@@ -17,17 +17,17 @@ Gem::Specification.new do |s|
|
|
17
17
|
'bug_tracker_uri' => 'https://github.com/ruby-grape/grape/issues',
|
18
18
|
'changelog_uri' => "https://github.com/ruby-grape/grape/blob/v#{s.version}/CHANGELOG.md",
|
19
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}"
|
20
|
+
'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}",
|
21
|
+
'rubygems_mfa_required' => 'true'
|
21
22
|
}
|
22
23
|
|
23
|
-
s.
|
24
|
-
s.
|
25
|
-
s.
|
26
|
-
s.
|
27
|
-
s.
|
28
|
-
s.add_runtime_dependency 'rack-accept'
|
24
|
+
s.add_dependency 'activesupport', '>= 6.1'
|
25
|
+
s.add_dependency 'dry-types', '>= 1.1'
|
26
|
+
s.add_dependency 'mustermann-grape', '~> 1.1.0'
|
27
|
+
s.add_dependency 'rack', '>= 2'
|
28
|
+
s.add_dependency 'zeitwerk'
|
29
29
|
|
30
30
|
s.files = Dir['lib/**/*', 'CHANGELOG.md', 'CONTRIBUTING.md', 'README.md', 'grape.png', 'UPGRADING.md', 'LICENSE', 'grape.gemspec']
|
31
31
|
s.require_paths = ['lib']
|
32
|
-
s.required_ruby_version = '>= 2.
|
32
|
+
s.required_ruby_version = '>= 2.7.0'
|
33
33
|
end
|
data/lib/grape/api/instance.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'grape/router'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
class API
|
7
5
|
# The API Instance class, is the engine behind Grape::API. Each class that inherits
|
8
6
|
# from this will represent a different API instance
|
9
7
|
class Instance
|
8
|
+
extend Grape::Middleware::Auth::DSL
|
10
9
|
include Grape::DSL::API
|
11
10
|
|
12
11
|
class << self
|
@@ -48,7 +47,7 @@ module Grape
|
|
48
47
|
# Parses the API's definition and compiles it into an instance of
|
49
48
|
# Grape::API.
|
50
49
|
def compile
|
51
|
-
@instance ||= new
|
50
|
+
@instance ||= new # rubocop:disable Naming/MemoizedInstanceVariableName
|
52
51
|
end
|
53
52
|
|
54
53
|
# Wipe the compiled API so we can recompile after changes were made.
|
@@ -112,9 +111,9 @@ module Grape
|
|
112
111
|
end
|
113
112
|
|
114
113
|
def evaluate_as_instance_with_configuration(block, lazy: false)
|
115
|
-
lazy_block = Grape::Util::
|
114
|
+
lazy_block = Grape::Util::Lazy::Block.new do |configuration|
|
116
115
|
value_for_configuration = configuration
|
117
|
-
self.configuration = value_for_configuration.evaluate if value_for_configuration.
|
116
|
+
self.configuration = value_for_configuration.evaluate if value_for_configuration.try(:lazy?)
|
118
117
|
response = instance_eval(&block)
|
119
118
|
self.configuration = value_for_configuration
|
120
119
|
response
|
@@ -127,6 +126,7 @@ module Grape
|
|
127
126
|
end
|
128
127
|
|
129
128
|
def inherited(subclass)
|
129
|
+
super
|
130
130
|
subclass.reset!
|
131
131
|
subclass.logger = logger.clone
|
132
132
|
end
|
@@ -162,9 +162,13 @@ module Grape
|
|
162
162
|
|
163
163
|
# Handle a request. See Rack documentation for what `env` is.
|
164
164
|
def call(env)
|
165
|
-
|
166
|
-
|
167
|
-
|
165
|
+
status, headers, response = @router.call(env)
|
166
|
+
unless cascade?
|
167
|
+
headers = Grape::Util::Header.new.merge(headers)
|
168
|
+
headers.delete('X-Cascade')
|
169
|
+
end
|
170
|
+
|
171
|
+
[status, headers, response]
|
168
172
|
end
|
169
173
|
|
170
174
|
# Some requests may return a HTTP 404 error if grape cannot find a matching
|
@@ -191,88 +195,52 @@ module Grape
|
|
191
195
|
# will return an HTTP 405 response for any HTTP method that the resource
|
192
196
|
# cannot handle.
|
193
197
|
def add_head_not_allowed_methods_and_options_methods
|
194
|
-
versioned_route_configs = collect_route_config_per_pattern
|
195
198
|
# The paths we collected are prepared (cf. Path#prepare), so they
|
196
199
|
# contain already versioning information when using path versioning.
|
200
|
+
all_routes = self.class.endpoints.map(&:routes).flatten
|
201
|
+
|
197
202
|
# Disable versioning so adding a route won't prepend versioning
|
198
203
|
# informations again.
|
199
|
-
|
200
|
-
|
201
|
-
versioned_route_configs.each do |config|
|
202
|
-
next if config[:options][:matching_wildchar]
|
203
|
-
|
204
|
-
allowed_methods = config[:methods].dup
|
205
|
-
|
206
|
-
allowed_methods |= [Grape::Http::Headers::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Grape::Http::Headers::GET)
|
204
|
+
without_root_prefix_and_versioning { collect_route_config_per_pattern(all_routes) }
|
205
|
+
end
|
207
206
|
|
208
|
-
|
207
|
+
def collect_route_config_per_pattern(all_routes)
|
208
|
+
routes_by_regexp = all_routes.group_by(&:pattern_regexp)
|
209
209
|
|
210
|
-
|
210
|
+
# Build the configuration based on the first endpoint and the collection of methods supported.
|
211
|
+
routes_by_regexp.each_value do |routes|
|
212
|
+
last_route = routes.last # Most of the configuration is taken from the last endpoint
|
213
|
+
next if routes.any? { |route| route.request_method == '*' }
|
211
214
|
|
212
|
-
|
213
|
-
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
215
|
+
allowed_methods = routes.map(&:request_method)
|
216
|
+
allowed_methods |= [Rack::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Rack::GET)
|
218
217
|
|
219
|
-
|
220
|
-
|
221
|
-
routes_by_regexp = all_routes.group_by { |route| route.pattern.to_regexp }
|
218
|
+
allow_header = self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Rack::OPTIONS] | allowed_methods
|
219
|
+
last_route.app.options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Rack::OPTIONS)
|
222
220
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
{
|
228
|
-
options: { matching_wildchar: matching_wildchar },
|
229
|
-
pattern: last_route.pattern,
|
230
|
-
requirements: last_route.requirements,
|
231
|
-
path: last_route.origin,
|
232
|
-
endpoint: last_route.app,
|
233
|
-
methods: matching_wildchar ? Grape::Http::Headers::SUPPORTED_METHODS : routes.map(&:request_method)
|
234
|
-
}
|
221
|
+
@router.associate_routes(last_route.pattern, {
|
222
|
+
endpoint: last_route.app,
|
223
|
+
allow_header: allow_header
|
224
|
+
})
|
235
225
|
end
|
236
226
|
end
|
237
227
|
|
238
|
-
# Generate a route that returns an HTTP 405 response for a user defined
|
239
|
-
# path on methods not specified
|
240
|
-
def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
|
241
|
-
supported_methods =
|
242
|
-
if self.class.namespace_inheritable(:do_not_route_options)
|
243
|
-
Grape::Http::Headers::SUPPORTED_METHODS
|
244
|
-
else
|
245
|
-
Grape::Http::Headers::SUPPORTED_METHODS_WITHOUT_OPTIONS
|
246
|
-
end
|
247
|
-
not_allowed_methods = supported_methods - allowed_methods
|
248
|
-
@router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
|
249
|
-
end
|
250
|
-
|
251
228
|
# Allows definition of endpoints that ignore the versioning configuration
|
252
229
|
# used by the rest of your API.
|
253
|
-
def
|
230
|
+
def without_root_prefix_and_versioning
|
254
231
|
old_version = self.class.namespace_inheritable(:version)
|
255
232
|
old_version_options = self.class.namespace_inheritable(:version_options)
|
233
|
+
old_root_prefix = self.class.namespace_inheritable(:root_prefix)
|
256
234
|
|
257
235
|
self.class.namespace_inheritable_to_nil(:version)
|
258
236
|
self.class.namespace_inheritable_to_nil(:version_options)
|
237
|
+
self.class.namespace_inheritable_to_nil(:root_prefix)
|
259
238
|
|
260
239
|
yield
|
261
240
|
|
262
241
|
self.class.namespace_inheritable(:version, old_version)
|
263
242
|
self.class.namespace_inheritable(:version_options, old_version_options)
|
264
|
-
|
265
|
-
|
266
|
-
# Allows definition of endpoints that ignore the root prefix used by the
|
267
|
-
# rest of your API.
|
268
|
-
def without_root_prefix(&_block)
|
269
|
-
old_prefix = self.class.namespace_inheritable(:root_prefix)
|
270
|
-
|
271
|
-
self.class.namespace_inheritable_to_nil(:root_prefix)
|
272
|
-
|
273
|
-
yield
|
274
|
-
|
275
|
-
self.class.namespace_inheritable(:root_prefix, old_prefix)
|
243
|
+
self.class.namespace_inheritable(:root_prefix, old_root_prefix)
|
276
244
|
end
|
277
245
|
end
|
278
246
|
end
|