grape 1.5.0 → 1.6.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 +51 -2
- data/README.md +43 -10
- data/UPGRADING.md +91 -0
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +13 -17
- data/lib/grape/api.rb +7 -14
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +1 -1
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/helpers.rb +6 -4
- data/lib/grape/dsl/inside_route.rb +18 -9
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +11 -7
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +7 -6
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/endpoint.rb +21 -36
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/validation.rb +2 -3
- data/lib/grape/exceptions/validation_errors.rb +1 -1
- 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/locale/en.yml +1 -1
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/base.rb +4 -2
- data/lib/grape/middleware/error.rb +1 -1
- data/lib/grape/middleware/formatter.rb +4 -4
- data/lib/grape/middleware/stack.rb +10 -16
- 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/parser/json.rb +1 -1
- data/lib/grape/parser/xml.rb +1 -1
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +3 -0
- data/lib/grape/router/attribute_translator.rb +1 -1
- 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/validations/attributes_iterator.rb +8 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +92 -58
- data/lib/grape/validations/single_attribute_iterator.rb +1 -1
- data/lib/grape/validations/types/custom_type_coercer.rb +3 -2
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +3 -3
- data/lib/grape/validations/types.rb +1 -4
- data/lib/grape/validations/validator_factory.rb +1 -1
- data/lib/grape/validations/validators/all_or_none.rb +1 -0
- data/lib/grape/validations/validators/as.rb +4 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -0
- data/lib/grape/validations/validators/base.rb +12 -7
- data/lib/grape/validations/validators/coerce.rb +8 -9
- data/lib/grape/validations/validators/default.rb +1 -0
- data/lib/grape/validations/validators/exactly_one_of.rb +1 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +5 -2
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -0
- data/lib/grape/validations/validators/presence.rb +1 -0
- data/lib/grape/validations/validators/regexp.rb +1 -0
- data/lib/grape/validations/validators/same_as.rb +1 -0
- data/lib/grape/validations/validators/values.rb +3 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +3 -1
- data/spec/grape/api/custom_validations_spec.rb +1 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api_remount_spec.rb +9 -4
- data/spec/grape/api_spec.rb +203 -37
- data/spec/grape/dsl/callbacks_spec.rb +1 -1
- data/spec/grape/dsl/middleware_spec.rb +1 -1
- data/spec/grape/dsl/parameters_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/endpoint/declared_spec.rb +259 -1
- data/spec/grape/endpoint_spec.rb +18 -5
- data/spec/grape/entity_spec.rb +10 -10
- data/spec/grape/middleware/auth/dsl_spec.rb +1 -1
- data/spec/grape/middleware/error_spec.rb +1 -2
- data/spec/grape/middleware/formatter_spec.rb +2 -2
- data/spec/grape/middleware/stack_spec.rb +4 -3
- data/spec/grape/request_spec.rb +1 -1
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +13 -3
- data/spec/grape/validations/params_scope_spec.rb +37 -3
- data/spec/grape/validations/single_attribute_iterator_spec.rb +17 -6
- data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
- data/spec/grape/validations/validators/coerce_spec.rb +129 -22
- data/spec/grape/validations/validators/except_values_spec.rb +2 -2
- data/spec/grape/validations/validators/values_spec.rb +15 -11
- data/spec/grape/validations_spec.rb +280 -0
- data/spec/shared/versioning_examples.rb +22 -22
- data/spec/spec_helper.rb +1 -1
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- data/spec/support/versioned_helpers.rb +1 -1
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7af0abe5fd1cb203ceda93732e74ded372552f7a859c6a3c6b847d1ac31c9c97
|
4
|
+
data.tar.gz: 103a38ad4d41eccbfef3240cd8b04fe17f2bc080a4265e4353b43076ce03fb15
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c8fb085cbaa5ea150e072a54e98546f4336966871225ae5587d70d115da62d420e1f67b2459f83c39c5d29717f311e93d09e0440dd4bf4a75d1f1cec7573022
|
7
|
+
data.tar.gz: c0f6eb027e3f43c309c59643ceb28c8f7d783466c0663f3b65e93be52a0c3148123110d9aa81dda842027ace7f0f319519b5cdc1fec7102d65811a84fbc2b2ed
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,52 @@
|
|
1
|
+
### 1.6.0 (2021/10/04)
|
2
|
+
|
3
|
+
#### Features
|
4
|
+
|
5
|
+
* [#2190](https://github.com/ruby-grape/grape/pull/2190): Upgrade dev deps & drop Ruby 2.4.x support - [@dnesteryuk](https://github.com/dnesteryuk).
|
6
|
+
|
7
|
+
#### Fixes
|
8
|
+
|
9
|
+
* [#2176](https://github.com/ruby-grape/grape/pull/2176): Fix: OPTIONS fails if matching all routes - [@myxoh](https://github.com/myxoh).
|
10
|
+
* [#2177](https://github.com/ruby-grape/grape/pull/2177): Fix: `default` validator fails if preceded by `as` validator - [@Catsuko](https://github.com/Catsuko).
|
11
|
+
* [#2180](https://github.com/ruby-grape/grape/pull/2180): Call `super` in `API.inherited` - [@yogeshjain999](https://github.com/yogeshjain999).
|
12
|
+
* [#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).
|
13
|
+
|
14
|
+
### 1.5.3 (2021/03/07)
|
15
|
+
|
16
|
+
#### Fixes
|
17
|
+
|
18
|
+
* [#2161](https://github.com/ruby-grape/grape/pull/2157): Handle EOFError from Rack when given an empty multipart body - [@bschmeck](https://github.com/bschmeck).
|
19
|
+
* [#2162](https://github.com/ruby-grape/grape/pull/2162): Corrected a hash modification while iterating issue - [@Jack12816](https://github.com/Jack12816).
|
20
|
+
* [#2164](https://github.com/ruby-grape/grape/pull/2164): Fix: `coerce_with` is now called for params with `nil` value - [@braktar](https://github.com/braktar).
|
21
|
+
|
22
|
+
### 1.5.2 (2021/02/06)
|
23
|
+
|
24
|
+
#### Features
|
25
|
+
|
26
|
+
* [#2157](https://github.com/ruby-grape/grape/pull/2157): Custom types can set a message to be used in the response when invalid - [@dnesteryuk](https://github.com/dnesteryuk).
|
27
|
+
* [#2145](https://github.com/ruby-grape/grape/pull/2145): Ruby 3.0 compatibility - [@ericproulx](https://github.com/ericproulx).
|
28
|
+
* [#2143](https://github.com/ruby-grape/grape/pull/2143): Enable GitHub Actions with updated RuboCop and Danger - [@anakinj](https://github.com/anakinj).
|
29
|
+
|
30
|
+
#### Fixes
|
31
|
+
|
32
|
+
* [#2144](https://github.com/ruby-grape/grape/pull/2144): Fix compatibility issue with activesupport 6.1 and XML serialization of arrays - [@anakinj](https://github.com/anakinj).
|
33
|
+
* [#2137](https://github.com/ruby-grape/grape/pull/2137): Fix typos - [@johnny-miyake](https://github.com/johnny-miyake).
|
34
|
+
* [#2131](https://github.com/ruby-grape/grape/pull/2131): Fix Ruby 2.7 keyword deprecation warning in validators/coerce - [@K0H205](https://github.com/K0H205).
|
35
|
+
* [#2132](https://github.com/ruby-grape/grape/pull/2132): Use #ruby2_keywords for correct delegation on Ruby <= 2.6, 2.7 and 3 - [@eregon](https://github.com/eregon).
|
36
|
+
* [#2152](https://github.com/ruby-grape/grape/pull/2152): Fix configuration method inside namespaced params - [@fsainz](https://github.com/fsainz).
|
37
|
+
|
38
|
+
### 1.5.1 (2020/11/15)
|
39
|
+
|
40
|
+
#### Fixes
|
41
|
+
|
42
|
+
* [#2129](https://github.com/ruby-grape/grape/pull/2129): Fix validation error when Required Array nested inside an optional array, for Multiparam validators - [@dwhenry](https://github.com/dwhenry).
|
43
|
+
* [#2128](https://github.com/ruby-grape/grape/pull/2128): Fix validation error when Required Array nested inside an optional array - [@dwhenry](https://github.com/dwhenry).
|
44
|
+
* [#2127](https://github.com/ruby-grape/grape/pull/2127): Fix a performance issue with dependent params - [@dnesteryuk](https://github.com/dnesteryuk).
|
45
|
+
* [#2126](https://github.com/ruby-grape/grape/pull/2126): Fix warnings about redefined attribute accessors in `AttributeTranslator` - [@samsonjs](https://github.com/samsonjs).
|
46
|
+
* [#2121](https://github.com/ruby-grape/grape/pull/2121): Fix 2.7 deprecation warning in validator_factory - [@Legogris](https://github.com/Legogris).
|
47
|
+
* [#2115](https://github.com/ruby-grape/grape/pull/2115): Fix declared_params regression with multiple allowed types - [@stanhu](https://github.com/stanhu).
|
48
|
+
* [#2123](https://github.com/ruby-grape/grape/pull/2123): Fix 2.7 deprecation warning in middleware/stack - [@Legogris](https://github.com/Legogris).
|
49
|
+
|
1
50
|
### 1.5.0 (2020/10/05)
|
2
51
|
|
3
52
|
#### Fixes
|
@@ -6,7 +55,7 @@
|
|
6
55
|
* [#2103](https://github.com/ruby-grape/grape/pull/2103): Ensure complete declared params structure is present - [@tlconnor](https://github.com/tlconnor).
|
7
56
|
* [#2099](https://github.com/ruby-grape/grape/pull/2099): Added truffleruby to Travis-CI - [@gogainda](https://github.com/gogainda).
|
8
57
|
* [#2089](https://github.com/ruby-grape/grape/pull/2089): Specify order of mounting Grape with Rack::Cascade in README - [@jonmchan](https://github.com/jonmchan).
|
9
|
-
* [#
|
58
|
+
* [#2088](https://github.com/ruby-grape/grape/pull/2088): Set `Cache-Control` header only for streamed responses - [@stanhu](https://github.com/stanhu).
|
10
59
|
* [#2092](https://github.com/ruby-grape/grape/pull/2092): Correct an example params in Include Missing doc - [@huyvohcmc](https://github.com/huyvohcmc).
|
11
60
|
* [#2091](https://github.com/ruby-grape/grape/pull/2091): Fix ruby 2.7 keyword deprecations - [@dim](https://github.com/dim).
|
12
61
|
* [#2097](https://github.com/ruby-grape/grape/pull/2097): Skip to set default value unless `meets_dependency?` - [@wanabe](https://github.com/wanabe).
|
@@ -609,7 +658,7 @@
|
|
609
658
|
* [#492](https://github.com/ruby-grape/grape/pull/492): Don't allow to have nil value when a param is required and has a list of allowed values - [@Antti](https://github.com/Antti).
|
610
659
|
* [#495](https://github.com/ruby-grape/grape/pull/495): Fixed `ParamsScope#params` for parameters nested inside arrays - [@asross](https://github.com/asross).
|
611
660
|
* [#498](https://github.com/ruby-grape/grape/pull/498): Dry'ed up options and headers logic, allow headers to be passed to OPTIONS requests - [@karlfreeman](https://github.com/karlfreeman).
|
612
|
-
* [#500](https://github.com/ruby-grape/grape/pull/500): Skip entity auto-detection when
|
661
|
+
* [#500](https://github.com/ruby-grape/grape/pull/500): Skip entity auto-detection when explicitly passed - [@yaneq](https://github.com/yaneq).
|
613
662
|
* [#503](https://github.com/ruby-grape/grape/pull/503): Calling declared(params) from child namespace fails to include parent namespace defined params - [@myitcv](https://github.com/myitcv).
|
614
663
|
* [#512](https://github.com/ruby-grape/grape/pull/512): Don't create `Grape::Request` multiple times - [@dblock](https://github.com/dblock).
|
615
664
|
* [#538](https://github.com/ruby-grape/grape/pull/538): Fixed default values for grouped params - [@dm1try](https://github.com/dm1try).
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
![grape logo](grape.png)
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/grape.svg)](http://badge.fury.io/rb/grape)
|
4
|
-
[![Build Status](https://
|
4
|
+
[![Build Status](https://github.com/ruby-grape/grape/workflows/test/badge.svg?branch=master)](https://github.com/ruby-grape/grape/actions)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/ruby-grape/grape.svg)](https://codeclimate.com/github/ruby-grape/grape)
|
6
6
|
[![Coverage Status](https://coveralls.io/repos/github/ruby-grape/grape/badge.svg?branch=master)](https://coveralls.io/github/ruby-grape/grape?branch=master)
|
7
7
|
[![Inline docs](https://inch-ci.org/github/ruby-grape/grape.svg)](https://inch-ci.org/github/ruby-grape/grape)
|
@@ -19,6 +19,8 @@
|
|
19
19
|
- [All](#all)
|
20
20
|
- [Rack](#rack)
|
21
21
|
- [ActiveRecord without Rails](#activerecord-without-rails)
|
22
|
+
- [Rails 4](#rails-4)
|
23
|
+
- [Rails 5+](#rails-5)
|
22
24
|
- [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
|
23
25
|
- [Rails](#rails)
|
24
26
|
- [Rails < 5.2](#rails--52)
|
@@ -156,7 +158,7 @@ content negotiation, versioning and much more.
|
|
156
158
|
|
157
159
|
## Stable Release
|
158
160
|
|
159
|
-
You're reading the documentation for the stable release of Grape, 1.
|
161
|
+
You're reading the documentation for the stable release of Grape, **1.6.0**.
|
160
162
|
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
161
163
|
|
162
164
|
## Project Resources
|
@@ -316,13 +318,21 @@ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just
|
|
316
318
|
If you want to use ActiveRecord within Grape, you will need to make sure that ActiveRecord's connection pool
|
317
319
|
is handled correctly.
|
318
320
|
|
321
|
+
#### Rails 4
|
322
|
+
|
319
323
|
The easiest way to achieve that is by using ActiveRecord's `ConnectionManagement` middleware in your
|
320
324
|
`config.ru` before mounting Grape, e.g.:
|
321
325
|
|
322
326
|
```ruby
|
323
327
|
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
328
|
+
```
|
324
329
|
|
325
|
-
|
330
|
+
#### Rails 5+
|
331
|
+
|
332
|
+
Use [otr-activerecord](https://github.com/jhollinger/otr-activerecord) as follows:
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
use OTR::ActiveRecord::ConnectionManagement
|
326
336
|
```
|
327
337
|
|
328
338
|
### Alongside Sinatra (or other frameworks)
|
@@ -790,6 +800,7 @@ Grape allows you to access only the parameters that have been declared by your `
|
|
790
800
|
|
791
801
|
* Filter out the params that have been passed, but are not allowed.
|
792
802
|
* Include any optional params that are declared but not passed.
|
803
|
+
* Perform any parameter renaming on the resulting hash.
|
793
804
|
|
794
805
|
Consider the following API endpoint:
|
795
806
|
|
@@ -984,8 +995,10 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
|
|
984
995
|
````json
|
985
996
|
{
|
986
997
|
"declared_params": {
|
987
|
-
"
|
988
|
-
|
998
|
+
"user": {
|
999
|
+
"first_name": "first name",
|
1000
|
+
"last_name": null
|
1001
|
+
}
|
989
1002
|
}
|
990
1003
|
}
|
991
1004
|
````
|
@@ -1172,7 +1185,8 @@ Aside from the default set of supported types listed above, any class can be
|
|
1172
1185
|
used as a type as long as an explicit coercion method is supplied. If the type
|
1173
1186
|
implements a class-level `parse` method, Grape will use it automatically.
|
1174
1187
|
This method must take one string argument and return an instance of the correct
|
1175
|
-
type, or
|
1188
|
+
type, or return an instance of `Grape::Types::InvalidValue` which optionally
|
1189
|
+
accepts a message to be returned in the response.
|
1176
1190
|
|
1177
1191
|
```ruby
|
1178
1192
|
class Color
|
@@ -1182,8 +1196,9 @@ class Color
|
|
1182
1196
|
end
|
1183
1197
|
|
1184
1198
|
def self.parse(value)
|
1185
|
-
|
1186
|
-
|
1199
|
+
return new(value) if %w[blue red green]).include?(value)
|
1200
|
+
|
1201
|
+
Grape::Types::InvalidValue.new('Unsupported color')
|
1187
1202
|
end
|
1188
1203
|
end
|
1189
1204
|
|
@@ -1215,6 +1230,7 @@ params do
|
|
1215
1230
|
end
|
1216
1231
|
end
|
1217
1232
|
```
|
1233
|
+
Note that, a `nil` value will call the custom coercion method, while a missing parameter will not.
|
1218
1234
|
|
1219
1235
|
Example of use of `coerce_with` with a lambda (a class with a `parse` method could also have been used)
|
1220
1236
|
It will parse a string and return an Array of Integers, matching the `Array[Integer]` `type`.
|
@@ -1516,6 +1532,14 @@ end
|
|
1516
1532
|
|
1517
1533
|
While Procs are convenient for single cases, consider using [Custom Validators](#custom-validators) in cases where a validation is used more than once.
|
1518
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
|
+
|
1519
1543
|
#### `except_values`
|
1520
1544
|
|
1521
1545
|
Parameters can be restricted from having a specific set of values with the `:except_values` option.
|
@@ -2039,10 +2063,10 @@ end
|
|
2039
2063
|
|
2040
2064
|
# is NOT the same as
|
2041
2065
|
|
2042
|
-
get ':status' do # this makes
|
2066
|
+
get ':status' do # this makes params[:status] available
|
2043
2067
|
end
|
2044
2068
|
|
2045
|
-
# This will make both
|
2069
|
+
# This will make both params[:status_id] and params[:id] available
|
2046
2070
|
|
2047
2071
|
get 'statuses/:status_id/reviews/:id' do
|
2048
2072
|
end
|
@@ -3628,6 +3652,14 @@ You can access the controller params, headers, and helpers through the context w
|
|
3628
3652
|
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.
|
3629
3653
|
You only have to implement the helpers to access the specific `env` variable.
|
3630
3654
|
|
3655
|
+
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.
|
3656
|
+
|
3657
|
+
```ruby
|
3658
|
+
class Company::Application < Rails::Application
|
3659
|
+
config.middleware.insert_before(Rack::Attack, Middleware::ApiLogger)
|
3660
|
+
end
|
3661
|
+
```
|
3662
|
+
|
3631
3663
|
### Remote IP
|
3632
3664
|
|
3633
3665
|
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`.
|
@@ -3932,6 +3964,7 @@ Grape integrates with following third-party tools:
|
|
3932
3964
|
* **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
|
3933
3965
|
* **[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)
|
3934
3966
|
* **[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)
|
3967
|
+
* **[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)
|
3935
3968
|
|
3936
3969
|
## Contributing to Grape
|
3937
3970
|
|
data/UPGRADING.md
CHANGED
@@ -1,6 +1,95 @@
|
|
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 an `Grape::Exceptions::UnknownParameter` because `:a` was replace 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.
|
44
|
+
|
45
|
+
### Upgrading to >= 1.5.3
|
46
|
+
|
47
|
+
#### Nil value and coercion
|
48
|
+
|
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.
|
50
|
+
This behavior was not tested or documented. Version 1.3.0 quietly changed this behavior, in such that `nil` values skipped the coercion. Version 1.5.3 fixes and documents this as follows:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
class Api < Grape::API
|
54
|
+
params do
|
55
|
+
optional :value, type: Integer, coerce_with: ->(val) { val || 0 }
|
56
|
+
end
|
57
|
+
|
58
|
+
get 'example' do
|
59
|
+
params[:my_param]
|
60
|
+
end
|
61
|
+
get '/example', params: { value: nil }
|
62
|
+
# 1.5.2 = nil
|
63
|
+
# 1.5.3 = 0
|
64
|
+
get '/example', params: {}
|
65
|
+
# 1.5.2 = nil
|
66
|
+
# 1.5.3 = nil
|
67
|
+
end
|
68
|
+
```
|
69
|
+
See [#2164](https://github.com/ruby-grape/grape/pull/2164) for more information.
|
70
|
+
|
71
|
+
### Upgrading to >= 1.5.1
|
72
|
+
|
73
|
+
#### Dependent params
|
74
|
+
|
75
|
+
If you use [dependent params](https://github.com/ruby-grape/grape#dependent-parameters) with
|
76
|
+
`Grape::Extensions::Hash::ParamBuilder`, make sure a parameter to be dependent on is set as a Symbol.
|
77
|
+
If a String is given, a parameter that other parameters depend on won't be found even if it is present.
|
78
|
+
|
79
|
+
_Correct_:
|
80
|
+
```ruby
|
81
|
+
given :matrix do
|
82
|
+
# dependent params
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
_Wrong_:
|
87
|
+
```ruby
|
88
|
+
given 'matrix' do
|
89
|
+
# dependent params
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
4
93
|
### Upgrading to >= 1.5.0
|
5
94
|
|
6
95
|
Prior to 1.3.3, the `declared` helper would always return the complete params structure if `include_missing=true` was set. In 1.3.3 a regression was introduced such that a missing Hash with or without nested parameters would always resolve to `{}`.
|
@@ -167,6 +256,8 @@ end
|
|
167
256
|
|
168
257
|
### Upgrading to >= 1.3.0
|
169
258
|
|
259
|
+
You will need to upgrade to this version if you depend on `rack >= 2.1.0`.
|
260
|
+
|
170
261
|
#### Ruby
|
171
262
|
|
172
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
@@ -20,10 +20,11 @@ module Grape
|
|
20
20
|
|
21
21
|
# When inherited, will create a list of all instances (times the API was mounted)
|
22
22
|
# It will listen to the setup required to mount that endpoint, and replicate it on any new instance
|
23
|
-
def inherited(api
|
24
|
-
|
23
|
+
def inherited(api)
|
24
|
+
super
|
25
|
+
|
26
|
+
api.initial_setup(Grape::API == self ? Grape::API::Instance : @base_instance)
|
25
27
|
api.override_all_methods!
|
26
|
-
make_inheritable(api)
|
27
28
|
end
|
28
29
|
|
29
30
|
# Initialize the instance variables on the remountable class, and the base_instance
|
@@ -68,15 +69,6 @@ module Grape
|
|
68
69
|
instance_for_rack.call(*args, &block)
|
69
70
|
end
|
70
71
|
|
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
72
|
# Alleviates problems with autoloading by tring to search for the constant
|
81
73
|
def const_missing(*args)
|
82
74
|
if base_instance.const_defined?(*args)
|
@@ -87,7 +79,7 @@ module Grape
|
|
87
79
|
end
|
88
80
|
|
89
81
|
# The remountable class can have a configuration hash to provide some dynamic class-level variables.
|
90
|
-
# For instance, a
|
82
|
+
# For instance, a description could be done using: `desc configuration[:description]` if it may vary
|
91
83
|
# depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
|
92
84
|
# too much, you may actually want to provide a new API rather than remount it.
|
93
85
|
def mount_instance(**opts)
|
@@ -141,7 +133,7 @@ module Grape
|
|
141
133
|
# Adds a new stage to the set up require to get a Grape::API up and running
|
142
134
|
def add_setup(method, *args, &block)
|
143
135
|
setup_step = { method: method, args: args, block: block }
|
144
|
-
@setup
|
136
|
+
@setup += [setup_step]
|
145
137
|
last_response = nil
|
146
138
|
@instances.each do |instance|
|
147
139
|
last_response = replay_step_on(instance, setup_step)
|
@@ -151,6 +143,7 @@ module Grape
|
|
151
143
|
|
152
144
|
def replay_step_on(instance, setup_step)
|
153
145
|
return if skip_immediate_run?(instance, setup_step[:args])
|
146
|
+
|
154
147
|
args = evaluate_arguments(instance.configuration, *setup_step[:args])
|
155
148
|
response = instance.send(setup_step[:method], *args, &setup_step[:block])
|
156
149
|
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/callbacks.rb
CHANGED
@@ -59,7 +59,7 @@ module Grape
|
|
59
59
|
# end
|
60
60
|
# end
|
61
61
|
#
|
62
|
-
# This will make sure that the ApiLogger is opened and
|
62
|
+
# This will make sure that the ApiLogger is opened and closed around every
|
63
63
|
# request
|
64
64
|
# @param ensured_block [Proc] The block to be executed after every api_call
|
65
65
|
def finally(&block)
|
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/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
|