grape 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +36 -14
- data/UPGRADING.md +56 -1
- data/grape.gemspec +1 -1
- data/lib/grape/api/instance.rb +3 -2
- data/lib/grape/api.rb +43 -66
- data/lib/grape/cookies.rb +31 -25
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/helpers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +6 -18
- data/lib/grape/dsl/parameters.rb +3 -3
- data/lib/grape/dsl/routing.rb +9 -1
- data/lib/grape/endpoint.rb +30 -33
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
- data/lib/grape/extensions/hash.rb +2 -1
- data/lib/grape/extensions/hashie/mash.rb +3 -5
- data/lib/grape/locale/en.yml +44 -44
- data/lib/grape/middleware/auth/base.rb +11 -32
- data/lib/grape/middleware/auth/dsl.rb +23 -29
- data/lib/grape/middleware/base.rb +30 -11
- data/lib/grape/middleware/error.rb +16 -24
- data/lib/grape/middleware/formatter.rb +38 -72
- data/lib/grape/middleware/stack.rb +26 -36
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -3
- data/lib/grape/middleware/versioner/base.rb +10 -18
- data/lib/grape/middleware/versioner/header.rb +1 -1
- data/lib/grape/middleware/versioner/param.rb +2 -3
- data/lib/grape/params_builder/base.rb +18 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/request.rb +161 -22
- data/lib/grape/router/route.rb +1 -1
- data/lib/grape/router.rb +25 -7
- data/lib/grape/validations/params_scope.rb +8 -3
- data/lib/grape/validations/validators/base.rb +2 -2
- data/lib/grape/validations/validators/except_values_validator.rb +1 -1
- data/lib/grape/validations/validators/presence_validator.rb +1 -1
- data/lib/grape/validations/validators/regexp_validator.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +13 -1
- metadata +18 -13
- data/lib/grape/error_formatter/jsonapi.rb +0 -7
- data/lib/grape/http/headers.rb +0 -56
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/parser/jsonapi.rb +0 -7
- data/lib/grape/util/lazy/object.rb +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83e82bddb3698a0f659d7c58edcb20c2d7edd44cce6e78f31139a2cf3452da0e
|
4
|
+
data.tar.gz: d47076d4e1b7445029b7695b5cfcb0ddebf72a1106743817eb583f79e636c8fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a22fbee812a61d12aa8ca151cc53835df146d840173fc92d9bb4ae049b5b14a70f6d399fcd06440167312af7bd60116e8382f21424a7c8b494b0251a82e7cea5
|
7
|
+
data.tar.gz: cd3d6d737305696db1cb703639dcdc3d7a6a4e32c02a25fd3942bef3e91529a8acaf11bb5cd4039b108ef2c908461c233a54207d2c4f528959fd944afeac0b22
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,32 @@
|
|
1
|
+
### 2.4.0 (2025-06-18)
|
2
|
+
|
3
|
+
#### Features
|
4
|
+
|
5
|
+
* [#2532](https://github.com/ruby-grape/grape/pull/2532): Update RuboCop 1.71.2 - [@ericproulx](https://github.com/ericproulx).
|
6
|
+
* [#2535](https://github.com/ruby-grape/grape/pull/2535): Delegate calls to inner objects - [@ericproulx](https://github.com/ericproulx).
|
7
|
+
* [#2537](https://github.com/ruby-grape/grape/pull/2537): Use activesupport `try` pattern - [@ericproulx](https://github.com/ericproulx).
|
8
|
+
* [#2536](https://github.com/ruby-grape/grape/pull/2536): Update normalize_path like Rails - [@ericproulx](https://github.com/ericproulx).
|
9
|
+
* [#2540](https://github.com/ruby-grape/grape/pull/2540): Introduce params builder with symbolized short name - [@ericproulx](https://github.com/ericproulx).
|
10
|
+
* [#2550](https://github.com/ruby-grape/grape/pull/2550): Drop ActiveSupport 6.0 - [@ericproulx](https://github.com/ericproulx).
|
11
|
+
* [#2549](https://github.com/ruby-grape/grape/pull/2549): Delegate cookies management to `Grape::Request` - [@ericproulx](https://github.com/ericproulx).
|
12
|
+
* [#2554](https://github.com/ruby-grape/grape/pull/2554): Remove `Grape::Http::Headers` and `Grape::Util::Lazy::Object` - [@ericproulx](https://github.com/ericproulx).
|
13
|
+
* [#2556](https://github.com/ruby-grape/grape/pull/2556): Remove unused `Grape::Request::DEFAULT_PARAMS_BUILDER` constant - [@eriklovmo](https://github.com/eriklovmo).
|
14
|
+
* [#2558](https://github.com/ruby-grape/grape/pull/2558): Add Ruby's option `enable_frozen_string_literal` in CI - [@ericproulx](https://github.com/ericproulx).
|
15
|
+
* [#2557](https://github.com/ruby-grape/grape/pull/2557): Add `lint!` - [@ericproulx](https://github.com/ericproulx).
|
16
|
+
* [#2561](https://github.com/ruby-grape/grape/pull/2561): Optimize hash alloc for middleware's default options - [@ericproulx](https://github.com/ericproulx).
|
17
|
+
* [#2563](https://github.com/ruby-grape/grape/pull/2563): Update `Grape::Middleware::Auth::Base` - [@ericproulx](https://github.com/ericproulx).
|
18
|
+
* [#2571](https://github.com/ruby-grape/grape/pull/2571): Update RuboCop 1.75.8 - [@pieterocp](https://github.com/pieterocp).
|
19
|
+
|
20
|
+
#### Fixes
|
21
|
+
|
22
|
+
* [#2538](https://github.com/ruby-grape/grape/pull/2538): Fix validating nested json array params - [@mohammednasser-32](https://github.com/mohammednasser-32).
|
23
|
+
* [#2543](https://github.com/ruby-grape/grape/pull/2543): Fix array allocation on mount - [@ericproulx](https://github.com/ericproulx).
|
24
|
+
* [#2546](https://github.com/ruby-grape/grape/pull/2546): Fix middleware with keywords - [@ericproulx](https://github.com/ericproulx).
|
25
|
+
* [#2547](https://github.com/ruby-grape/grape/pull/2547): Remove jsonapi related code - [@ericproulx](https://github.com/ericproulx).
|
26
|
+
* [#2548](https://github.com/ruby-grape/grape/pull/2548): Formatting from header acts like versioning from header - [@ericproulx](https://github.com/ericproulx).
|
27
|
+
* [#2552](https://github.com/ruby-grape/grape/pull/2552): Fix declared params optional array - [@ericproulx](https://github.com/ericproulx).
|
28
|
+
* [#2553](https://github.com/ruby-grape/grape/pull/2553): Improve performance of query params parsing - [@ericproulx](https://github.com/ericproulx).
|
29
|
+
|
1
30
|
### 2.3.0 (2025-02-08)
|
2
31
|
|
3
32
|
#### Features
|
data/CONTRIBUTING.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Contributing to Grape
|
2
2
|
=====================
|
3
3
|
|
4
|
-
Grape is work of [hundreds of contributors](https://github.com/ruby-grape/grape/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/ruby-grape/grape/pulls), [propose features and discuss issues](https://github.com/ruby-grape/grape/issues).
|
4
|
+
Grape is work of [hundreds of contributors](https://github.com/ruby-grape/grape/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/ruby-grape/grape/pulls), [propose features and discuss issues](https://github.com/ruby-grape/grape/issues).
|
5
5
|
|
6
6
|
#### Fork the Project
|
7
7
|
|
data/README.md
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|

|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/grape)
|
4
|
-
[](https://codeclimate.com/github/ruby-grape/grape)
|
4
|
+
[](https://github.com/ruby-grape/grape/actions/workflows/test.yml)
|
6
5
|
[](https://coveralls.io/github/ruby-grape/grape?branch=master)
|
7
|
-
[](https://inch-ci.org/github/ruby-grape/grape)
|
8
|
-
[](https://gitter.im/ruby-grape/grape?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
9
6
|
|
10
7
|
## Table of Contents
|
11
8
|
|
@@ -31,6 +28,8 @@
|
|
31
28
|
- [Header](#header)
|
32
29
|
- [Accept-Version Header](#accept-version-header)
|
33
30
|
- [Param](#param)
|
31
|
+
- [Linting](#linting)
|
32
|
+
- [Bug in Rack::ETag under Rack 3.X](#bug-in-racketag-under-rack-3x)
|
34
33
|
- [Describing Methods](#describing-methods)
|
35
34
|
- [Configuration](#configuration)
|
36
35
|
- [Parameters](#parameters)
|
@@ -157,14 +156,14 @@ Grape is a REST-like API framework for Ruby. It's designed to run on Rack or com
|
|
157
156
|
|
158
157
|
## Stable Release
|
159
158
|
|
160
|
-
You're reading the documentation for the
|
161
|
-
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
159
|
+
You're reading the documentation for the stable release of Grape, 2.4.0.
|
160
|
+
Please read [UPGRADING](https://github.com/ruby-grape/grape/blob/v2.4.0/UPGRADING.md) when upgrading from a previous version.
|
162
161
|
|
163
162
|
## Project Resources
|
164
163
|
|
165
164
|
* [Grape Website](http://www.ruby-grape.org)
|
166
165
|
* [Documentation](http://www.rubydoc.info/gems/grape)
|
167
|
-
* Need help?
|
166
|
+
* Need help? [Open an Issue](https://github.com/ruby-grape/grape/issues)
|
168
167
|
* [Follow us on Twitter](https://twitter.com/grapeframework)
|
169
168
|
|
170
169
|
## Grape for Enterprise
|
@@ -272,7 +271,7 @@ Grape's [deprecator](https://api.rubyonrails.org/v7.1.0/classes/ActiveSupport/De
|
|
272
271
|
### All
|
273
272
|
|
274
273
|
|
275
|
-
By default Grape will compile the routes on the first route, it is possible to pre-load routes using the `compile!` method.
|
274
|
+
By default Grape will compile the routes on the first route, but it is possible to pre-load routes using the `compile!` method.
|
276
275
|
|
277
276
|
```ruby
|
278
277
|
Twitter::API.compile!
|
@@ -653,6 +652,27 @@ version 'v1', using: :param, parameter: 'v'
|
|
653
652
|
curl http://localhost:9292/statuses/public_timeline?v=v1
|
654
653
|
|
655
654
|
|
655
|
+
## Linting
|
656
|
+
|
657
|
+
You can check whether your API is in conformance with the [Rack's specification](https://github.com/rack/rack/blob/main/SPEC.rdoc) by calling `lint!` at the API level or through [configuration](#configuration).
|
658
|
+
|
659
|
+
```ruby
|
660
|
+
class Api < Grape::API
|
661
|
+
lint!
|
662
|
+
end
|
663
|
+
```
|
664
|
+
```ruby
|
665
|
+
Grape.configure do |config|
|
666
|
+
config.lint = true
|
667
|
+
end
|
668
|
+
```
|
669
|
+
```ruby
|
670
|
+
Grape.config.lint = true
|
671
|
+
```
|
672
|
+
|
673
|
+
### Bug in Rack::ETag under Rack 3.X
|
674
|
+
If you're using Rack 3.X and the `Rack::Etag` middleware (used by [Rails](https://guides.rubyonrails.org/rails_on_rack.html#inspecting-middleware-stack)), a [bug](https://github.com/rack/rack/pull/2324) related to linting has been fixed in [3.1.13](https://github.com/rack/rack/blob/v3.1.13/CHANGELOG.md#3113---2025-04-13) and [3.0.15](https://github.com/rack/rack/blob/v3.1.13/CHANGELOG.md#3015---2025-04-13) respectively.
|
675
|
+
|
656
676
|
## Describing Methods
|
657
677
|
|
658
678
|
You can add a description to API methods and namespaces. The description would be used by [grape-swagger][grape-swagger] to generate swagger compliant documentation.
|
@@ -719,10 +739,13 @@ For example, for the `param_builder`, the following code could run in an initial
|
|
719
739
|
|
720
740
|
```ruby
|
721
741
|
Grape.configure do |config|
|
722
|
-
config.param_builder =
|
742
|
+
config.param_builder = :hashie_mash
|
723
743
|
end
|
724
744
|
```
|
725
745
|
|
746
|
+
Available parameter builders are `:hash`, `:hash_with_indifferent_access`, and `:hashie_mash`.
|
747
|
+
See [params_builder](lib/grape/params_builder).
|
748
|
+
|
726
749
|
You can also configure a single API:
|
727
750
|
|
728
751
|
```ruby
|
@@ -789,7 +812,7 @@ By default parameters are available as `ActiveSupport::HashWithIndifferentAccess
|
|
789
812
|
|
790
813
|
```ruby
|
791
814
|
class API < Grape::API
|
792
|
-
|
815
|
+
build_with :hashie_mash
|
793
816
|
|
794
817
|
params do
|
795
818
|
optional :color, type: String
|
@@ -803,16 +826,15 @@ The class can also be overridden on individual parameter blocks using `build_wit
|
|
803
826
|
|
804
827
|
```ruby
|
805
828
|
params do
|
806
|
-
build_with
|
829
|
+
build_with :hash
|
807
830
|
optional :color, type: String
|
808
831
|
end
|
809
832
|
```
|
810
833
|
|
811
|
-
Or globally with the [Configuration](#configuration) `Grape.configure.param_builder`.
|
812
|
-
|
813
834
|
In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.
|
814
835
|
|
815
|
-
Available parameter builders are
|
836
|
+
Available parameter builders are `:hash`, `:hash_with_indifferent_access`, and `:hashie_mash`.
|
837
|
+
See [params_builder](lib/grape/params_builder).
|
816
838
|
|
817
839
|
### Declared
|
818
840
|
|
data/UPGRADING.md
CHANGED
@@ -1,6 +1,61 @@
|
|
1
1
|
Upgrading Grape
|
2
2
|
===============
|
3
3
|
|
4
|
+
### Upgrading to >= 2.4.0
|
5
|
+
|
6
|
+
#### Grape::Middleware::Auth::Base
|
7
|
+
`type` is now validated at compile time and will raise a `Grape::Exceptions::UnknownAuthStrategy` if unknown.
|
8
|
+
|
9
|
+
#### Grape::Middleware::Base
|
10
|
+
|
11
|
+
- Second argument `options` is now a double splat (**) instead of single splat (*). If you're redefining `initialize` in your middleware and/or calling `super` in it, you might have to adapt the signature and the `super` call. Also, you might have to remove `{}` if you're pass `options` as a literal `Hash` or add `**` if you're using a variable.
|
12
|
+
- `Grape::Middleware::Helpers` has been removed. The equivalent method `context` is now part of `Grape::Middleware::Base`.
|
13
|
+
|
14
|
+
#### Grape::Http::Headers, Grape::Util::Lazy::Object
|
15
|
+
|
16
|
+
Both have been removed. See [2554](https://github.com/ruby-grape/grape/pull/2554).
|
17
|
+
Here are the notable changes:
|
18
|
+
|
19
|
+
- Constants like `HTTP_ACCEPT` have been replaced by their literal value.
|
20
|
+
- `SUPPORTED_METHODS` has been moved to `Grape` module.
|
21
|
+
- `HTTP_HEADERS` has been moved to `Grape::Request` and renamed `KNOWN_HEADERS`. The last has been refreshed with new headers, and it's not lazy anymore.
|
22
|
+
- `SUPPORTED_METHODS_WITHOUT_OPTIONS` and `find_supported_method` have been removed.
|
23
|
+
|
24
|
+
#### Grape::Middleware::Base
|
25
|
+
|
26
|
+
- Constant `TEXT_HTML` has been removed in favor of using literal string 'text/html'.
|
27
|
+
- `rack_request` and `query_params` have been added. Feel free to call these in your middlewares.
|
28
|
+
|
29
|
+
#### Params Builder
|
30
|
+
|
31
|
+
- Passing a class to `build_with` or `Grape.config.param_builder` has been deprecated in favor of a symbolized short_name. See `SHORTNAME_LOOKUP` in [params_builder](lib/grape/params_builder.rb).
|
32
|
+
- Including Grape's extensions like `Grape::Extensions::Hashie::Mash::ParamBuilder` has been deprecated in favor of using `build_with` at the route level.
|
33
|
+
|
34
|
+
#### Accept Header Negotiation Harmonized
|
35
|
+
|
36
|
+
[Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept) header is now fully interpreted through `Rack::Utils.best_q_match` which is following [RFC2616 14.1](https://datatracker.ietf.org/doc/html/rfc2616#section-14.1). Since [Grape 2.1.0](https://github.com/ruby-grape/grape/blob/master/CHANGELOG.md#210-20240615), the [header versioning strategy](https://github.com/ruby-grape/grape?tab=readme-ov-file#header) was adhering to it, but `Grape::Middleware::Formatter` never did.
|
37
|
+
|
38
|
+
Your API might act differently since it will strictly follow the [RFC2616 14.1](https://datatracker.ietf.org/doc/html/rfc2616#section-14.1) when interpreting the `Accept` header. Here are the differences:
|
39
|
+
|
40
|
+
##### Invalid or missing quality ranking
|
41
|
+
The following used to yield `application/xml` and now will yield `application/json` as the preferred media type:
|
42
|
+
- `application/json;q=invalid,application/xml;q=0.5`
|
43
|
+
- `application/json,application/xml;q=1.0`
|
44
|
+
|
45
|
+
For the invalid case, the value `invalid` was automatically `to_f` and `invalid.to_f` equals `0.0`. Now, since it doesn't match [Rack's regex](https://github.com/rack/rack/blob/3-1-stable/lib/rack/utils.rb#L138), its interpreted as non provided and its quality ranking equals 1.0.
|
46
|
+
|
47
|
+
For the non provided case, 1.0 was automatically assigned and in a case of multiple best matches, the first was returned based on Ruby's sort_by `quality`. Now, 1.0 is still assigned and the last is returned in case of multiple best matches. See [Rack's implementation](https://github.com/rack/rack/blob/e8f47608668d507e0f231a932fa37c9ca551c0a5/lib/rack/utils.rb#L167) of the RFC.
|
48
|
+
|
49
|
+
##### Considering the closest generic when vendor tree
|
50
|
+
Excluding the [header versioning strategy](https://github.com/ruby-grape/grape?tab=readme-ov-file#header), whenever a media type with the [vendor tree](https://datatracker.ietf.org/doc/html/rfc6838#section-3.2) leading facet `vnd.` like `application/vnd.api+json` was provided, Grape would also consider its closest generic when negotiating. In that case, `application/json` was added to the negotiation. Now, it will just consider the provided media types without considering any closest generics, and you'll need to [register](https://github.com/ruby-grape/grape?tab=readme-ov-file#api-formats) it.
|
51
|
+
You can find the official vendor tree registrations on [IANA](https://www.iana.org/assignments/media-types/media-types.xhtml)
|
52
|
+
|
53
|
+
#### Custom Validators
|
54
|
+
|
55
|
+
If you now receive an error of `'Grape::Validations.require_validator': unknown validator: your_custom_validation (Grape::Exceptions::UnknownValidator)` after upgrading to 2.4.0 then you will need to ensure that you require the `your_custom_validation` file before your Grape API code is loaded.
|
56
|
+
|
57
|
+
See [2533](https://github.com/ruby-grape/grape/issues/2533) for more information.
|
58
|
+
|
4
59
|
### Upgrading to >= 2.3.0
|
5
60
|
|
6
61
|
### `content_type` vs `api.format` inside API
|
@@ -83,7 +138,7 @@ When using together with `Grape::Extensions::Hash::ParamBuilder`, `route_param`
|
|
83
138
|
This was a regression introduced by [#2326](https://github.com/ruby-grape/grape/pull/2326) in Grape v1.8.0.
|
84
139
|
|
85
140
|
```ruby
|
86
|
-
|
141
|
+
Grape.configure do |config|
|
87
142
|
config.param_builder = Grape::Extensions::Hash::ParamBuilder
|
88
143
|
end
|
89
144
|
|
data/grape.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
'rubygems_mfa_required' => 'true'
|
22
22
|
}
|
23
23
|
|
24
|
-
s.add_dependency 'activesupport', '>= 6'
|
24
|
+
s.add_dependency 'activesupport', '>= 6.1'
|
25
25
|
s.add_dependency 'dry-types', '>= 1.1'
|
26
26
|
s.add_dependency 'mustermann-grape', '~> 1.1.0'
|
27
27
|
s.add_dependency 'rack', '>= 2'
|
data/lib/grape/api/instance.rb
CHANGED
@@ -5,6 +5,7 @@ module Grape
|
|
5
5
|
# The API Instance class, is the engine behind Grape::API. Each class that inherits
|
6
6
|
# from this will represent a different API instance
|
7
7
|
class Instance
|
8
|
+
extend Grape::Middleware::Auth::DSL
|
8
9
|
include Grape::DSL::API
|
9
10
|
|
10
11
|
class << self
|
@@ -112,7 +113,7 @@ module Grape
|
|
112
113
|
def evaluate_as_instance_with_configuration(block, lazy: false)
|
113
114
|
lazy_block = Grape::Util::Lazy::Block.new do |configuration|
|
114
115
|
value_for_configuration = configuration
|
115
|
-
self.configuration = value_for_configuration.evaluate if value_for_configuration.
|
116
|
+
self.configuration = value_for_configuration.evaluate if value_for_configuration.try(:lazy?)
|
116
117
|
response = instance_eval(&block)
|
117
118
|
self.configuration = value_for_configuration
|
118
119
|
response
|
@@ -164,7 +165,7 @@ module Grape
|
|
164
165
|
status, headers, response = @router.call(env)
|
165
166
|
unless cascade?
|
166
167
|
headers = Grape::Util::Header.new.merge(headers)
|
167
|
-
headers.delete(
|
168
|
+
headers.delete('X-Cascade')
|
168
169
|
end
|
169
170
|
|
170
171
|
[status, headers, response]
|
data/lib/grape/api.rb
CHANGED
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
# should subclass this class in order to build an API.
|
6
6
|
class API
|
7
7
|
# Class methods that we want to call on the API rather than on the API object
|
8
|
-
NON_OVERRIDABLE = %i[call call! configuration compile! inherited].freeze
|
8
|
+
NON_OVERRIDABLE = %i[call call! configuration compile! inherited recognize_path].freeze
|
9
9
|
|
10
10
|
class Boolean
|
11
11
|
def self.build(val)
|
@@ -20,12 +20,18 @@ module Grape
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class << self
|
23
|
+
extend Forwardable
|
23
24
|
attr_accessor :base_instance, :instances
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
delegate_missing_to :base_instance
|
27
|
+
def_delegators :base_instance, :new, :configuration
|
28
|
+
|
29
|
+
# This is the interface point between Rack and Grape; it accepts a request
|
30
|
+
# from Rack and ultimately returns an array of three values: the status,
|
31
|
+
# the headers, and the body. See [the rack specification]
|
32
|
+
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
|
33
|
+
# NOTE: This will only be called on an API directly mounted on RACK
|
34
|
+
def_delegators :instance_for_rack, :call, :compile!
|
29
35
|
|
30
36
|
# When inherited, will create a list of all instances (times the API was mounted)
|
31
37
|
# It will listen to the setup required to mount that endpoint, and replicate it on any new instance
|
@@ -49,7 +55,7 @@ module Grape
|
|
49
55
|
def override_all_methods!
|
50
56
|
(base_instance.methods - Class.methods - NON_OVERRIDABLE).each do |method_override|
|
51
57
|
define_singleton_method(method_override) do |*args, &block|
|
52
|
-
add_setup(method_override,
|
58
|
+
add_setup(method: method_override, args: args, block: block)
|
53
59
|
end
|
54
60
|
end
|
55
61
|
end
|
@@ -69,58 +75,28 @@ module Grape
|
|
69
75
|
end
|
70
76
|
end
|
71
77
|
|
72
|
-
# This is the interface point between Rack and Grape; it accepts a request
|
73
|
-
# from Rack and ultimately returns an array of three values: the status,
|
74
|
-
# the headers, and the body. See [the rack specification]
|
75
|
-
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
|
76
|
-
# NOTE: This will only be called on an API directly mounted on RACK
|
77
|
-
def call(...)
|
78
|
-
instance_for_rack.call(...)
|
79
|
-
end
|
80
|
-
|
81
78
|
# The remountable class can have a configuration hash to provide some dynamic class-level variables.
|
82
79
|
# For instance, a description could be done using: `desc configuration[:description]` if it may vary
|
83
80
|
# depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
|
84
81
|
# too much, you may actually want to provide a new API rather than remount it.
|
85
|
-
def mount_instance(
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
82
|
+
def mount_instance(configuration: nil)
|
83
|
+
Class.new(@base_parent).tap do |instance|
|
84
|
+
instance.configuration = Grape::Util::EndpointConfiguration.new(configuration || {})
|
85
|
+
instance.base = self
|
86
|
+
replay_setup_on(instance)
|
87
|
+
end
|
91
88
|
end
|
92
89
|
|
90
|
+
private
|
91
|
+
|
93
92
|
# Replays the set up to produce an API as defined in this class, can be called
|
94
93
|
# on classes that inherit from Grape::API
|
95
94
|
def replay_setup_on(instance)
|
96
95
|
@setup.each do |setup_step|
|
97
|
-
replay_step_on(instance, setup_step)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def respond_to?(method, include_private = false)
|
102
|
-
super || base_instance.respond_to?(method, include_private)
|
103
|
-
end
|
104
|
-
|
105
|
-
def respond_to_missing?(method, include_private = false)
|
106
|
-
base_instance.respond_to?(method, include_private)
|
107
|
-
end
|
108
|
-
|
109
|
-
def method_missing(method, *args, &block)
|
110
|
-
# If there's a missing method, it may be defined on the base_instance instead.
|
111
|
-
if respond_to_missing?(method)
|
112
|
-
base_instance.send(method, *args, &block)
|
113
|
-
else
|
114
|
-
super
|
96
|
+
replay_step_on(instance, **setup_step)
|
115
97
|
end
|
116
98
|
end
|
117
99
|
|
118
|
-
def compile!
|
119
|
-
instance_for_rack.compile! # See API::Instance.compile!
|
120
|
-
end
|
121
|
-
|
122
|
-
private
|
123
|
-
|
124
100
|
def instance_for_rack
|
125
101
|
if never_mounted?
|
126
102
|
base_instance
|
@@ -130,34 +106,35 @@ module Grape
|
|
130
106
|
end
|
131
107
|
|
132
108
|
# Adds a new stage to the set up require to get a Grape::API up and running
|
133
|
-
def add_setup(
|
134
|
-
|
135
|
-
@setup += [setup_step]
|
109
|
+
def add_setup(step)
|
110
|
+
@setup << step
|
136
111
|
last_response = nil
|
137
112
|
@instances.each do |instance|
|
138
|
-
last_response = replay_step_on(instance,
|
113
|
+
last_response = replay_step_on(instance, **step)
|
139
114
|
end
|
140
115
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
116
|
+
refresh_mount_step if step[:method] != :mount
|
117
|
+
last_response
|
118
|
+
end
|
119
|
+
|
120
|
+
# Updating all previously mounted classes in the case that new methods have been executed.
|
121
|
+
def refresh_mount_step
|
122
|
+
@setup.each do |setup_step|
|
123
|
+
next if setup_step[:method] != :mount
|
124
|
+
|
125
|
+
refresh_mount_step = setup_step.merge(method: :refresh_mounted_api)
|
126
|
+
@setup << refresh_mount_step
|
127
|
+
@instances.each do |instance|
|
128
|
+
replay_step_on(instance, **refresh_mount_step)
|
150
129
|
end
|
151
130
|
end
|
152
|
-
|
153
|
-
last_response
|
154
131
|
end
|
155
132
|
|
156
|
-
def replay_step_on(instance,
|
157
|
-
return if skip_immediate_run?(instance,
|
133
|
+
def replay_step_on(instance, method:, args:, block:)
|
134
|
+
return if skip_immediate_run?(instance, args)
|
158
135
|
|
159
|
-
|
160
|
-
response = instance.send(
|
136
|
+
eval_args = evaluate_arguments(instance.configuration, *args)
|
137
|
+
response = instance.send(method, *eval_args, &block)
|
161
138
|
if skip_immediate_run?(instance, [response])
|
162
139
|
response
|
163
140
|
else
|
@@ -172,12 +149,12 @@ module Grape
|
|
172
149
|
end
|
173
150
|
|
174
151
|
def any_lazy?(args)
|
175
|
-
args.any? { |argument| argument.
|
152
|
+
args.any? { |argument| argument.try(:lazy?) }
|
176
153
|
end
|
177
154
|
|
178
155
|
def evaluate_arguments(configuration, *args)
|
179
156
|
args.map do |argument|
|
180
|
-
if argument.
|
157
|
+
if argument.try(:lazy?)
|
181
158
|
argument.evaluate_from(configuration)
|
182
159
|
elsif argument.is_a?(Hash)
|
183
160
|
argument.transform_values { |value| evaluate_arguments(configuration, value).first }
|
data/lib/grape/cookies.rb
CHANGED
@@ -2,43 +2,49 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
class Cookies
|
5
|
-
|
6
|
-
@cookies = {}
|
7
|
-
@send_cookies = {}
|
8
|
-
end
|
5
|
+
extend Forwardable
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
DELETED_COOKIES_ATTRS = {
|
8
|
+
max_age: '0',
|
9
|
+
value: '',
|
10
|
+
expires: Time.at(0)
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
def_delegators :cookies, :[], :each
|
14
|
+
|
15
|
+
def initialize(rack_cookies)
|
16
|
+
@cookies = rack_cookies
|
17
|
+
@send_cookies = nil
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
def response_cookies
|
21
|
+
return unless @send_cookies
|
22
|
+
|
23
|
+
send_cookies.each do |name|
|
24
|
+
yield name, cookies[name]
|
20
25
|
end
|
21
26
|
end
|
22
27
|
|
23
|
-
def [](name)
|
24
|
-
|
28
|
+
def []=(name, value)
|
29
|
+
cookies[name] = value
|
30
|
+
send_cookies << name
|
25
31
|
end
|
26
32
|
|
27
|
-
|
28
|
-
|
29
|
-
|
33
|
+
# see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
|
34
|
+
def delete(name, **opts)
|
35
|
+
self.[]=(name, opts.merge(DELETED_COOKIES_ATTRS))
|
30
36
|
end
|
31
37
|
|
32
|
-
|
33
|
-
|
38
|
+
private
|
39
|
+
|
40
|
+
def cookies
|
41
|
+
return @cookies unless @cookies.is_a?(Proc)
|
42
|
+
|
43
|
+
@cookies = @cookies.call.with_indifferent_access
|
34
44
|
end
|
35
45
|
|
36
|
-
|
37
|
-
|
38
|
-
def delete(name, **opts)
|
39
|
-
options = opts.merge(max_age: '0', value: '', expires: Time.at(0))
|
40
|
-
self.[]=(name, options)
|
46
|
+
def send_cookies
|
47
|
+
@send_cookies ||= Set.new
|
41
48
|
end
|
42
|
-
# rubocop:enable Layout/SpaceBeforeBrackets
|
43
49
|
end
|
44
50
|
end
|
data/lib/grape/dsl/api.rb
CHANGED
data/lib/grape/dsl/headers.rb
CHANGED
@@ -10,7 +10,7 @@ module Grape
|
|
10
10
|
# 4. Delete a specifc header key-value pair
|
11
11
|
def header(key = nil, val = nil)
|
12
12
|
if key
|
13
|
-
val ? header[key
|
13
|
+
val ? header[key] = val : header.delete(key)
|
14
14
|
else
|
15
15
|
@header ||= Grape::Util::Header.new
|
16
16
|
end
|
data/lib/grape/dsl/helpers.rb
CHANGED
@@ -98,7 +98,7 @@ module Grape
|
|
98
98
|
protected
|
99
99
|
|
100
100
|
def process_named_params
|
101
|
-
return
|
101
|
+
return if @named_params.blank?
|
102
102
|
|
103
103
|
api.namespace_stackable(:named_params, @named_params)
|
104
104
|
end
|
@@ -79,7 +79,7 @@ module Grape
|
|
79
79
|
else
|
80
80
|
# If it is not a Hash then it does not have children.
|
81
81
|
# Find its value or set it to nil.
|
82
|
-
return unless options[:include_missing] || passed_params.key
|
82
|
+
return unless options[:include_missing] || passed_params.try(:key?, declared_param)
|
83
83
|
|
84
84
|
rename_path = params_nested_path + [declared_param.to_s]
|
85
85
|
renamed_param_name = renamed_params[rename_path]
|
@@ -107,7 +107,7 @@ module Grape
|
|
107
107
|
|
108
108
|
if type == 'Hash' && !has_children
|
109
109
|
{}
|
110
|
-
elsif type == 'Array' || (type&.start_with?('[') && type
|
110
|
+
elsif type == 'Array' || (type&.start_with?('[') && type.exclude?(','))
|
111
111
|
[]
|
112
112
|
elsif type == 'Set' || type&.start_with?('#<Set')
|
113
113
|
Set.new
|
@@ -213,7 +213,7 @@ module Grape
|
|
213
213
|
status 302
|
214
214
|
body_message ||= "This resource has been moved temporarily to #{url}."
|
215
215
|
end
|
216
|
-
header
|
216
|
+
header 'Location', url
|
217
217
|
content_type 'text/plain'
|
218
218
|
body body_message
|
219
219
|
end
|
@@ -257,18 +257,6 @@ module Grape
|
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
260
|
-
# Set or get a cookie
|
261
|
-
#
|
262
|
-
# @example
|
263
|
-
# cookies[:mycookie] = 'mycookie val'
|
264
|
-
# cookies['mycookie-string'] = 'mycookie string val'
|
265
|
-
# cookies[:more] = { value: '123', expires: Time.at(0) }
|
266
|
-
# cookies.delete :more
|
267
|
-
#
|
268
|
-
def cookies
|
269
|
-
@cookies ||= Cookies.new
|
270
|
-
end
|
271
|
-
|
272
260
|
# Allows you to define the response body as something other than the
|
273
261
|
# return value.
|
274
262
|
#
|
@@ -342,7 +330,7 @@ module Grape
|
|
342
330
|
return if value.nil? && @stream.nil?
|
343
331
|
|
344
332
|
header Rack::CONTENT_LENGTH, nil
|
345
|
-
header
|
333
|
+
header 'Transfer-Encoding', nil
|
346
334
|
header Rack::CACHE_CONTROL, 'no-cache' # Skips ETag generation (reading the response up front)
|
347
335
|
if value.is_a?(String)
|
348
336
|
file_body = Grape::ServeStream::FileBody.new(value)
|
@@ -447,11 +435,11 @@ module Grape
|
|
447
435
|
def entity_representation_for(entity_class, object, options)
|
448
436
|
embeds = { env: env }
|
449
437
|
embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
|
450
|
-
entity_class.represent(object, **embeds
|
438
|
+
entity_class.represent(object, **embeds, **options)
|
451
439
|
end
|
452
440
|
|
453
441
|
def http_version
|
454
|
-
env.fetch(
|
442
|
+
env.fetch('HTTP_VERSION') { env[Rack::SERVER_PROTOCOL] }
|
455
443
|
end
|
456
444
|
|
457
445
|
def api_format(format)
|