grape 2.0.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +96 -1
- data/README.md +364 -317
- data/UPGRADING.md +205 -7
- data/grape.gemspec +7 -7
- data/lib/grape/api/instance.rb +14 -11
- data/lib/grape/api.rb +19 -10
- data/lib/grape/content_types.rb +13 -10
- data/lib/grape/cookies.rb +2 -1
- data/lib/grape/dry_types.rb +0 -2
- data/lib/grape/dsl/desc.rb +22 -20
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/helpers.rb +7 -3
- data/lib/grape/dsl/inside_route.rb +51 -15
- data/lib/grape/dsl/parameters.rb +5 -4
- data/lib/grape/dsl/request_response.rb +14 -18
- data/lib/grape/dsl/routing.rb +20 -4
- data/lib/grape/dsl/validations.rb +13 -0
- data/lib/grape/endpoint.rb +43 -35
- data/lib/grape/{util/env.rb → env.rb} +0 -5
- data/lib/grape/error_formatter/json.rb +13 -4
- data/lib/grape/error_formatter/txt.rb +11 -10
- data/lib/grape/error_formatter.rb +13 -25
- data/lib/grape/exceptions/base.rb +3 -3
- data/lib/grape/exceptions/validation.rb +0 -2
- data/lib/grape/exceptions/validation_array_errors.rb +1 -0
- data/lib/grape/exceptions/validation_errors.rb +2 -4
- data/lib/grape/extensions/hash.rb +5 -1
- data/lib/grape/formatter.rb +15 -25
- data/lib/grape/http/headers.rb +18 -34
- data/lib/grape/{util/json.rb → json.rb} +1 -3
- data/lib/grape/locale/en.yml +4 -0
- data/lib/grape/middleware/auth/base.rb +0 -2
- data/lib/grape/middleware/auth/dsl.rb +0 -2
- data/lib/grape/middleware/base.rb +14 -15
- data/lib/grape/middleware/error.rb +61 -54
- data/lib/grape/middleware/formatter.rb +18 -15
- data/lib/grape/middleware/globals.rb +1 -3
- data/lib/grape/middleware/stack.rb +4 -5
- data/lib/grape/middleware/versioner/accept_version_header.rb +8 -33
- data/lib/grape/middleware/versioner/header.rb +62 -123
- data/lib/grape/middleware/versioner/param.rb +5 -23
- data/lib/grape/middleware/versioner/path.rb +11 -33
- data/lib/grape/middleware/versioner.rb +5 -14
- data/lib/grape/middleware/versioner_helpers.rb +75 -0
- data/lib/grape/namespace.rb +3 -4
- data/lib/grape/parser.rb +8 -24
- data/lib/grape/path.rb +24 -29
- data/lib/grape/request.rb +4 -12
- data/lib/grape/router/base_route.rb +39 -0
- data/lib/grape/router/greedy_route.rb +20 -0
- data/lib/grape/router/pattern.rb +39 -30
- data/lib/grape/router/route.rb +22 -59
- data/lib/grape/router.rb +32 -37
- 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/object.rb +45 -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/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 +71 -0
- data/lib/grape/validations/params_scope.rb +22 -19
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/build_coercer.rb +69 -71
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -11
- 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/validators/base.rb +1 -0
- data/lib/grape/validations/validators/default_validator.rb +5 -1
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +1 -1
- data/lib/grape/validations/validators/length_validator.rb +49 -0
- data/lib/grape/validations/validators/values_validator.rb +6 -1
- data/lib/grape/validations.rb +3 -7
- data/lib/grape/version.rb +1 -1
- data/lib/grape/{util/xml.rb → xml.rb} +1 -1
- data/lib/grape.rb +30 -274
- metadata +31 -38
- data/lib/grape/eager_load.rb +0 -20
- 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/UPGRADING.md
CHANGED
@@ -1,6 +1,207 @@
|
|
1
1
|
Upgrading Grape
|
2
2
|
===============
|
3
3
|
|
4
|
+
### Upgrading to >= 2.2.0
|
5
|
+
|
6
|
+
### `Length` validator
|
7
|
+
|
8
|
+
After Grape 2.2.0, `length` validator will only take effect for parameters with types that support `#length` method, will not throw `ArgumentError` exception.
|
9
|
+
|
10
|
+
See [#2464](https://github.com/ruby-grape/grape/pull/2464) for more information.
|
11
|
+
|
12
|
+
### Upgrading to >= 2.1.0
|
13
|
+
|
14
|
+
#### Optional Builder
|
15
|
+
|
16
|
+
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`.
|
17
|
+
|
18
|
+
See [#2445](https://github.com/ruby-grape/grape/pull/2445) for more information.
|
19
|
+
|
20
|
+
#### Deep Merging of Parameter Attributes
|
21
|
+
|
22
|
+
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.
|
23
|
+
With deep merge, attributes are now combined, allowing for more detailed and nuanced API specifications.
|
24
|
+
|
25
|
+
For example:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
with(documentation: { in: 'body' }) do
|
29
|
+
optional :vault, documentation: { default: 33 }
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
Before it was equivalent to:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
optional :vault, documentation: { default: 33 }
|
37
|
+
```
|
38
|
+
|
39
|
+
After it is an equivalent of:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
optional :vault, documentation: { in: 'body', default: 33 }
|
43
|
+
```
|
44
|
+
|
45
|
+
See [#2432](https://github.com/ruby-grape/grape/pull/2432) for more information.
|
46
|
+
|
47
|
+
#### Zeitwerk
|
48
|
+
|
49
|
+
Grape's autoloader has been updated and it's now based on [Zeitwerk](https://github.com/fxn/zeitwerk).
|
50
|
+
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.
|
51
|
+
|
52
|
+
See [#2363](https://github.com/ruby-grape/grape/pull/2363) for more information.
|
53
|
+
|
54
|
+
#### Changes in rescue_from
|
55
|
+
|
56
|
+
The `rack_response` method has been deprecated and the `error_response` method has been removed. Use `error!` instead.
|
57
|
+
|
58
|
+
See [#2414](https://github.com/ruby-grape/grape/pull/2414) for more information.
|
59
|
+
|
60
|
+
#### Change in parameters precedence
|
61
|
+
|
62
|
+
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.
|
63
|
+
|
64
|
+
This was a regression introduced by [#2326](https://github.com/ruby-grape/grape/pull/2326) in Grape v1.8.0.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
grape.configure do |config|
|
68
|
+
config.param_builder = Grape::Extensions::Hash::ParamBuilder
|
69
|
+
end
|
70
|
+
|
71
|
+
params do
|
72
|
+
requires :foo, type: String
|
73
|
+
end
|
74
|
+
route_param :foo do
|
75
|
+
get do
|
76
|
+
{ value: params[:foo] }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
Request:
|
82
|
+
|
83
|
+
```bash
|
84
|
+
curl -X POST -H "Content-Type: application/json" localhost:9292/bar -d '{"foo": "baz"}'
|
85
|
+
```
|
86
|
+
|
87
|
+
Response prior to v1.8.0:
|
88
|
+
|
89
|
+
```json
|
90
|
+
{
|
91
|
+
"value": "bar"
|
92
|
+
}
|
93
|
+
```
|
94
|
+
|
95
|
+
v1.8.0..v2.0.0:
|
96
|
+
|
97
|
+
```json
|
98
|
+
{
|
99
|
+
"value": "baz"
|
100
|
+
}
|
101
|
+
```
|
102
|
+
|
103
|
+
v2.1.0+:
|
104
|
+
|
105
|
+
```json
|
106
|
+
{
|
107
|
+
"value": "bar"
|
108
|
+
}
|
109
|
+
```
|
110
|
+
|
111
|
+
See [#2378](https://github.com/ruby-grape/grape/pull/2378) for details.
|
112
|
+
|
113
|
+
#### Grape::Router::Route.route_xxx methods have been removed
|
114
|
+
|
115
|
+
- `route_method` is accessible through `request_method`
|
116
|
+
- `route_path` is accessible through `path`
|
117
|
+
- Any other `route_xyz` are accessible through `options[xyz]`
|
118
|
+
|
119
|
+
#### Instance variables scope
|
120
|
+
|
121
|
+
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`.
|
122
|
+
|
123
|
+
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.
|
124
|
+
|
125
|
+
Before:
|
126
|
+
```ruby
|
127
|
+
class TwitterAPI < Grape::API
|
128
|
+
before do
|
129
|
+
@var = 1
|
130
|
+
end
|
131
|
+
|
132
|
+
get '/' do
|
133
|
+
puts @var # => 1
|
134
|
+
raise
|
135
|
+
end
|
136
|
+
|
137
|
+
rescue_from :all do
|
138
|
+
puts @var # => nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
After:
|
144
|
+
```ruby
|
145
|
+
class TwitterAPI < Grape::API
|
146
|
+
before do
|
147
|
+
@var = 1
|
148
|
+
end
|
149
|
+
|
150
|
+
get '/' do
|
151
|
+
puts @var # => 1
|
152
|
+
raise
|
153
|
+
end
|
154
|
+
|
155
|
+
rescue_from :all do
|
156
|
+
puts @var # => 1
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
#### Recognizing Path
|
162
|
+
|
163
|
+
Grape now considers the types of the configured `route_params` in order to determine the endpoint that matches with the performed request.
|
164
|
+
|
165
|
+
So taking into account this `Grape::API` class
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
class Books < Grape::API
|
169
|
+
resource :books do
|
170
|
+
route_param :id, type: Integer do
|
171
|
+
# GET /books/:id
|
172
|
+
get do
|
173
|
+
#...
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
resource :share do
|
178
|
+
# POST /books/share
|
179
|
+
post do
|
180
|
+
# ....
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
Before:
|
188
|
+
```ruby
|
189
|
+
API.recognize_path '/books/1' # => /books/:id
|
190
|
+
API.recognize_path '/books/share' # => /books/:id
|
191
|
+
API.recognize_path '/books/other' # => /books/:id
|
192
|
+
```
|
193
|
+
|
194
|
+
After:
|
195
|
+
```ruby
|
196
|
+
API.recognize_path '/books/1' # => /books/:id
|
197
|
+
API.recognize_path '/books/share' # => /books/share
|
198
|
+
API.recognize_path '/books/other' # => nil
|
199
|
+
```
|
200
|
+
|
201
|
+
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`.
|
202
|
+
|
203
|
+
See [#2379](https://github.com/ruby-grape/grape/pull/2379) for more information.
|
204
|
+
|
4
205
|
### Upgrading to >= 2.0.0
|
5
206
|
|
6
207
|
#### Headers
|
@@ -19,7 +220,7 @@ If you are using Rack 3 in your application then the headers will be set to:
|
|
19
220
|
{ "content-type" => "application/json", "secret-password" => "foo"}
|
20
221
|
```
|
21
222
|
|
22
|
-
This means if you are checking for header values in your application, you would need to change your code to use downcased keys.
|
223
|
+
This means if you are checking for header values in your application, you would need to change your code to use downcased keys.
|
23
224
|
|
24
225
|
```ruby
|
25
226
|
get do
|
@@ -474,8 +675,7 @@ end
|
|
474
675
|
|
475
676
|
##### `name` (and other caveats) of the mounted API
|
476
677
|
|
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`.
|
678
|
+
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
679
|
|
480
680
|
What this means in practice, is:
|
481
681
|
|
@@ -855,8 +1055,7 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
|
|
855
1055
|
|
856
1056
|
#### Bypasses formatters when status code indicates no content
|
857
1057
|
|
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)
|
1058
|
+
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
1059
|
|
861
1060
|
See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
|
862
1061
|
|
@@ -1297,8 +1496,7 @@ As replacement can be used
|
|
1297
1496
|
* `Grape::Middleware::Auth::Digest` => [`Rack::Auth::Digest::MD5`](https://github.com/rack/rack/blob/master/lib/rack/auth/digest/md5.rb)
|
1298
1497
|
* `Grape::Middleware::Auth::OAuth2` => [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2)
|
1299
1498
|
|
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
|
1499
|
+
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
1500
|
|
1303
1501
|
See [#703](https://github.com/ruby-grape/Grape/pull/703) for more information.
|
1304
1502
|
|
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.add_runtime_dependency 'activesupport', '>=
|
24
|
-
s.add_runtime_dependency 'builder'
|
24
|
+
s.add_runtime_dependency 'activesupport', '>= 6'
|
25
25
|
s.add_runtime_dependency 'dry-types', '>= 1.1'
|
26
|
-
s.add_runtime_dependency 'mustermann-grape', '~> 1.
|
27
|
-
s.add_runtime_dependency 'rack', '>=
|
28
|
-
s.add_runtime_dependency '
|
26
|
+
s.add_runtime_dependency 'mustermann-grape', '~> 1.1.0'
|
27
|
+
s.add_runtime_dependency 'rack', '>= 2'
|
28
|
+
s.add_runtime_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,7 +1,5 @@
|
|
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
|
@@ -48,7 +46,7 @@ module Grape
|
|
48
46
|
# Parses the API's definition and compiles it into an instance of
|
49
47
|
# Grape::API.
|
50
48
|
def compile
|
51
|
-
@instance ||= new
|
49
|
+
@instance ||= new # rubocop:disable Naming/MemoizedInstanceVariableName
|
52
50
|
end
|
53
51
|
|
54
52
|
# Wipe the compiled API so we can recompile after changes were made.
|
@@ -112,7 +110,7 @@ module Grape
|
|
112
110
|
end
|
113
111
|
|
114
112
|
def evaluate_as_instance_with_configuration(block, lazy: false)
|
115
|
-
lazy_block = Grape::Util::
|
113
|
+
lazy_block = Grape::Util::Lazy::Block.new do |configuration|
|
116
114
|
value_for_configuration = configuration
|
117
115
|
self.configuration = value_for_configuration.evaluate if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
|
118
116
|
response = instance_eval(&block)
|
@@ -127,6 +125,7 @@ module Grape
|
|
127
125
|
end
|
128
126
|
|
129
127
|
def inherited(subclass)
|
128
|
+
super
|
130
129
|
subclass.reset!
|
131
130
|
subclass.logger = logger.clone
|
132
131
|
end
|
@@ -162,9 +161,13 @@ module Grape
|
|
162
161
|
|
163
162
|
# Handle a request. See Rack documentation for what `env` is.
|
164
163
|
def call(env)
|
165
|
-
|
166
|
-
|
167
|
-
|
164
|
+
status, headers, response = @router.call(env)
|
165
|
+
unless cascade?
|
166
|
+
headers = Grape::Util::Header.new.merge(headers)
|
167
|
+
headers.delete(Grape::Http::Headers::X_CASCADE)
|
168
|
+
end
|
169
|
+
|
170
|
+
[status, headers, response]
|
168
171
|
end
|
169
172
|
|
170
173
|
# Some requests may return a HTTP 404 error if grape cannot find a matching
|
@@ -203,11 +206,11 @@ module Grape
|
|
203
206
|
|
204
207
|
allowed_methods = config[:methods].dup
|
205
208
|
|
206
|
-
allowed_methods |= [
|
209
|
+
allowed_methods |= [Rack::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Rack::GET)
|
207
210
|
|
208
|
-
allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [
|
211
|
+
allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Rack::OPTIONS] | allowed_methods)
|
209
212
|
|
210
|
-
config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(
|
213
|
+
config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Rack::OPTIONS)
|
211
214
|
|
212
215
|
attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
|
213
216
|
generate_not_allowed_method(config[:pattern], **attributes)
|
@@ -218,7 +221,7 @@ module Grape
|
|
218
221
|
|
219
222
|
def collect_route_config_per_pattern
|
220
223
|
all_routes = self.class.endpoints.map(&:routes).flatten
|
221
|
-
routes_by_regexp = all_routes.group_by
|
224
|
+
routes_by_regexp = all_routes.group_by(&:pattern_regexp)
|
222
225
|
|
223
226
|
# Build the configuration based on the first endpoint and the collection of methods supported.
|
224
227
|
routes_by_regexp.values.map do |routes|
|
data/lib/grape/api.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'grape/router'
|
4
|
-
require 'grape/api/instance'
|
5
|
-
|
6
3
|
module Grape
|
7
4
|
# The API class is the primary entry point for creating Grape APIs. Users
|
8
5
|
# should subclass this class in order to build an API.
|
@@ -26,8 +23,8 @@ module Grape
|
|
26
23
|
attr_accessor :base_instance, :instances
|
27
24
|
|
28
25
|
# Rather than initializing an object of type Grape::API, create an object of type Instance
|
29
|
-
def new(
|
30
|
-
base_instance.new(
|
26
|
+
def new(...)
|
27
|
+
base_instance.new(...)
|
31
28
|
end
|
32
29
|
|
33
30
|
# When inherited, will create a list of all instances (times the API was mounted)
|
@@ -35,7 +32,7 @@ module Grape
|
|
35
32
|
def inherited(api)
|
36
33
|
super
|
37
34
|
|
38
|
-
api.initial_setup(Grape::API
|
35
|
+
api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance)
|
39
36
|
api.override_all_methods!
|
40
37
|
end
|
41
38
|
|
@@ -77,8 +74,8 @@ module Grape
|
|
77
74
|
# the headers, and the body. See [the rack specification]
|
78
75
|
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
|
79
76
|
# NOTE: This will only be called on an API directly mounted on RACK
|
80
|
-
def call(
|
81
|
-
instance_for_rack.call(
|
77
|
+
def call(...)
|
78
|
+
instance_for_rack.call(...)
|
82
79
|
end
|
83
80
|
|
84
81
|
# Alleviates problems with autoloading by tring to search for the constant
|
@@ -111,7 +108,7 @@ module Grape
|
|
111
108
|
end
|
112
109
|
|
113
110
|
def respond_to?(method, include_private = false)
|
114
|
-
super
|
111
|
+
super || base_instance.respond_to?(method, include_private)
|
115
112
|
end
|
116
113
|
|
117
114
|
def respond_to_missing?(method, include_private = false)
|
@@ -128,7 +125,6 @@ module Grape
|
|
128
125
|
end
|
129
126
|
|
130
127
|
def compile!
|
131
|
-
require 'grape/eager_load'
|
132
128
|
instance_for_rack.compile! # See API::Instance.compile!
|
133
129
|
end
|
134
130
|
|
@@ -150,6 +146,19 @@ module Grape
|
|
150
146
|
@instances.each do |instance|
|
151
147
|
last_response = replay_step_on(instance, setup_step)
|
152
148
|
end
|
149
|
+
|
150
|
+
# Updating all previously mounted classes in the case that new methods have been executed.
|
151
|
+
if method != :mount && @setup.any?
|
152
|
+
previous_mount_steps = @setup.select { |step| step[:method] == :mount }
|
153
|
+
previous_mount_steps.each do |mount_step|
|
154
|
+
refresh_mount_step = mount_step.merge(method: :refresh_mounted_api)
|
155
|
+
@setup += [refresh_mount_step]
|
156
|
+
@instances.each do |instance|
|
157
|
+
replay_step_on(instance, refresh_mount_step)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
153
162
|
last_response
|
154
163
|
end
|
155
164
|
|
data/lib/grape/content_types.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'grape/util/registrable'
|
4
|
-
|
5
3
|
module Grape
|
6
4
|
module ContentTypes
|
7
|
-
|
5
|
+
module_function
|
8
6
|
|
9
7
|
# Content types are listed in order of preference.
|
10
|
-
|
8
|
+
DEFAULTS = {
|
11
9
|
xml: 'application/xml',
|
12
10
|
serializable_hash: 'application/json',
|
13
11
|
json: 'application/json',
|
@@ -15,13 +13,18 @@ module Grape
|
|
15
13
|
txt: 'text/plain'
|
16
14
|
}.freeze
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
MIME_TYPES = Grape::ContentTypes::DEFAULTS.except(:serializable_hash).invert.freeze
|
17
|
+
|
18
|
+
def content_types_for(from_settings)
|
19
|
+
from_settings.presence || DEFAULTS
|
20
|
+
end
|
21
|
+
|
22
|
+
def mime_types_for(from_settings)
|
23
|
+
return MIME_TYPES if from_settings == Grape::ContentTypes::DEFAULTS
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
from_settings.each_with_object({}) do |(k, v), types_without_params|
|
26
|
+
# remove optional parameter
|
27
|
+
types_without_params[v.split(';', 2).first] = k
|
25
28
|
end
|
26
29
|
end
|
27
30
|
end
|
data/lib/grape/cookies.rb
CHANGED
@@ -33,9 +33,10 @@ module Grape
|
|
33
33
|
@cookies.each(&block)
|
34
34
|
end
|
35
35
|
|
36
|
+
# see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
|
36
37
|
# rubocop:disable Layout/SpaceBeforeBrackets
|
37
38
|
def delete(name, **opts)
|
38
|
-
options = opts.merge(value: '
|
39
|
+
options = opts.merge(max_age: '0', value: '', expires: Time.at(0))
|
39
40
|
self.[]=(name, options)
|
40
41
|
end
|
41
42
|
# rubocop:enable Layout/SpaceBeforeBrackets
|
data/lib/grape/dry_types.rb
CHANGED
data/lib/grape/dsl/desc.rb
CHANGED
@@ -5,6 +5,27 @@ module Grape
|
|
5
5
|
module Desc
|
6
6
|
include Grape::DSL::Settings
|
7
7
|
|
8
|
+
ROUTE_ATTRIBUTES = %i[
|
9
|
+
body_name
|
10
|
+
consumes
|
11
|
+
default
|
12
|
+
deprecated
|
13
|
+
description
|
14
|
+
detail
|
15
|
+
entity
|
16
|
+
headers
|
17
|
+
hidden
|
18
|
+
http_codes
|
19
|
+
is_array
|
20
|
+
named
|
21
|
+
nickname
|
22
|
+
params
|
23
|
+
produces
|
24
|
+
security
|
25
|
+
summary
|
26
|
+
tags
|
27
|
+
].freeze
|
28
|
+
|
8
29
|
# Add a description to the next namespace or function.
|
9
30
|
# @param description [String] descriptive string for this endpoint
|
10
31
|
# or namespace
|
@@ -81,26 +102,7 @@ module Grape
|
|
81
102
|
# Returns an object which configures itself via an instance-context DSL.
|
82
103
|
def desc_container(endpoint_configuration)
|
83
104
|
Module.new do
|
84
|
-
include Grape::Util::StrictHashConfiguration.module(
|
85
|
-
:summary,
|
86
|
-
:description,
|
87
|
-
:detail,
|
88
|
-
:params,
|
89
|
-
:entity,
|
90
|
-
:http_codes,
|
91
|
-
:named,
|
92
|
-
:body_name,
|
93
|
-
:headers,
|
94
|
-
:hidden,
|
95
|
-
:deprecated,
|
96
|
-
:is_array,
|
97
|
-
:nickname,
|
98
|
-
:produces,
|
99
|
-
:consumes,
|
100
|
-
:security,
|
101
|
-
:tags,
|
102
|
-
:default
|
103
|
-
)
|
105
|
+
include Grape::Util::StrictHashConfiguration.module(*ROUTE_ATTRIBUTES)
|
104
106
|
config_context.define_singleton_method(:configuration) do
|
105
107
|
endpoint_configuration
|
106
108
|
end
|
data/lib/grape/dsl/headers.rb
CHANGED
data/lib/grape/dsl/helpers.rb
CHANGED
@@ -33,18 +33,22 @@ module Grape
|
|
33
33
|
# end
|
34
34
|
#
|
35
35
|
def helpers(*new_modules, &block)
|
36
|
-
include_new_modules(new_modules)
|
37
|
-
include_block(block)
|
36
|
+
include_new_modules(new_modules)
|
37
|
+
include_block(block)
|
38
38
|
include_all_in_scope if !block && new_modules.empty?
|
39
39
|
end
|
40
40
|
|
41
41
|
protected
|
42
42
|
|
43
43
|
def include_new_modules(modules)
|
44
|
+
return if modules.empty?
|
45
|
+
|
44
46
|
modules.each { |mod| make_inclusion(mod) }
|
45
47
|
end
|
46
48
|
|
47
49
|
def include_block(block)
|
50
|
+
return unless block
|
51
|
+
|
48
52
|
Module.new.tap do |mod|
|
49
53
|
make_inclusion(mod) { mod.class_eval(&block) }
|
50
54
|
end
|
@@ -58,7 +62,7 @@ module Grape
|
|
58
62
|
|
59
63
|
def include_all_in_scope
|
60
64
|
Module.new.tap do |mod|
|
61
|
-
namespace_stackable(:helpers).each { |mod_to_include| mod.
|
65
|
+
namespace_stackable(:helpers).each { |mod_to_include| mod.include mod_to_include }
|
62
66
|
change!
|
63
67
|
end
|
64
68
|
end
|