grape 1.5.3 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/CONTRIBUTING.md +2 -1
- data/README.md +31 -3
- data/UPGRADING.md +46 -4
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +13 -17
- data/lib/grape/api.rb +17 -12
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/headers.rb +5 -2
- data/lib/grape/dsl/helpers.rb +7 -5
- data/lib/grape/dsl/inside_route.rb +17 -8
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +3 -3
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +2 -2
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/endpoint.rb +20 -35
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/validation.rb +1 -2
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -1
- data/lib/grape/formatter/xml.rb +1 -0
- data/lib/grape/middleware/auth/dsl.rb +7 -1
- data/lib/grape/middleware/base.rb +3 -1
- data/lib/grape/middleware/formatter.rb +4 -4
- data/lib/grape/middleware/stack.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +1 -0
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +2 -0
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +1 -0
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +6 -0
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -2
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/params_scope.rb +88 -55
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -0
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +3 -3
- data/lib/grape/validations/validators/all_or_none.rb +8 -5
- data/lib/grape/validations/validators/allow_blank.rb +9 -7
- data/lib/grape/validations/validators/as.rb +6 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
- data/lib/grape/validations/validators/base.rb +75 -70
- data/lib/grape/validations/validators/coerce.rb +63 -79
- data/lib/grape/validations/validators/default.rb +37 -34
- data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
- data/lib/grape/validations/validators/except_values.rb +13 -11
- data/lib/grape/validations/validators/multiple_params_base.rb +24 -20
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
- data/lib/grape/validations/validators/presence.rb +7 -4
- data/lib/grape/validations/validators/regexp.rb +8 -5
- data/lib/grape/validations/validators/same_as.rb +18 -15
- data/lib/grape/validations/validators/values.rb +61 -56
- data/lib/grape/validations.rb +6 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +3 -1
- data/spec/grape/api/custom_validations_spec.rb +77 -45
- data/spec/grape/api/deeply_included_options_spec.rb +3 -3
- data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +1 -1
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
- data/spec/grape/api_remount_spec.rb +16 -15
- data/spec/grape/api_spec.rb +440 -227
- data/spec/grape/dsl/callbacks_spec.rb +2 -1
- data/spec/grape/dsl/headers_spec.rb +39 -9
- data/spec/grape/dsl/helpers_spec.rb +3 -2
- data/spec/grape/dsl/inside_route_spec.rb +6 -4
- data/spec/grape/dsl/logger_spec.rb +16 -18
- data/spec/grape/dsl/middleware_spec.rb +2 -1
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +10 -7
- data/spec/grape/endpoint/declared_spec.rb +259 -12
- data/spec/grape/endpoint_spec.rb +64 -55
- data/spec/grape/entity_spec.rb +22 -22
- data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
- data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
- data/spec/grape/exceptions/validation_spec.rb +5 -3
- data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
- data/spec/grape/integration/rack_sendfile_spec.rb +1 -1
- data/spec/grape/loading_spec.rb +8 -8
- data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
- data/spec/grape/middleware/auth/strategies_spec.rb +60 -20
- data/spec/grape/middleware/base_spec.rb +24 -15
- data/spec/grape/middleware/error_spec.rb +2 -2
- data/spec/grape/middleware/exception_spec.rb +111 -161
- data/spec/grape/middleware/formatter_spec.rb +27 -6
- data/spec/grape/middleware/globals_spec.rb +7 -4
- data/spec/grape/middleware/stack_spec.rb +14 -12
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
- data/spec/grape/middleware/versioner/header_spec.rb +14 -13
- data/spec/grape/middleware/versioner/param_spec.rb +7 -1
- data/spec/grape/middleware/versioner/path_spec.rb +5 -1
- data/spec/grape/middleware/versioner_spec.rb +1 -1
- data/spec/grape/parser_spec.rb +4 -0
- data/spec/grape/path_spec.rb +52 -52
- data/spec/grape/presenters/presenter_spec.rb +7 -6
- data/spec/grape/request_spec.rb +6 -4
- data/spec/grape/util/inheritable_setting_spec.rb +7 -7
- data/spec/grape/util/inheritable_values_spec.rb +3 -2
- data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
- data/spec/grape/util/stackable_values_spec.rb +7 -5
- data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +1 -0
- data/spec/grape/validations/params_scope_spec.rb +46 -10
- data/spec/grape/validations/single_attribute_iterator_spec.rb +2 -1
- data/spec/grape/validations/types/primitive_coercer_spec.rb +4 -4
- data/spec/grape/validations/types_spec.rb +8 -8
- data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
- data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
- data/spec/grape/validations/validators/coerce_spec.rb +23 -22
- data/spec/grape/validations/validators/default_spec.rb +72 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
- data/spec/grape/validations/validators/except_values_spec.rb +3 -3
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
- data/spec/grape/validations/validators/presence_spec.rb +16 -1
- data/spec/grape/validations/validators/regexp_spec.rb +25 -31
- data/spec/grape/validations/validators/same_as_spec.rb +14 -20
- data/spec/grape/validations/validators/values_spec.rb +183 -178
- data/spec/grape/validations_spec.rb +99 -58
- data/spec/integration/eager_load/eager_load_spec.rb +2 -2
- data/spec/integration/multi_json/json_spec.rb +1 -1
- data/spec/integration/multi_xml/xml_spec.rb +1 -1
- data/spec/shared/versioning_examples.rb +12 -9
- data/spec/spec_helper.rb +12 -2
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- metadata +103 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d182a6dfa2a571345d24a6a2b7286b4a2b34eeedc6570334ba2259fdda59faac
|
4
|
+
data.tar.gz: c365091e871e7ad78dd46dc0aab52cdd41d1e5ddb5a46503fcf0020af45f4324
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 539fdf761a5058dfcb3bab993a7fac3c7223ec06e0b828e102cb52095ac79f63668ebab1c1c4b47bf16984990c94f9a291537e7b2a1359626736b7abdd2c34ec
|
7
|
+
data.tar.gz: 2b30d941ab114aab53c4f4630742adedd037856ecc63b65f87b0df506349a13eed092964d9f53c003774006e74b9800043178b2ad2e6c55b0d5d42f6039a7e71
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,42 @@
|
|
1
|
+
### 1.6.2 (2021/12/30)
|
2
|
+
|
3
|
+
#### Features
|
4
|
+
|
5
|
+
#### Fixes
|
6
|
+
|
7
|
+
* [#2219](https://github.com/ruby-grape/grape/pull/2219): Revert the changes for autoloading provided in 1.6.1 - [@dm1try](https://github.com/dm1try).
|
8
|
+
|
9
|
+
### 1.6.1 (2021/12/28)
|
10
|
+
|
11
|
+
#### Features
|
12
|
+
|
13
|
+
* [#2196](https://github.com/ruby-grape/grape/pull/2196): Add support for `passwords_hashed` param for `digest_auth` - [@lHydra](https://github.com/lhydra).
|
14
|
+
* [#2208](https://github.com/ruby-grape/grape/pull/2208): Added Rails 7 support - [@ericproulx](https://github.com/ericproulx).
|
15
|
+
|
16
|
+
#### Fixes
|
17
|
+
|
18
|
+
* [#2206](https://github.com/ruby-grape/grape/pull/2206): Require main active_support lib before any of its extension definitions - [@annih](https://github.com/Annih).
|
19
|
+
* [#2193](https://github.com/ruby-grape/grape/pull/2193): Fixed the broken ruby-head NoMethodError spec - [@Jack12816](https://github.com/Jack12816).
|
20
|
+
* [#2192](https://github.com/ruby-grape/grape/pull/2192): Memoize the result of Grape::Middleware::Base#response - [@Jack12816](https://github.com/Jack12816).
|
21
|
+
* [#2200](https://github.com/ruby-grape/grape/pull/2200): Add validators module to all validators - [@ericproulx](https://github.com/ericproulx).
|
22
|
+
* [#2202](https://github.com/ruby-grape/grape/pull/2202): Fix random mock spec error - [@ericproulx](https://github.com/ericproulx).
|
23
|
+
* [#2203](https://github.com/ruby-grape/grape/pull/2203): Add rubocop-rspec - [@ericproulx](https://github.com/ericproulx).
|
24
|
+
* [#2207](https://github.com/ruby-grape/grape/pull/2207): Autoload Validations/Validators - [@ericproulx](https://github.com/ericproulx).
|
25
|
+
* [#2209](https://github.com/ruby-grape/grape/pull/2209): Autoload Validations/Types - [@ericproulx](https://github.com/ericproulx).
|
26
|
+
|
27
|
+
### 1.6.0 (2021/10/04)
|
28
|
+
|
29
|
+
#### Features
|
30
|
+
|
31
|
+
* [#2190](https://github.com/ruby-grape/grape/pull/2190): Upgrade dev deps & drop Ruby 2.4.x support - [@dnesteryuk](https://github.com/dnesteryuk).
|
32
|
+
|
33
|
+
#### Fixes
|
34
|
+
|
35
|
+
* [#2176](https://github.com/ruby-grape/grape/pull/2176): Fix: OPTIONS fails if matching all routes - [@myxoh](https://github.com/myxoh).
|
36
|
+
* [#2177](https://github.com/ruby-grape/grape/pull/2177): Fix: `default` validator fails if preceded by `as` validator - [@Catsuko](https://github.com/Catsuko).
|
37
|
+
* [#2180](https://github.com/ruby-grape/grape/pull/2180): Call `super` in `API.inherited` - [@yogeshjain999](https://github.com/yogeshjain999).
|
38
|
+
* [#2189](https://github.com/ruby-grape/grape/pull/2189): Fix: rename parameters when using `:as` (behaviour and grape-swagger documentation) - [@Jack12816](https://github.com/Jack12816).
|
39
|
+
|
1
40
|
### 1.5.3 (2021/03/07)
|
2
41
|
|
3
42
|
#### Fixes
|
data/CONTRIBUTING.md
CHANGED
@@ -35,6 +35,7 @@ bundle exec rake
|
|
35
35
|
Run tests against all supported versions of Rails.
|
36
36
|
|
37
37
|
```
|
38
|
+
gem install appraisal
|
38
39
|
appraisal install
|
39
40
|
appraisal rake spec
|
40
41
|
```
|
@@ -118,7 +119,7 @@ Go back to your pull request after a few minutes and see whether it passed muste
|
|
118
119
|
|
119
120
|
#### Be Patient
|
120
121
|
|
121
|
-
It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang
|
122
|
+
It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang in there!
|
122
123
|
|
123
124
|
#### Thank You
|
124
125
|
|
data/README.md
CHANGED
@@ -158,7 +158,7 @@ content negotiation, versioning and much more.
|
|
158
158
|
|
159
159
|
## Stable Release
|
160
160
|
|
161
|
-
You're reading the documentation for the stable release of Grape, **
|
161
|
+
You're reading the documentation for the stable release of Grape, **1.6.2**.
|
162
162
|
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
163
163
|
|
164
164
|
## Project Resources
|
@@ -800,6 +800,7 @@ Grape allows you to access only the parameters that have been declared by your `
|
|
800
800
|
|
801
801
|
* Filter out the params that have been passed, but are not allowed.
|
802
802
|
* Include any optional params that are declared but not passed.
|
803
|
+
* Perform any parameter renaming on the resulting hash.
|
803
804
|
|
804
805
|
Consider the following API endpoint:
|
805
806
|
|
@@ -994,8 +995,10 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
|
|
994
995
|
````json
|
995
996
|
{
|
996
997
|
"declared_params": {
|
997
|
-
"
|
998
|
-
|
998
|
+
"user": {
|
999
|
+
"first_name": "first name",
|
1000
|
+
"last_name": null
|
1001
|
+
}
|
999
1002
|
}
|
1000
1003
|
}
|
1001
1004
|
````
|
@@ -1529,6 +1532,14 @@ end
|
|
1529
1532
|
|
1530
1533
|
While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
|
1531
1534
|
|
1535
|
+
Note that [allow_blank](#allow_blank) validator applies while using `:values`. In the following example the absence of `:allow_blank` does not prevent `:state` from receiving blank values because `:allow_blank` defaults to `true`.
|
1536
|
+
|
1537
|
+
```ruby
|
1538
|
+
params do
|
1539
|
+
requires :state, type: Symbol, values: [:active, :inactive]
|
1540
|
+
end
|
1541
|
+
```
|
1542
|
+
|
1532
1543
|
#### `except_values`
|
1533
1544
|
|
1534
1545
|
Parameters can be restricted from having a specific set of values with the `:except_values` option.
|
@@ -3285,12 +3296,20 @@ http_basic do |username, password|
|
|
3285
3296
|
end
|
3286
3297
|
```
|
3287
3298
|
|
3299
|
+
Digest auth supports clear-text passwords and password hashes.
|
3300
|
+
|
3288
3301
|
```ruby
|
3289
3302
|
http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
|
3290
3303
|
# lookup the user's password here
|
3291
3304
|
end
|
3292
3305
|
```
|
3293
3306
|
|
3307
|
+
```ruby
|
3308
|
+
http_digest(realm: { realm: 'Test Api', opaque: 'app secret', passwords_hashed: true }) do |username|
|
3309
|
+
# lookup the user's password hash here
|
3310
|
+
end
|
3311
|
+
```
|
3312
|
+
|
3294
3313
|
### Register custom middleware for authentication
|
3295
3314
|
|
3296
3315
|
Grape can use custom Middleware for authentication. How to implement these
|
@@ -3641,6 +3660,14 @@ You can access the controller params, headers, and helpers through the context w
|
|
3641
3660
|
Note that when you're using Grape mounted on Rails you don't have to use Rails middleware because it's already included into your middleware stack.
|
3642
3661
|
You only have to implement the helpers to access the specific `env` variable.
|
3643
3662
|
|
3663
|
+
If you are using a custom application that is inherited from `Rails::Application` and need to insert a new middleware among the ones initiated via Rails, you will need to register it manually in your custom application class.
|
3664
|
+
|
3665
|
+
```ruby
|
3666
|
+
class Company::Application < Rails::Application
|
3667
|
+
config.middleware.insert_before(Rack::Attack, Middleware::ApiLogger)
|
3668
|
+
end
|
3669
|
+
```
|
3670
|
+
|
3644
3671
|
### Remote IP
|
3645
3672
|
|
3646
3673
|
By default you can access remote IP with `request.ip`. This is the remote IP address implemented by Rack. Sometimes it is desirable to get the remote IP [Rails-style](http://stackoverflow.com/questions/10997005/whats-the-difference-between-request-remote-ip-and-request-ip-in-rails) with `ActionDispatch::RemoteIp`.
|
@@ -3945,6 +3972,7 @@ Grape integrates with following third-party tools:
|
|
3945
3972
|
* **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
|
3946
3973
|
* **[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)
|
3947
3974
|
* **[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)
|
3975
|
+
* **[Datadog APM](https://docs.datadoghq.com/tracing/)** - [ddtrace](https://github.com/datadog/dd-trace-rb) gem, [documentation](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#grape)
|
3948
3976
|
|
3949
3977
|
## Contributing to Grape
|
3950
3978
|
|
data/UPGRADING.md
CHANGED
@@ -1,13 +1,53 @@
|
|
1
1
|
Upgrading Grape
|
2
2
|
===============
|
3
3
|
|
4
|
+
### Upgrading to >= 1.6.0
|
5
|
+
|
6
|
+
#### Parameter renaming with :as
|
7
|
+
|
8
|
+
Prior to 1.6.0 the [parameter renaming](https://github.com/ruby-grape/grape#renaming) with `:as` was directly touching the request payload ([`#params`](https://github.com/ruby-grape/grape#parameters)) while duplicating the old and the new key to be both available in the hash. This allowed clients to bypass any validation in case they knew the internal name of the parameter. Unfortunately, in combination with [grape-swagger](https://github.com/ruby-grape/grape-swagger) the internal name (name set with `:as`) of the parameters were documented.
|
9
|
+
|
10
|
+
This behavior was fixed. Parameter renaming is now done when using the [`#declared(params)`](https://github.com/ruby-grape/grape#declared) parameters helper. This stops confusing validation/coercion behavior.
|
11
|
+
|
12
|
+
Here comes an illustration of the old and new behaviour as code:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# (1) Rename a to b, while client sends +a+
|
16
|
+
optional :a, type: Integer, as: :b
|
17
|
+
params = { a: 1 }
|
18
|
+
declared(params, include_missing: false)
|
19
|
+
# expected => { b: 1 }
|
20
|
+
# actual => { b: 1 }
|
21
|
+
|
22
|
+
# (2) Rename a to b, while client sends +b+
|
23
|
+
optional :a, type: Integer, as: :b, values: [1, 2, 3]
|
24
|
+
params = { b: '5' }
|
25
|
+
declared(params, include_missing: false)
|
26
|
+
# expected => { } (>= 1.6.0)
|
27
|
+
# actual => { b: '5' } (uncasted, unvalidated, <= 1.5.3)
|
28
|
+
```
|
29
|
+
|
30
|
+
Another implication of this change is the dependent parameter resolution. Prior to 1.6.0 the following code produced a `Grape::Exceptions::UnknownParameter` because `:a` was replaced by `:b`:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
params do
|
34
|
+
optional :a, as: :b
|
35
|
+
given :a do # (<= 1.5.3 you had to reference +:b+ here to make it work)
|
36
|
+
requires :c
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
This code now works without any errors, as the renaming is just an internal behaviour of the `#declared(params)` parameter helper.
|
42
|
+
|
43
|
+
See [#2189](https://github.com/ruby-grape/grape/pull/2189) for more information.
|
4
44
|
|
5
45
|
### Upgrading to >= 1.5.3
|
6
46
|
|
7
|
-
|
47
|
+
#### Nil value and coercion
|
8
48
|
|
9
49
|
Prior to 1.2.5 version passing a `nil` value for a parameter with a custom coercer would invoke the coercer, and not passing a parameter would not invoke it.
|
10
|
-
This behavior was not tested or documented. Version 1.3.0 quietly changed this behavior, in
|
50
|
+
This behavior was not tested or documented. Version 1.3.0 quietly changed this behavior, in that `nil` values skipped the coercion. Version 1.5.3 fixes and documents this as follows:
|
11
51
|
|
12
52
|
```ruby
|
13
53
|
class Api < Grape::API
|
@@ -157,13 +197,13 @@ end
|
|
157
197
|
|
158
198
|
#### Nil values for structures
|
159
199
|
|
160
|
-
Nil values always been a special case when dealing with types especially with the following structures:
|
200
|
+
Nil values have always been a special case when dealing with types, especially with the following structures:
|
161
201
|
|
162
202
|
- Array
|
163
203
|
- Hash
|
164
204
|
- Set
|
165
205
|
|
166
|
-
The behavior for these structures has
|
206
|
+
The behavior for these structures has changed throughout the latest releases. For example:
|
167
207
|
|
168
208
|
```ruby
|
169
209
|
class Api < Grape::API
|
@@ -216,6 +256,8 @@ end
|
|
216
256
|
|
217
257
|
### Upgrading to >= 1.3.0
|
218
258
|
|
259
|
+
You will need to upgrade to this version if you depend on `rack >= 2.1.0`.
|
260
|
+
|
219
261
|
#### Ruby
|
220
262
|
|
221
263
|
After adding dry-types, Ruby 2.4 or newer is required.
|
data/grape.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
$LOAD_PATH.unshift File.expand_path('
|
3
|
+
$LOAD_PATH.unshift File.expand_path('lib', __dir__)
|
4
4
|
require 'grape/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
@@ -14,10 +14,10 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.description = 'A Ruby framework for rapid API development with great conventions.'
|
15
15
|
s.license = 'MIT'
|
16
16
|
s.metadata = {
|
17
|
-
'bug_tracker_uri'
|
18
|
-
'changelog_uri'
|
17
|
+
'bug_tracker_uri' => 'https://github.com/ruby-grape/grape/issues',
|
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'
|
20
|
+
'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}"
|
21
21
|
}
|
22
22
|
|
23
23
|
s.add_runtime_dependency 'activesupport'
|
@@ -32,5 +32,5 @@ Gem::Specification.new do |s|
|
|
32
32
|
s.files += Dir['lib/**/*']
|
33
33
|
s.test_files = Dir['spec/**/*']
|
34
34
|
s.require_paths = ['lib']
|
35
|
-
s.required_ruby_version = '>= 2.
|
35
|
+
s.required_ruby_version = '>= 2.5.0'
|
36
36
|
end
|
data/lib/grape/api/instance.rb
CHANGED
@@ -10,12 +10,11 @@ module Grape
|
|
10
10
|
include Grape::DSL::API
|
11
11
|
|
12
12
|
class << self
|
13
|
-
attr_reader :instance
|
14
|
-
attr_reader :base
|
13
|
+
attr_reader :instance, :base
|
15
14
|
attr_accessor :configuration
|
16
15
|
|
17
16
|
def given(conditional_option, &block)
|
18
|
-
evaluate_as_instance_with_configuration(block, lazy: true) if conditional_option &&
|
17
|
+
evaluate_as_instance_with_configuration(block, lazy: true) if conditional_option && block
|
19
18
|
end
|
20
19
|
|
21
20
|
def mounted(&block)
|
@@ -28,7 +27,7 @@ module Grape
|
|
28
27
|
end
|
29
28
|
|
30
29
|
def to_s
|
31
|
-
|
30
|
+
base&.to_s || super
|
32
31
|
end
|
33
32
|
|
34
33
|
def base_instance?
|
@@ -82,6 +81,7 @@ module Grape
|
|
82
81
|
|
83
82
|
def compile!
|
84
83
|
return if instance
|
84
|
+
|
85
85
|
LOCK.synchronize { compile unless instance }
|
86
86
|
end
|
87
87
|
|
@@ -103,7 +103,7 @@ module Grape
|
|
103
103
|
def nest(*blocks, &block)
|
104
104
|
blocks.reject!(&:nil?)
|
105
105
|
if blocks.any?
|
106
|
-
evaluate_as_instance_with_configuration(block) if
|
106
|
+
evaluate_as_instance_with_configuration(block) if block
|
107
107
|
blocks.each { |b| evaluate_as_instance_with_configuration(b) }
|
108
108
|
reset_validations!
|
109
109
|
else
|
@@ -114,9 +114,7 @@ module Grape
|
|
114
114
|
def evaluate_as_instance_with_configuration(block, lazy: false)
|
115
115
|
lazy_block = Grape::Util::LazyBlock.new do |configuration|
|
116
116
|
value_for_configuration = configuration
|
117
|
-
if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
|
118
|
-
self.configuration = value_for_configuration.evaluate
|
119
|
-
end
|
117
|
+
self.configuration = value_for_configuration.evaluate if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
|
120
118
|
response = instance_eval(&block)
|
121
119
|
self.configuration = value_for_configuration
|
122
120
|
response
|
@@ -179,7 +177,8 @@ module Grape
|
|
179
177
|
# X-Cascade. Default :cascade is true.
|
180
178
|
def cascade?
|
181
179
|
return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.key?(:cascade)
|
182
|
-
return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options)
|
180
|
+
return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options)&.key?(:cascade)
|
181
|
+
|
183
182
|
true
|
184
183
|
end
|
185
184
|
|
@@ -200,17 +199,15 @@ module Grape
|
|
200
199
|
without_root_prefix do
|
201
200
|
without_versioning do
|
202
201
|
versioned_route_configs.each do |config|
|
202
|
+
next if config[:options][:matching_wildchar]
|
203
|
+
|
203
204
|
allowed_methods = config[:methods].dup
|
204
205
|
|
205
|
-
|
206
|
-
allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
|
207
|
-
end
|
206
|
+
allowed_methods |= [Grape::Http::Headers::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Grape::Http::Headers::GET)
|
208
207
|
|
209
208
|
allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods)
|
210
209
|
|
211
|
-
unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
|
212
|
-
config[:endpoint].options[:options_route_enabled] = true
|
213
|
-
end
|
210
|
+
config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
|
214
211
|
|
215
212
|
attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
|
216
213
|
generate_not_allowed_method(config[:pattern], **attributes)
|
@@ -228,7 +225,7 @@ module Grape
|
|
228
225
|
last_route = routes.last # Most of the configuration is taken from the last endpoint
|
229
226
|
matching_wildchar = routes.any? { |route| route.request_method == '*' }
|
230
227
|
{
|
231
|
-
options: {},
|
228
|
+
options: { matching_wildchar: matching_wildchar },
|
232
229
|
pattern: last_route.pattern,
|
233
230
|
requirements: last_route.requirements,
|
234
231
|
path: last_route.origin,
|
@@ -248,7 +245,6 @@ module Grape
|
|
248
245
|
Grape::Http::Headers::SUPPORTED_METHODS_WITHOUT_OPTIONS
|
249
246
|
end
|
250
247
|
not_allowed_methods = supported_methods - allowed_methods
|
251
|
-
return if not_allowed_methods.empty?
|
252
248
|
@router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
|
253
249
|
end
|
254
250
|
|
data/lib/grape/api.rb
CHANGED
@@ -10,6 +10,18 @@ module Grape
|
|
10
10
|
# Class methods that we want to call on the API rather than on the API object
|
11
11
|
NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration compile! inherited]).freeze
|
12
12
|
|
13
|
+
class Boolean
|
14
|
+
def self.build(val)
|
15
|
+
return nil if val != true && val != false
|
16
|
+
|
17
|
+
new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Instance
|
22
|
+
Boolean = Grape::API::Boolean
|
23
|
+
end
|
24
|
+
|
13
25
|
class << self
|
14
26
|
attr_accessor :base_instance, :instances
|
15
27
|
|
@@ -20,10 +32,11 @@ module Grape
|
|
20
32
|
|
21
33
|
# When inherited, will create a list of all instances (times the API was mounted)
|
22
34
|
# It will listen to the setup required to mount that endpoint, and replicate it on any new instance
|
23
|
-
def inherited(api
|
24
|
-
|
35
|
+
def inherited(api)
|
36
|
+
super
|
37
|
+
|
38
|
+
api.initial_setup(Grape::API == self ? Grape::API::Instance : @base_instance)
|
25
39
|
api.override_all_methods!
|
26
|
-
make_inheritable(api)
|
27
40
|
end
|
28
41
|
|
29
42
|
# Initialize the instance variables on the remountable class, and the base_instance
|
@@ -68,15 +81,6 @@ module Grape
|
|
68
81
|
instance_for_rack.call(*args, &block)
|
69
82
|
end
|
70
83
|
|
71
|
-
# Allows an API to itself be inheritable:
|
72
|
-
def make_inheritable(api)
|
73
|
-
# When a child API inherits from a parent API.
|
74
|
-
def api.inherited(child_api)
|
75
|
-
# The instances of the child API inherit from the instances of the parent API
|
76
|
-
Grape::API.inherited(child_api, base_instance)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
84
|
# Alleviates problems with autoloading by tring to search for the constant
|
81
85
|
def const_missing(*args)
|
82
86
|
if base_instance.const_defined?(*args)
|
@@ -151,6 +155,7 @@ module Grape
|
|
151
155
|
|
152
156
|
def replay_step_on(instance, setup_step)
|
153
157
|
return if skip_immediate_run?(instance, setup_step[:args])
|
158
|
+
|
154
159
|
args = evaluate_arguments(instance.configuration, *setup_step[:args])
|
155
160
|
response = instance.send(setup_step[:method], *args, &setup_step[:block])
|
156
161
|
if skip_immediate_run?(instance, [response])
|
data/lib/grape/cookies.rb
CHANGED
@@ -33,9 +33,11 @@ module Grape
|
|
33
33
|
@cookies.each(&block)
|
34
34
|
end
|
35
35
|
|
36
|
+
# rubocop:disable Layout/SpaceBeforeBrackets
|
36
37
|
def delete(name, **opts)
|
37
38
|
options = opts.merge(value: 'deleted', expires: Time.at(0))
|
38
39
|
self.[]=(name, options)
|
39
40
|
end
|
41
|
+
# rubocop:enable Layout/SpaceBeforeBrackets
|
40
42
|
end
|
41
43
|
end
|
data/lib/grape/dsl/desc.rb
CHANGED
@@ -50,7 +50,7 @@ module Grape
|
|
50
50
|
# end
|
51
51
|
#
|
52
52
|
def desc(description, options = {}, &config_block)
|
53
|
-
if
|
53
|
+
if config_block
|
54
54
|
endpoint_configuration = if defined?(configuration)
|
55
55
|
# When the instance is mounted - the configuration is executed on mount time
|
56
56
|
if configuration.respond_to?(:evaluate)
|
@@ -68,9 +68,7 @@ module Grape
|
|
68
68
|
end
|
69
69
|
|
70
70
|
config_class.configure(&config_block)
|
71
|
-
unless options.empty?
|
72
|
-
warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.'
|
73
|
-
end
|
71
|
+
warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.' unless options.empty?
|
74
72
|
options = config_class.settings
|
75
73
|
else
|
76
74
|
options = options.merge(description: description)
|
@@ -92,7 +90,7 @@ module Grape
|
|
92
90
|
|
93
91
|
def unset_description_field(field)
|
94
92
|
description = route_setting(:description)
|
95
|
-
description
|
93
|
+
description&.delete(field)
|
96
94
|
end
|
97
95
|
|
98
96
|
# Returns an object which configures itself via an instance-context DSL.
|
data/lib/grape/dsl/headers.rb
CHANGED
@@ -3,8 +3,11 @@
|
|
3
3
|
module Grape
|
4
4
|
module DSL
|
5
5
|
module Headers
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# This method has four responsibilities:
|
7
|
+
# 1. Set a specifc header value by key
|
8
|
+
# 2. Retrieve a specifc header value by key
|
9
|
+
# 3. Retrieve all headers that have been set
|
10
|
+
# 4. Delete a specifc header key-value pair
|
8
11
|
def header(key = nil, val = nil)
|
9
12
|
if key
|
10
13
|
val ? header[key.to_s] = val : header.delete(key.to_s)
|
data/lib/grape/dsl/helpers.rb
CHANGED
@@ -36,8 +36,8 @@ module Grape
|
|
36
36
|
#
|
37
37
|
def helpers(*new_modules, &block)
|
38
38
|
include_new_modules(new_modules) if new_modules.any?
|
39
|
-
include_block(block) if
|
40
|
-
include_all_in_scope if !
|
39
|
+
include_block(block) if block
|
40
|
+
include_all_in_scope if !block && new_modules.empty?
|
41
41
|
end
|
42
42
|
|
43
43
|
protected
|
@@ -67,12 +67,13 @@ module Grape
|
|
67
67
|
|
68
68
|
def define_boolean_in_mod(mod)
|
69
69
|
return if defined? mod::Boolean
|
70
|
-
|
70
|
+
|
71
|
+
mod.const_set(:Boolean, Grape::API::Boolean)
|
71
72
|
end
|
72
73
|
|
73
|
-
def inject_api_helpers_to_mod(mod, &
|
74
|
+
def inject_api_helpers_to_mod(mod, &block)
|
74
75
|
mod.extend(BaseHelper) unless mod.is_a?(BaseHelper)
|
75
|
-
yield if
|
76
|
+
yield if block
|
76
77
|
mod.api_changed(self)
|
77
78
|
end
|
78
79
|
end
|
@@ -96,6 +97,7 @@ module Grape
|
|
96
97
|
|
97
98
|
def process_named_params
|
98
99
|
return unless instance_variable_defined?(:@named_params) && @named_params && @named_params.any?
|
100
|
+
|
99
101
|
api.namespace_stackable(:named_params, @named_params)
|
100
102
|
end
|
101
103
|
end
|
@@ -48,6 +48,8 @@ module Grape
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def declared_hash(passed_params, options, declared_params, params_nested_path)
|
51
|
+
renamed_params = route_setting(:renamed_params) || {}
|
52
|
+
|
51
53
|
declared_params.each_with_object(passed_params.class.new) do |declared_param, memo|
|
52
54
|
if declared_param.is_a?(Hash)
|
53
55
|
declared_param.each_pair do |declared_parent_param, declared_children_params|
|
@@ -55,8 +57,11 @@ module Grape
|
|
55
57
|
params_nested_path_dup << declared_parent_param.to_s
|
56
58
|
next unless options[:include_missing] || passed_params.key?(declared_parent_param)
|
57
59
|
|
60
|
+
rename_path = params_nested_path + [declared_parent_param.to_s]
|
61
|
+
renamed_param_name = renamed_params[rename_path]
|
62
|
+
|
63
|
+
memo_key = optioned_param_key(renamed_param_name || declared_parent_param, options)
|
58
64
|
passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
|
59
|
-
memo_key = optioned_param_key(declared_parent_param, options)
|
60
65
|
|
61
66
|
memo[memo_key] = handle_passed_param(params_nested_path_dup, passed_children_params.any?) do
|
62
67
|
declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
|
@@ -65,13 +70,13 @@ module Grape
|
|
65
70
|
else
|
66
71
|
# If it is not a Hash then it does not have children.
|
67
72
|
# Find its value or set it to nil.
|
68
|
-
|
69
|
-
param_renaming = has_renaming[declared_param] if has_renaming
|
73
|
+
next unless options[:include_missing] || passed_params.key?(declared_param)
|
70
74
|
|
71
|
-
|
75
|
+
rename_path = params_nested_path + [declared_param.to_s]
|
76
|
+
renamed_param_name = renamed_params[rename_path]
|
72
77
|
|
73
|
-
memo_key = optioned_param_key(
|
74
|
-
passed_param = passed_params[
|
78
|
+
memo_key = optioned_param_key(renamed_param_name || declared_param, options)
|
79
|
+
passed_param = passed_params[declared_param]
|
75
80
|
|
76
81
|
params_nested_path_dup = params_nested_path.dup
|
77
82
|
params_nested_path_dup << declared_param.to_s
|
@@ -86,7 +91,7 @@ module Grape
|
|
86
91
|
return yield if has_passed_children
|
87
92
|
|
88
93
|
key = params_nested_path[0]
|
89
|
-
key +=
|
94
|
+
key += "[#{params_nested_path[1..-1].join('][')}]" if params_nested_path.size > 1
|
90
95
|
|
91
96
|
route_options_params = options[:route_options][:params] || {}
|
92
97
|
type = route_options_params.dig(key, :type)
|
@@ -94,7 +99,7 @@ module Grape
|
|
94
99
|
|
95
100
|
if type == 'Hash' && !has_children
|
96
101
|
{}
|
97
|
-
elsif type == 'Array' || type&.start_with?('[') && !type&.include?(',')
|
102
|
+
elsif type == 'Array' || (type&.start_with?('[') && !type&.include?(','))
|
98
103
|
[]
|
99
104
|
elsif type == 'Set' || type&.start_with?('#<Set')
|
100
105
|
Set.new
|
@@ -117,6 +122,7 @@ module Grape
|
|
117
122
|
end
|
118
123
|
|
119
124
|
raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
|
125
|
+
|
120
126
|
declared_params
|
121
127
|
end
|
122
128
|
end
|
@@ -187,11 +193,13 @@ module Grape
|
|
187
193
|
case status
|
188
194
|
when Symbol
|
189
195
|
raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(status)
|
196
|
+
|
190
197
|
@status = Rack::Utils.status_code(status)
|
191
198
|
when Integer
|
192
199
|
@status = status
|
193
200
|
when nil
|
194
201
|
return @status if instance_variable_defined?(:@status) && @status
|
202
|
+
|
195
203
|
case request.request_method.to_s.upcase
|
196
204
|
when Grape::Http::Headers::POST
|
197
205
|
201
|
@@ -369,6 +377,7 @@ module Grape
|
|
369
377
|
representation = (body || {}).merge(key => representation)
|
370
378
|
elsif entity_class.present? && body
|
371
379
|
raise ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
|
380
|
+
|
372
381
|
representation = body.merge(representation)
|
373
382
|
end
|
374
383
|
|
data/lib/grape/dsl/middleware.rb
CHANGED
@@ -18,28 +18,28 @@ module Grape
|
|
18
18
|
# to inject.
|
19
19
|
def use(middleware_class, *args, &block)
|
20
20
|
arr = [:use, middleware_class, *args]
|
21
|
-
arr << block if
|
21
|
+
arr << block if block
|
22
22
|
|
23
23
|
namespace_stackable(:middleware, arr)
|
24
24
|
end
|
25
25
|
|
26
26
|
def insert(*args, &block)
|
27
27
|
arr = [:insert, *args]
|
28
|
-
arr << block if
|
28
|
+
arr << block if block
|
29
29
|
|
30
30
|
namespace_stackable(:middleware, arr)
|
31
31
|
end
|
32
32
|
|
33
33
|
def insert_before(*args, &block)
|
34
34
|
arr = [:insert_before, *args]
|
35
|
-
arr << block if
|
35
|
+
arr << block if block
|
36
36
|
|
37
37
|
namespace_stackable(:middleware, arr)
|
38
38
|
end
|
39
39
|
|
40
40
|
def insert_after(*args, &block)
|
41
41
|
arr = [:insert_after, *args]
|
42
|
-
arr << block if
|
42
|
+
arr << block if block
|
43
43
|
|
44
44
|
namespace_stackable(:middleware, arr)
|
45
45
|
end
|