grape 1.3.1 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/LICENSE +1 -1
- data/README.md +19 -6
- data/UPGRADING.md +120 -16
- data/lib/grape/api/instance.rb +12 -7
- data/lib/grape/dsl/inside_route.rb +37 -14
- data/lib/grape/http/headers.rb +1 -0
- data/lib/grape/middleware/versioner/header.rb +1 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/path.rb +2 -2
- data/lib/grape/router/attribute_translator.rb +23 -2
- data/lib/grape/router/route.rb +3 -22
- data/lib/grape/router.rb +6 -14
- data/lib/grape/util/base_inheritable.rb +9 -6
- data/lib/grape/util/reverse_stackable_values.rb +3 -1
- data/lib/grape/util/stackable_values.rb +3 -1
- data/lib/grape/validations/types/array_coercer.rb +14 -5
- data/lib/grape/validations/types/build_coercer.rb +5 -8
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
- data/lib/grape/validations/types/dry_type_coercer.rb +36 -1
- data/lib/grape/validations/types/file.rb +15 -13
- data/lib/grape/validations/types/json.rb +40 -36
- data/lib/grape/validations/types/primitive_coercer.rb +11 -4
- data/lib/grape/validations/types/set_coercer.rb +6 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +1 -1
- data/lib/grape/validations/types.rb +6 -5
- data/lib/grape/validations/validators/coerce.rb +3 -10
- data/lib/grape/validations/validators/default.rb +0 -1
- data/lib/grape/validations/validators/regexp.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/instance_spec.rb +50 -0
- data/spec/grape/endpoint_spec.rb +18 -5
- data/spec/grape/path_spec.rb +4 -4
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +5 -1
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +1 -1
- data/spec/grape/validations/validators/coerce_spec.rb +207 -29
- data/spec/grape/validations/validators/default_spec.rb +121 -0
- data/spec/grape/validations/validators/values_spec.rb +1 -1
- data/spec/grape/validations_spec.rb +5 -5
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d63fb79e412ead32064ad4994e171e65962131a04afb20d12957942c744f4df8
|
4
|
+
data.tar.gz: 9a9f4fb654e346eabb8a0b902d5e49ae54dc567797789c98c76f2a2fe2e2daa9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b75afa355e6a2b8200d72a2c4407bc78646648832a94d576bff06bdebde90d54b1897df09a560665bd9f58e735f9832a110b63bbcaabe14a4e2ee3c97e743b57
|
7
|
+
data.tar.gz: 18a4ea057230ae0e6cfe16f92e5043339b66026a94d29bd8c897d2482577cb279ec4e202f41a643605a3ea09c748fa159fddc36977a3c4828b5b64daef080272
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
### 1.3.3 (2020/05/23)
|
2
|
+
|
3
|
+
#### Features
|
4
|
+
|
5
|
+
* [#2048](https://github.com/ruby-grape/grape/issues/2034): Grape Enterprise support is now available [via TideLift](https://tidelift.com/subscription/request-a-demo?utm_source=rubygems-grape&utm_medium=referral&utm_campaign=enterprise) - [@dblock](https://github.com/dblock).
|
6
|
+
* [#2039](https://github.com/ruby-grape/grape/pull/2039): Travis - update rails versions - [@ericproulx](https://github.com/ericproulx).
|
7
|
+
* [#2038](https://github.com/ruby-grape/grape/pull/2038): Travis - update ruby versions - [@ericproulx](https://github.com/ericproulx).
|
8
|
+
* [#2050](https://github.com/ruby-grape/grape/pull/2050): Refactor route public_send to AttributeTranslator - [@ericproulx](https://github.com/ericproulx).
|
9
|
+
|
10
|
+
#### Fixes
|
11
|
+
|
12
|
+
* [#2049](https://github.com/ruby-grape/grape/pull/2049): Coerce an empty string to nil in case of the bool type - [@dnesteryuk](https://github.com/dnesteryuk).
|
13
|
+
* [#2043](https://github.com/ruby-grape/grape/pull/2043): Modify declared for nested array and hash - [@kadotami](https://github.com/kadotami).
|
14
|
+
* [#2040](https://github.com/ruby-grape/grape/pull/2040): Fix a regression with Array of type nil - [@ericproulx](https://github.com/ericproulx).
|
15
|
+
* [#2054](https://github.com/ruby-grape/grape/pull/2054): Coercing of nested arrays - [@dnesteryuk](https://github.com/dnesteryuk).
|
16
|
+
* [#2050](https://github.com/ruby-grape/grape/pull/2053): Fix broken multiple mounts - [@Jack12816](https://github.com/Jack12816).
|
17
|
+
|
18
|
+
### 1.3.2 (2020/04/12)
|
19
|
+
|
20
|
+
#### Features
|
21
|
+
|
22
|
+
* [#2020](https://github.com/ruby-grape/grape/pull/2020): Reduce array allocation - [@ericproulx](https://github.com/ericproulx).
|
23
|
+
* [#2015](https://github.com/ruby-grape/grape/pull/2014): Reduce MatchData allocation - [@ericproulx](https://github.com/ericproulx).
|
24
|
+
* [#2014](https://github.com/ruby-grape/grape/pull/2014): Reduce total allocated arrays - [@ericproulx](https://github.com/ericproulx).
|
25
|
+
* [#2011](https://github.com/ruby-grape/grape/pull/2011): Reduce total retained regexes - [@ericproulx](https://github.com/ericproulx).
|
26
|
+
|
27
|
+
#### Fixes
|
28
|
+
|
29
|
+
* [#2033](https://github.com/ruby-grape/grape/pull/2033): Ensure `Float` params are correctly coerced to `BigDecimal` - [@tlconnor](https://github.com/tlconnor).
|
30
|
+
* [#2031](https://github.com/ruby-grape/grape/pull/2031): Fix a regression with an array of a custom type - [@dnesteryuk](https://github.com/dnesteryuk).
|
31
|
+
* [#2026](https://github.com/ruby-grape/grape/pull/2026): Fix a regression in `coerce_with` when coercion returns `nil` - [@misdoro](https://github.com/misdoro).
|
32
|
+
* [#2025](https://github.com/ruby-grape/grape/pull/2025): Fix Decimal type category - [@kdoya](https://github.com/kdoya).
|
33
|
+
* [#2019](https://github.com/ruby-grape/grape/pull/2019): Avoid coercing parameter with multiple types to an empty Array - [@stanhu](https://github.com/stanhu).
|
34
|
+
|
1
35
|
### 1.3.1 (2020/03/11)
|
2
36
|
|
3
37
|
#### Features
|
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2010-
|
1
|
+
Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
data/README.md
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
- [What is Grape?](#what-is-grape)
|
13
13
|
- [Stable Release](#stable-release)
|
14
14
|
- [Project Resources](#project-resources)
|
15
|
+
- [Grape for Enterprise](#grape-for-enterprise)
|
15
16
|
- [Installation](#installation)
|
16
17
|
- [Basic Usage](#basic-usage)
|
17
18
|
- [Mounting](#mounting)
|
@@ -141,6 +142,7 @@
|
|
141
142
|
- [format_response.grape](#format_responsegrape)
|
142
143
|
- [Monitoring Products](#monitoring-products)
|
143
144
|
- [Contributing to Grape](#contributing-to-grape)
|
145
|
+
- [Security](#security)
|
144
146
|
- [License](#license)
|
145
147
|
- [Copyright](#copyright)
|
146
148
|
|
@@ -154,7 +156,7 @@ content negotiation, versioning and much more.
|
|
154
156
|
|
155
157
|
## Stable Release
|
156
158
|
|
157
|
-
You're reading the documentation for the stable release of Grape, **1.3.
|
159
|
+
You're reading the documentation for the stable release of Grape, **1.3.3**.
|
158
160
|
|
159
161
|
## Project Resources
|
160
162
|
|
@@ -163,6 +165,14 @@ You're reading the documentation for the stable release of Grape, **1.3.1**.
|
|
163
165
|
* Need help? Try [Grape Google Group](http://groups.google.com/group/ruby-grape) or [Gitter](https://gitter.im/ruby-grape/grape)
|
164
166
|
* [Follow us on Twitter](https://twitter.com/grapeframework)
|
165
167
|
|
168
|
+
## Grape for Enterprise
|
169
|
+
|
170
|
+
Available as part of the Tidelift Subscription.
|
171
|
+
|
172
|
+
The maintainers of Grape are working with Tidelift to deliver commercial support and maintenance. Save time, reduce risk, and improve code health, while paying the maintainers of Grape. Click [here](https://tidelift.com/subscription/request-a-demo?utm_source=rubygems-grape&utm_medium=referral&utm_campaign=enterprise) for more details.
|
173
|
+
|
174
|
+
In 2020, we plan to use the money towards gathering Grape contributors for dinner in New York City.
|
175
|
+
|
166
176
|
## Installation
|
167
177
|
|
168
178
|
Ruby 2.4 or newer is required.
|
@@ -3187,14 +3197,13 @@ applies to the current namespace and any children, but not parents.
|
|
3187
3197
|
```ruby
|
3188
3198
|
http_basic do |username, password|
|
3189
3199
|
# verify user's password here
|
3190
|
-
|
3200
|
+
# IMPORTANT: make sure you use a comparison method which isn't prone to a timing attack
|
3191
3201
|
end
|
3192
3202
|
```
|
3193
3203
|
|
3194
3204
|
```ruby
|
3195
3205
|
http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
|
3196
3206
|
# lookup the user's password here
|
3197
|
-
{ 'user1' => 'password1' }[username]
|
3198
3207
|
end
|
3199
3208
|
```
|
3200
3209
|
|
@@ -3851,7 +3860,7 @@ Grape integrates with following third-party tools:
|
|
3851
3860
|
* **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
|
3852
3861
|
* **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
|
3853
3862
|
* **[AppSignal](https://www.appsignal.com)** - [appsignal-ruby](https://github.com/appsignal/appsignal-ruby) gem, [documentation](http://docs.appsignal.com/getting-started/supported-frameworks.html#grape)
|
3854
|
-
* **[ElasticAPM](https://www.elastic.co/products/apm) - [elastic-apm](https://github.com/elastic/apm-agent-ruby) gem, [documentation](https://www.elastic.co/guide/en/apm/agent/ruby/3.x/getting-started-rack.html#getting-started-grape)
|
3863
|
+
* **[ElasticAPM](https://www.elastic.co/products/apm)** - [elastic-apm](https://github.com/elastic/apm-agent-ruby) gem, [documentation](https://www.elastic.co/guide/en/apm/agent/ruby/3.x/getting-started-rack.html#getting-started-grape)
|
3855
3864
|
|
3856
3865
|
## Contributing to Grape
|
3857
3866
|
|
@@ -3860,10 +3869,14 @@ features and discuss issues.
|
|
3860
3869
|
|
3861
3870
|
See [CONTRIBUTING](CONTRIBUTING.md).
|
3862
3871
|
|
3872
|
+
## Security
|
3873
|
+
|
3874
|
+
See [SECURITY](SECURITY.md) for details.
|
3875
|
+
|
3863
3876
|
## License
|
3864
3877
|
|
3865
|
-
MIT License. See LICENSE for details.
|
3878
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
3866
3879
|
|
3867
3880
|
## Copyright
|
3868
3881
|
|
3869
|
-
Copyright (c) 2010-
|
3882
|
+
Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.
|
data/UPGRADING.md
CHANGED
@@ -1,6 +1,65 @@
|
|
1
1
|
Upgrading Grape
|
2
2
|
===============
|
3
3
|
|
4
|
+
### Upgrading to >= 1.4.0
|
5
|
+
|
6
|
+
#### Nil values for structures
|
7
|
+
|
8
|
+
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:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
class Api < Grape::API
|
17
|
+
params do
|
18
|
+
require :my_param, type: Array[Integer]
|
19
|
+
end
|
20
|
+
|
21
|
+
get 'example' do
|
22
|
+
params[:my_param]
|
23
|
+
end
|
24
|
+
get '/example', params: { my_param: nil }
|
25
|
+
# 1.3.1 = []
|
26
|
+
# 1.3.2 = nil
|
27
|
+
end
|
28
|
+
```
|
29
|
+
For now on, `nil` values stay `nil` values for all types, including arrays, sets and hashes.
|
30
|
+
|
31
|
+
If you want to have the same behavior as 1.3.1, apply a `default` validator
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class Api < Grape::API
|
35
|
+
params do
|
36
|
+
require :my_param, type: Array[Integer], default: []
|
37
|
+
end
|
38
|
+
|
39
|
+
get 'example' do
|
40
|
+
params[:my_param]
|
41
|
+
end
|
42
|
+
get '/example', params: { my_param: nil } # => []
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
#### Default validator
|
47
|
+
|
48
|
+
Default validator is now applied for `nil` values.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
class Api < Grape::API
|
52
|
+
params do
|
53
|
+
requires :my_param, type: Integer, default: 0
|
54
|
+
end
|
55
|
+
|
56
|
+
get 'example' do
|
57
|
+
params[:my_param]
|
58
|
+
end
|
59
|
+
get '/example', params: { my_param: nil } #=> before: nil, after: 0
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
4
63
|
### Upgrading to >= 1.3.0
|
5
64
|
|
6
65
|
#### Ruby
|
@@ -9,38 +68,83 @@ After adding dry-types, Ruby 2.4 or newer is required.
|
|
9
68
|
|
10
69
|
#### Coercion
|
11
70
|
|
12
|
-
[Virtus](https://github.com/solnic/virtus) has been replaced by
|
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`.
|
75
|
+
|
76
|
+
Here's an example of how to migrate a custom type from Virtus to dry-types:
|
13
77
|
|
14
78
|
```ruby
|
15
|
-
|
16
|
-
|
79
|
+
# Legacy Grape parser
|
80
|
+
class SecureUriType < Virtus::Attribute
|
81
|
+
def coerce(input)
|
82
|
+
URI.parse value
|
83
|
+
end
|
17
84
|
|
18
|
-
|
19
|
-
|
85
|
+
def value_coerced?(input)
|
86
|
+
value.is_a? String
|
87
|
+
end
|
20
88
|
end
|
21
89
|
|
22
|
-
# somewhere in your API
|
23
90
|
params do
|
24
|
-
requires :
|
91
|
+
requires :secure_uri, type: SecureUri
|
25
92
|
end
|
26
93
|
```
|
27
94
|
|
28
|
-
|
95
|
+
To use dry-types, we need to:
|
29
96
|
|
30
|
-
|
31
|
-
|
32
|
-
|
97
|
+
1. Remove the inheritance of `Virtus::Attribute`
|
98
|
+
1. Rename `coerce` to `self.parse`
|
99
|
+
1. Rename `value_coerced?` to `self.parsed?`
|
100
|
+
|
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:
|
33
105
|
|
34
|
-
|
35
|
-
|
106
|
+
```ruby
|
107
|
+
# New dry-types parser
|
108
|
+
class SecureUri
|
109
|
+
def self.parse(value)
|
110
|
+
URI.parse value
|
111
|
+
end
|
36
112
|
|
37
|
-
def self.
|
38
|
-
|
113
|
+
def self.parsed?(value)
|
114
|
+
value.is_a? URI::HTTPS
|
39
115
|
end
|
40
116
|
end
|
117
|
+
|
118
|
+
params do
|
119
|
+
requires :secure_uri, type: SecureUri
|
120
|
+
end
|
41
121
|
```
|
42
122
|
|
43
|
-
|
123
|
+
#### Ensure that Array types have explicit coercions
|
124
|
+
|
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:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
requires :values, type: Array[String]
|
131
|
+
```
|
132
|
+
|
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:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
requires :values, type: Array[String], coerce_with: ->(val) { val.split(',').map(&:strip) }
|
141
|
+
```
|
142
|
+
|
143
|
+
Likewise, for `Array[Integer]`, you might do:
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(',').map(&:strip).map(&:to_i) }
|
147
|
+
```
|
44
148
|
|
45
149
|
For more information see [#1920](https://github.com/ruby-grape/grape/pull/1920).
|
46
150
|
|
data/lib/grape/api/instance.rb
CHANGED
@@ -205,11 +205,12 @@ module Grape
|
|
205
205
|
route_settings[:requirements] = route.requirements
|
206
206
|
route_settings[:path] = route.origin
|
207
207
|
route_settings[:methods] ||= []
|
208
|
-
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
|
209
213
|
route_settings[:endpoint] = route.app
|
210
|
-
|
211
|
-
# using the :any shorthand produces [nil] for route methods, substitute all manually
|
212
|
-
route_settings[:methods] = Grape::Http::Headers::SUPPORTED_METHODS if route_settings[:methods].include?('*')
|
213
214
|
end
|
214
215
|
end
|
215
216
|
|
@@ -243,9 +244,13 @@ module Grape
|
|
243
244
|
# Generate a route that returns an HTTP 405 response for a user defined
|
244
245
|
# path on methods not specified
|
245
246
|
def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
|
246
|
-
|
247
|
-
|
248
|
-
|
247
|
+
supported_methods =
|
248
|
+
if self.class.namespace_inheritable(:do_not_route_options)
|
249
|
+
Grape::Http::Headers::SUPPORTED_METHODS
|
250
|
+
else
|
251
|
+
Grape::Http::Headers::SUPPORTED_METHODS_WITHOUT_OPTIONS
|
252
|
+
end
|
253
|
+
not_allowed_methods = supported_methods - allowed_methods
|
249
254
|
return if not_allowed_methods.empty?
|
250
255
|
|
251
256
|
@router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
|
@@ -28,36 +28,38 @@ module Grape
|
|
28
28
|
# Methods which should not be available in filters until the before filter
|
29
29
|
# has completed
|
30
30
|
module PostBeforeFilter
|
31
|
-
def declared(passed_params, options = {}, declared_params = nil)
|
31
|
+
def declared(passed_params, options = {}, declared_params = nil, params_nested_path = [])
|
32
32
|
options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
|
33
33
|
declared_params ||= optioned_declared_params(**options)
|
34
34
|
|
35
35
|
if passed_params.is_a?(Array)
|
36
|
-
declared_array(passed_params, options, declared_params)
|
36
|
+
declared_array(passed_params, options, declared_params, params_nested_path)
|
37
37
|
else
|
38
|
-
declared_hash(passed_params, options, declared_params)
|
38
|
+
declared_hash(passed_params, options, declared_params, params_nested_path)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
private
|
43
43
|
|
44
|
-
def declared_array(passed_params, options, declared_params)
|
44
|
+
def declared_array(passed_params, options, declared_params, params_nested_path)
|
45
45
|
passed_params.map do |passed_param|
|
46
|
-
declared(passed_param || {}, options, declared_params)
|
46
|
+
declared(passed_param || {}, options, declared_params, params_nested_path)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
def declared_hash(passed_params, options, declared_params)
|
50
|
+
def declared_hash(passed_params, options, declared_params, params_nested_path)
|
51
51
|
declared_params.each_with_object(passed_params.class.new) do |declared_param, memo|
|
52
52
|
if declared_param.is_a?(Hash)
|
53
53
|
declared_param.each_pair do |declared_parent_param, declared_children_params|
|
54
|
+
params_nested_path_dup = params_nested_path.dup
|
55
|
+
params_nested_path_dup << declared_parent_param.to_s
|
54
56
|
next unless options[:include_missing] || passed_params.key?(declared_parent_param)
|
55
57
|
|
56
58
|
passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
|
57
59
|
memo_key = optioned_param_key(declared_parent_param, options)
|
58
60
|
|
59
|
-
memo[memo_key] = handle_passed_param(
|
60
|
-
declared(passed_children_params, options, declared_children_params)
|
61
|
+
memo[memo_key] = handle_passed_param(passed_children_params, params_nested_path_dup) do
|
62
|
+
declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
|
61
63
|
end
|
62
64
|
end
|
63
65
|
else
|
@@ -77,19 +79,34 @@ module Grape
|
|
77
79
|
end
|
78
80
|
end
|
79
81
|
|
80
|
-
def handle_passed_param(
|
81
|
-
|
82
|
+
def handle_passed_param(passed_children_params, params_nested_path, &_block)
|
83
|
+
if should_be_empty_hash?(passed_children_params, params_nested_path)
|
84
|
+
{}
|
85
|
+
elsif should_be_empty_array?(passed_children_params, params_nested_path)
|
86
|
+
[]
|
87
|
+
else
|
88
|
+
yield
|
89
|
+
end
|
82
90
|
end
|
83
91
|
|
84
|
-
def should_be_empty_array?(
|
85
|
-
|
92
|
+
def should_be_empty_array?(passed_children_params, params_nested_path)
|
93
|
+
passed_children_params.empty? && declared_param_is_array?(params_nested_path)
|
86
94
|
end
|
87
95
|
|
88
|
-
def declared_param_is_array?(
|
89
|
-
key =
|
96
|
+
def declared_param_is_array?(params_nested_path)
|
97
|
+
key = route_options_params_key(params_nested_path)
|
90
98
|
route_options_params[key] && route_options_params[key][:type] == 'Array'
|
91
99
|
end
|
92
100
|
|
101
|
+
def should_be_empty_hash?(passed_children_params, params_nested_path)
|
102
|
+
passed_children_params.empty? && declared_param_is_hash?(params_nested_path)
|
103
|
+
end
|
104
|
+
|
105
|
+
def declared_param_is_hash?(params_nested_path)
|
106
|
+
key = route_options_params_key(params_nested_path)
|
107
|
+
route_options_params[key] && route_options_params[key][:type] == 'Hash'
|
108
|
+
end
|
109
|
+
|
93
110
|
def route_options_params
|
94
111
|
options[:route_options][:params] || {}
|
95
112
|
end
|
@@ -98,6 +115,12 @@ module Grape
|
|
98
115
|
options[:stringify] ? declared_param.to_s : declared_param.to_sym
|
99
116
|
end
|
100
117
|
|
118
|
+
def route_options_params_key(params_nested_path)
|
119
|
+
key = params_nested_path[0]
|
120
|
+
key += '[' + params_nested_path[1..-1].join('][') + ']' if params_nested_path.size > 1
|
121
|
+
key
|
122
|
+
end
|
123
|
+
|
101
124
|
def optioned_declared_params(**options)
|
102
125
|
declared_params = if options[:include_parent_namespaces]
|
103
126
|
# Declared params including parent namespaces
|
data/lib/grape/http/headers.rb
CHANGED
@@ -21,6 +21,7 @@ module Grape
|
|
21
21
|
OPTIONS = 'OPTIONS'
|
22
22
|
|
23
23
|
SUPPORTED_METHODS = [GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS].freeze
|
24
|
+
SUPPORTED_METHODS_WITHOUT_OPTIONS = Grape::Util::LazyObject.new { [GET, POST, PUT, PATCH, DELETE, HEAD].freeze }
|
24
25
|
|
25
26
|
HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION'
|
26
27
|
X_CASCADE = 'X-Cascade'
|
@@ -3,11 +3,12 @@
|
|
3
3
|
module Rack
|
4
4
|
module Accept
|
5
5
|
module Header
|
6
|
+
ALLOWED_CHARACTERS = %r{^([a-z*]+)\/([a-z0-9*\&\^\-_#\$!.+]+)(?:;([a-z0-9=;]+))?$}.freeze
|
6
7
|
class << self
|
7
8
|
# Corrected version of https://github.com/mjackson/rack-accept/blob/master/lib/rack/accept/header.rb#L40-L44
|
8
9
|
def parse_media_type(media_type)
|
9
10
|
# see http://tools.ietf.org/html/rfc6838#section-4.2 for allowed characters in media type names
|
10
|
-
m = media_type
|
11
|
+
m = media_type&.match(ALLOWED_CHARACTERS)
|
11
12
|
m ? [m[1], m[2], m[3] || ''] : []
|
12
13
|
end
|
13
14
|
end
|
data/lib/grape/path.rb
CHANGED
@@ -42,11 +42,11 @@ module Grape
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def namespace?
|
45
|
-
namespace
|
45
|
+
namespace&.match?(/^\S/) && namespace != '/'
|
46
46
|
end
|
47
47
|
|
48
48
|
def path?
|
49
|
-
raw_path
|
49
|
+
raw_path&.match?(/^\S/) && raw_path != '/'
|
50
50
|
end
|
51
51
|
|
52
52
|
def suffix
|
@@ -6,10 +6,31 @@ module Grape
|
|
6
6
|
class AttributeTranslator
|
7
7
|
attr_reader :attributes, :request_method, :requirements
|
8
8
|
|
9
|
+
ROUTE_ATTRIBUTES = %i[
|
10
|
+
prefix
|
11
|
+
version
|
12
|
+
settings
|
13
|
+
format
|
14
|
+
description
|
15
|
+
http_codes
|
16
|
+
headers
|
17
|
+
entity
|
18
|
+
details
|
19
|
+
requirements
|
20
|
+
request_method
|
21
|
+
namespace
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
ROUTER_ATTRIBUTES = %i[pattern index].freeze
|
25
|
+
|
9
26
|
def initialize(attributes = {})
|
10
27
|
@attributes = attributes
|
11
|
-
|
12
|
-
|
28
|
+
end
|
29
|
+
|
30
|
+
(ROUTER_ATTRIBUTES + ROUTE_ATTRIBUTES).each do |attr|
|
31
|
+
define_method attr do
|
32
|
+
attributes[attr]
|
33
|
+
end
|
13
34
|
end
|
14
35
|
|
15
36
|
def to_h
|
data/lib/grape/router/route.rb
CHANGED
@@ -12,12 +12,13 @@ module Grape
|
|
12
12
|
SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')?$/.freeze
|
13
13
|
FIXED_NAMED_CAPTURES = %w[format version].freeze
|
14
14
|
|
15
|
-
attr_accessor :pattern, :translator, :app, :index, :
|
15
|
+
attr_accessor :pattern, :translator, :app, :index, :options
|
16
16
|
|
17
17
|
alias attributes translator
|
18
18
|
|
19
19
|
extend Forwardable
|
20
20
|
def_delegators :pattern, :path, :origin
|
21
|
+
delegate Grape::Router::AttributeTranslator::ROUTE_ATTRIBUTES => :attributes
|
21
22
|
|
22
23
|
def method_missing(method_id, *arguments)
|
23
24
|
match = ROUTE_ATTRIBUTE_REGEXP.match(method_id.to_s)
|
@@ -31,26 +32,7 @@ module Grape
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def respond_to_missing?(method_id, _)
|
34
|
-
ROUTE_ATTRIBUTE_REGEXP.match(method_id.to_s)
|
35
|
-
end
|
36
|
-
|
37
|
-
%i[
|
38
|
-
prefix
|
39
|
-
version
|
40
|
-
settings
|
41
|
-
format
|
42
|
-
description
|
43
|
-
http_codes
|
44
|
-
headers
|
45
|
-
entity
|
46
|
-
details
|
47
|
-
requirements
|
48
|
-
request_method
|
49
|
-
namespace
|
50
|
-
].each do |method_name|
|
51
|
-
define_method method_name do
|
52
|
-
attributes.public_send method_name
|
53
|
-
end
|
35
|
+
ROUTE_ATTRIBUTE_REGEXP.match?(method_id.to_s)
|
54
36
|
end
|
55
37
|
|
56
38
|
def route_method
|
@@ -67,7 +49,6 @@ module Grape
|
|
67
49
|
method_s = method.to_s
|
68
50
|
method_upcase = Grape::Http::Headers.find_supported_method(method_s) || method_s.upcase
|
69
51
|
|
70
|
-
@suffix = options[:suffix]
|
71
52
|
@options = options.merge(method: method_upcase)
|
72
53
|
@pattern = Pattern.new(pattern, **options)
|
73
54
|
@translator = AttributeTranslator.new(**options, request_method: method_upcase)
|
data/lib/grape/router.rb
CHANGED
@@ -7,16 +7,6 @@ module Grape
|
|
7
7
|
class Router
|
8
8
|
attr_reader :map, :compiled
|
9
9
|
|
10
|
-
class Any < AttributeTranslator
|
11
|
-
attr_reader :pattern, :regexp, :index
|
12
|
-
def initialize(pattern, regexp, index, **attributes)
|
13
|
-
@pattern = pattern
|
14
|
-
@regexp = regexp
|
15
|
-
@index = index
|
16
|
-
super(attributes)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
10
|
class NormalizePathCache < Grape::Util::Cache
|
21
11
|
def initialize
|
22
12
|
@cache = Hash.new do |h, path|
|
@@ -39,18 +29,20 @@ module Grape
|
|
39
29
|
|
40
30
|
def initialize
|
41
31
|
@neutral_map = []
|
32
|
+
@neutral_regexes = []
|
42
33
|
@map = Hash.new { |hash, key| hash[key] = [] }
|
43
34
|
@optimized_map = Hash.new { |hash, key| hash[key] = // }
|
44
35
|
end
|
45
36
|
|
46
37
|
def compile!
|
47
38
|
return if compiled
|
48
|
-
@union = Regexp.union(@
|
39
|
+
@union = Regexp.union(@neutral_regexes)
|
40
|
+
@neutral_regexes = nil
|
49
41
|
self.class.supported_methods.each do |method|
|
50
42
|
routes = map[method]
|
51
43
|
@optimized_map[method] = routes.map.with_index do |route, index|
|
52
44
|
route.index = index
|
53
|
-
|
45
|
+
Regexp.new("(?<_#{index}>#{route.pattern.to_regexp})")
|
54
46
|
end
|
55
47
|
@optimized_map[method] = Regexp.union(@optimized_map[method])
|
56
48
|
end
|
@@ -62,8 +54,8 @@ module Grape
|
|
62
54
|
end
|
63
55
|
|
64
56
|
def associate_routes(pattern, **options)
|
65
|
-
|
66
|
-
@neutral_map <<
|
57
|
+
@neutral_regexes << Regexp.new("(?<_#{@neutral_map.length}>)#{pattern.to_regexp}")
|
58
|
+
@neutral_map << Grape::Router::AttributeTranslator.new(options.merge(pattern: pattern, index: @neutral_map.length))
|
67
59
|
end
|
68
60
|
|
69
61
|
def call(env)
|
@@ -5,8 +5,7 @@ module Grape
|
|
5
5
|
# Base for classes which need to operate with own values kept
|
6
6
|
# in the hash and inherited values kept in a Hash-like object.
|
7
7
|
class BaseInheritable
|
8
|
-
attr_accessor :inherited_values
|
9
|
-
attr_accessor :new_values
|
8
|
+
attr_accessor :inherited_values, :new_values
|
10
9
|
|
11
10
|
# @param inherited_values [Object] An object implementing an interface
|
12
11
|
# of the Hash class.
|
@@ -26,10 +25,14 @@ module Grape
|
|
26
25
|
end
|
27
26
|
|
28
27
|
def keys
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
if new_values.any?
|
29
|
+
combined = inherited_values.keys
|
30
|
+
combined.concat(new_values.keys)
|
31
|
+
combined.uniq!
|
32
|
+
combined
|
33
|
+
else
|
34
|
+
inherited_values.keys
|
35
|
+
end
|
33
36
|
end
|
34
37
|
|
35
38
|
def key?(name)
|
@@ -8,8 +8,10 @@ module Grape
|
|
8
8
|
protected
|
9
9
|
|
10
10
|
def concat_values(inherited_value, new_value)
|
11
|
+
return inherited_value unless new_value
|
12
|
+
|
11
13
|
[].tap do |value|
|
12
|
-
value.concat(new_value)
|
14
|
+
value.concat(new_value)
|
13
15
|
value.concat(inherited_value)
|
14
16
|
end
|
15
17
|
end
|