grape 2.4.0 → 3.0.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 +40 -0
- data/CONTRIBUTING.md +1 -9
- data/README.md +72 -31
- data/UPGRADING.md +34 -0
- data/grape.gemspec +4 -4
- data/lib/grape/api/instance.rb +49 -72
- data/lib/grape/api.rb +24 -34
- data/lib/grape/dry_types.rb +48 -4
- data/lib/grape/dsl/callbacks.rb +8 -58
- data/lib/grape/dsl/desc.rb +8 -67
- data/lib/grape/dsl/helpers.rb +59 -64
- data/lib/grape/dsl/inside_route.rb +20 -43
- data/lib/grape/dsl/logger.rb +3 -6
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +7 -16
- data/lib/grape/dsl/request_response.rb +136 -139
- data/lib/grape/dsl/routing.rb +229 -201
- data/lib/grape/dsl/settings.rb +22 -134
- data/lib/grape/dsl/validations.rb +37 -45
- data/lib/grape/endpoint.rb +64 -96
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/exceptions/base.rb +1 -1
- data/lib/grape/exceptions/missing_group_type.rb +0 -2
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
- data/lib/grape/middleware/auth/dsl.rb +5 -6
- data/lib/grape/middleware/error.rb +1 -11
- data/lib/grape/middleware/formatter.rb +4 -2
- data/lib/grape/middleware/stack.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/base.rb +24 -42
- data/lib/grape/middleware/versioner/header.rb +1 -1
- data/lib/grape/middleware/versioner/param.rb +2 -2
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +11 -0
- data/lib/grape/params_builder/base.rb +2 -0
- data/lib/grape/router.rb +4 -3
- data/lib/grape/util/api_description.rb +56 -0
- data/lib/grape/util/base_inheritable.rb +5 -2
- data/lib/grape/util/inheritable_setting.rb +7 -0
- data/lib/grape/util/media_type.rb +1 -1
- data/lib/grape/util/registry.rb +1 -1
- data/lib/grape/validations/contract_scope.rb +2 -2
- data/lib/grape/validations/params_documentation.rb +50 -0
- data/lib/grape/validations/params_scope.rb +38 -53
- data/lib/grape/validations/types/array_coercer.rb +2 -3
- data/lib/grape/validations/types/dry_type_coercer.rb +4 -11
- data/lib/grape/validations/types/primitive_coercer.rb +1 -28
- data/lib/grape/validations/types.rb +10 -25
- data/lib/grape/validations/validators/base.rb +0 -7
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +7 -10
- metadata +24 -14
- data/lib/grape/api/helpers.rb +0 -9
- data/lib/grape/dsl/api.rb +0 -17
- data/lib/grape/dsl/configuration.rb +0 -15
- data/lib/grape/types/invalid_value.rb +0 -8
- data/lib/grape/util/strict_hash_configuration.rb +0 -108
- data/lib/grape/validations/attributes_doc.rb +0 -60
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 919d93a2a8cd39c07b1a043fb342cbecdf5ce4a85b7696188d2de629f92b02a1
|
|
4
|
+
data.tar.gz: c33240607db8c00cba0fc1d5cf5d9c50809c561d3c29b10f246fe9e96423d277
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: baf576a03f0a56114032b12d842964d8fb5587de44bdded21c2faecca9a2da3bf7f8eb36aa7a8cec4b4b9b4904ab92ab9763045688452e88f891fc7bf781e527
|
|
7
|
+
data.tar.gz: 48dcc1cab168fd88fd7b839da6c0bbaecc8108c6eac62cc9e1f0f2c09c3e9fd5db2756fa246fce2fb2822c92eff5dc8ef6e2b694edf9b08e244b7c0749fb6633
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,42 @@
|
|
|
1
|
+
### 3.0.0 (2025-11-15)
|
|
2
|
+
|
|
3
|
+
#### Features
|
|
4
|
+
|
|
5
|
+
* [#2572](https://github.com/ruby-grape/grape/pull/2572): Drop support ruby 2.7 and active_support 6.1 - [@ericproulx](https://github.com/ericproulx).
|
|
6
|
+
* [#2573](https://github.com/ruby-grape/grape/pull/2573): Clean up deprecated code - [@ericproulx](https://github.com/ericproulx).
|
|
7
|
+
* [#2575](https://github.com/ruby-grape/grape/pull/2575): Refactor Api description class - [@ericproulx](https://github.com/ericproulx).
|
|
8
|
+
* [#2577](https://github.com/ruby-grape/grape/pull/2577): Deprecate `return` in endpoint execution - [@ericproulx](https://github.com/ericproulx).
|
|
9
|
+
* [#2580](https://github.com/ruby-grape/grape/pull/2580): Refactor endpoint helpers and error middleware integration - [@ericproulx](https://github.com/ericproulx).
|
|
10
|
+
* [#2581](https://github.com/ruby-grape/grape/pull/2581): Delegate `to_s` in Grape::API::Instance - [@ericproulx](https://github.com/ericproulx).
|
|
11
|
+
* [#2582](https://github.com/ruby-grape/grape/pull/2582): Fix leaky slash when normalizing - [@ericproulx](https://github.com/ericproulx).
|
|
12
|
+
* [#2583](https://github.com/ruby-grape/grape/pull/2583): Optimize api parameter documentation and memory usage - [@ericproulx](https://github.com/ericproulx).
|
|
13
|
+
* [#2589](https://github.com/ruby-grape/grape/pull/2589): Replace `send` by `__send__` in codebase - [@ericproulx](https://github.com/ericproulx).
|
|
14
|
+
* [#2598](https://github.com/ruby-grape/grape/pull/2598): Refactor settings DSL to use explicit methods instead of dynamic generation - [@ericproulx](https://github.com/ericproulx).
|
|
15
|
+
* [#2599](https://github.com/ruby-grape/grape/pull/2599): Simplify settings DSL get_or_set method and optimize logger implementation - [@ericproulx](https://github.com/ericproulx).
|
|
16
|
+
* [#2600](https://github.com/ruby-grape/grape/pull/2600): Refactor versioner middleware: simplify base class and improve consistency - [@ericproulx](https://github.com/ericproulx).
|
|
17
|
+
* [#2601](https://github.com/ruby-grape/grape/pull/2601): Refactor route_setting internal usage to use inheritable_setting.route for improved consistency and performance - [@ericproulx](https://github.com/ericproulx).
|
|
18
|
+
* [#2602](https://github.com/ruby-grape/grape/pull/2602): Remove `namespace_reverse_stackable` from public DSL interface and use direct inheritable_setting access - [@ericproulx](https://github.com/ericproulx).
|
|
19
|
+
* [#2603](https://github.com/ruby-grape/grape/pull/2603): Remove `namespace_stackable_with_hash` from public interface and move to internal InheritableSetting - [@ericproulx](https://github.com/ericproulx).
|
|
20
|
+
* [#2604](https://github.com/ruby-grape/grape/pull/2604): Enable branch coverage - [@ericproulx](https://github.com/ericproulx).
|
|
21
|
+
* [#2605](https://github.com/ruby-grape/grape/pull/2605): Add Rack 3.2 support with new gemfile and CI integration - [@ericproulx](https://github.com/ericproulx).
|
|
22
|
+
* [#2607](https://github.com/ruby-grape/grape/pull/2607): Remove namespace_stackable and namespace_inheritable from public API - [@ericproulx](https://github.com/ericproulx).
|
|
23
|
+
* [#2615](https://github.com/ruby-grape/grape/pull/2615): Remove manual toc and tod danger check - [@alexanderadam](https://github.com/alexanderadam).
|
|
24
|
+
* [#2612](https://github.com/ruby-grape/grape/pull/2612): Avoid multiple mount pollution - [@alexanderadam](https://github.com/alexanderadam).
|
|
25
|
+
* [#2617](https://github.com/ruby-grape/grape/pull/2617): Migrate from `ActiveSupport::Configurable` to `Dry::Configurable` - [@ericproulx](https://github.com/ericproulx).
|
|
26
|
+
* [#2618](https://github.com/ruby-grape/grape/pull/2618): Modernize argument delegation for Ruby 3+ compatibility - [@ericproulx](https://github.com/ericproulx).
|
|
27
|
+
* [#2623](https://github.com/ruby-grape/grape/pull/2623): Refactor coercer caching to use `Grape::Util::Cache` - [@ericproulx](https://github.com/ericproulx).
|
|
28
|
+
|
|
29
|
+
#### Fixes
|
|
30
|
+
|
|
31
|
+
* [#2586](https://github.com/ruby-grape/grape/pull/2586): Limit helpers DSL public scope - [@ericproulx](https://github.com/ericproulx).
|
|
32
|
+
* [#2588](https://github.com/ruby-grape/grape/pull/2588): Fix defaut format regression on */* - [@ericproulx](https://github.com/ericproulx).
|
|
33
|
+
* [#2593](https://github.com/ruby-grape/grape/pull/2593): Fix warning message when overriding global registry key - [@ericproulx](https://github.com/ericproulx).
|
|
34
|
+
* [#2594](https://github.com/ruby-grape/grape/pull/2594): Fix routes memoization - [@ericproulx](https://github.com/ericproulx).
|
|
35
|
+
* [#2595](https://github.com/ruby-grape/grape/pull/2595): Keep `within_namespace` as part of our internal api - [@ericproulx](https://github.com/ericproulx).
|
|
36
|
+
* [#2596](https://github.com/ruby-grape/grape/pull/2596): Remove `namespace_reverse_stackable_with_hash` from public scope - [@ericproulx](https://github.com/ericproulx).
|
|
37
|
+
* [#2621](https://github.com/ruby-grape/grape/pull/2621): Update upgrading notes regarding `return` usage and simplify endpoint execution - [@ericproulx](https://github.com/ericproulx).
|
|
38
|
+
* [#2622](https://github.com/ruby-grape/grape/pull/2622): Use `require_relative` instead of `$LOAD_PATH` in gemspec - [@ericproulx](https://github.com/ericproulx).
|
|
39
|
+
|
|
1
40
|
### 2.4.0 (2025-06-18)
|
|
2
41
|
|
|
3
42
|
#### Features
|
|
@@ -1165,3 +1204,4 @@
|
|
|
1165
1204
|
### 0.1.0 (2010/11/13)
|
|
1166
1205
|
|
|
1167
1206
|
* Initial public release - [@mbleigh](https://github.com/mbleigh).
|
|
1207
|
+
|
data/CONTRIBUTING.md
CHANGED
|
@@ -48,7 +48,7 @@ Here are some examples:
|
|
|
48
48
|
- running rspec on a specific file `docker-compose run --rm --build grape rspec spec/:file_path`
|
|
49
49
|
- running task `docker-compose run --rm --build grape rake <task_name>`
|
|
50
50
|
- running rubocop `docker-compose run --rm --build grape rubocop`
|
|
51
|
-
- running all specs on a specific ruby version (e.g
|
|
51
|
+
- running all specs on a specific ruby version (e.g 3.0) `RUBY_VERSION=3.0 docker-compose run --rm --build grape rspec`
|
|
52
52
|
- running specs on a specific gemfile (e.g rails_7_0.gemfile) `docker-compose run -e GEMFILE=rails_7_0 --rm --build grape rspec`
|
|
53
53
|
|
|
54
54
|
#### Bundle Install and Test
|
|
@@ -60,14 +60,6 @@ bundle install
|
|
|
60
60
|
bundle exec rake
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
Run tests against all supported versions of Rails.
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
gem install appraisal
|
|
67
|
-
appraisal install
|
|
68
|
-
appraisal rake spec
|
|
69
|
-
```
|
|
70
|
-
|
|
71
63
|
#### Write Tests
|
|
72
64
|
|
|
73
65
|
Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [spec/grape](spec/grape).
|
data/README.md
CHANGED
|
@@ -137,13 +137,17 @@
|
|
|
137
137
|
- [Reloading API Changes in Development](#reloading-api-changes-in-development)
|
|
138
138
|
- [Reloading in Rack Applications](#reloading-in-rack-applications)
|
|
139
139
|
- [Reloading in Rails Applications](#reloading-in-rails-applications)
|
|
140
|
+
- [Rails 7+ (Zeitwerk)](#rails-7-zeitwerk)
|
|
141
|
+
- [Rails 6 and Earlier](#rails-6-and-earlier)
|
|
140
142
|
- [Performance Monitoring](#performance-monitoring)
|
|
141
143
|
- [Active Support Instrumentation](#active-support-instrumentation)
|
|
142
|
-
- [
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
- [Hook Points](#hook-points)
|
|
145
|
+
- [endpoint_run.grape](#endpoint_rungrape)
|
|
146
|
+
- [endpoint_render.grape](#endpoint_rendergrape)
|
|
147
|
+
- [endpoint_run_filters.grape](#endpoint_run_filtersgrape)
|
|
148
|
+
- [endpoint_run_validators.grape](#endpoint_run_validatorsgrape)
|
|
149
|
+
- [format_response.grape](#format_responsegrape)
|
|
150
|
+
- [Subscribe to Hooks](#subscribe-to-hooks)
|
|
147
151
|
- [Monitoring Products](#monitoring-products)
|
|
148
152
|
- [Contributing to Grape](#contributing-to-grape)
|
|
149
153
|
- [Security](#security)
|
|
@@ -156,8 +160,7 @@ Grape is a REST-like API framework for Ruby. It's designed to run on Rack or com
|
|
|
156
160
|
|
|
157
161
|
## Stable Release
|
|
158
162
|
|
|
159
|
-
You're reading the documentation for the stable release of Grape,
|
|
160
|
-
Please read [UPGRADING](https://github.com/ruby-grape/grape/blob/v2.4.0/UPGRADING.md) when upgrading from a previous version.
|
|
163
|
+
You're reading the documentation for the stable release of Grape, 3.0.0.
|
|
161
164
|
|
|
162
165
|
## Project Resources
|
|
163
166
|
|
|
@@ -174,7 +177,7 @@ The maintainers of Grape are working with Tidelift to deliver commercial support
|
|
|
174
177
|
|
|
175
178
|
## Installation
|
|
176
179
|
|
|
177
|
-
Ruby
|
|
180
|
+
Ruby 3.0 or newer is required.
|
|
178
181
|
|
|
179
182
|
Grape is available as a gem, to install it run:
|
|
180
183
|
|
|
@@ -4077,6 +4080,25 @@ Use [grape-reload](https://github.com/AlexYankee/grape-reload).
|
|
|
4077
4080
|
|
|
4078
4081
|
### Reloading in Rails Applications
|
|
4079
4082
|
|
|
4083
|
+
#### Rails 7+ (Zeitwerk)
|
|
4084
|
+
|
|
4085
|
+
Rails 7+ uses [Zeitwerk](https://github.com/fxn/zeitwerk) as the default autoloader, which automatically handles reloading of code in development mode without any additional configuration.
|
|
4086
|
+
|
|
4087
|
+
If your API files are in `app/api`, Zeitwerk will automatically autoload and reload them. No additional configuration is needed.
|
|
4088
|
+
|
|
4089
|
+
If you encounter issues with reloading, ensure that:
|
|
4090
|
+
|
|
4091
|
+
1. Your API files follow Zeitwerk naming conventions (file names should match class names).
|
|
4092
|
+
2. The `config.enable_reloading` is set to `true` in `config/environments/development.rb` (this is the default).
|
|
4093
|
+
|
|
4094
|
+
For troubleshooting autoloading issues, have a look at the [Rails documentation](https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#troubleshooting).
|
|
4095
|
+
|
|
4096
|
+
See the [Rails Autoloading and Reloading Constants guide](https://guides.rubyonrails.org/autoloading_and_reloading_constants.html) for more information.
|
|
4097
|
+
|
|
4098
|
+
#### Rails 6 and Earlier
|
|
4099
|
+
|
|
4100
|
+
For Rails versions before 7, you need to configure reloading manually.
|
|
4101
|
+
|
|
4080
4102
|
Add API paths to `config/application.rb`.
|
|
4081
4103
|
|
|
4082
4104
|
```ruby
|
|
@@ -4095,28 +4117,12 @@ if Rails.env.development?
|
|
|
4095
4117
|
api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
|
|
4096
4118
|
Rails.application.reload_routes!
|
|
4097
4119
|
end
|
|
4098
|
-
|
|
4120
|
+
ActiveSupport::Reloader.to_prepare do
|
|
4099
4121
|
api_reloader.execute_if_updated
|
|
4100
4122
|
end
|
|
4101
4123
|
end
|
|
4102
4124
|
```
|
|
4103
4125
|
|
|
4104
|
-
For Rails >= 5.1.4, change this:
|
|
4105
|
-
|
|
4106
|
-
```ruby
|
|
4107
|
-
ActionDispatch::Callbacks.to_prepare do
|
|
4108
|
-
api_reloader.execute_if_updated
|
|
4109
|
-
end
|
|
4110
|
-
```
|
|
4111
|
-
|
|
4112
|
-
to this:
|
|
4113
|
-
|
|
4114
|
-
```ruby
|
|
4115
|
-
ActiveSupport::Reloader.to_prepare do
|
|
4116
|
-
api_reloader.execute_if_updated
|
|
4117
|
-
end
|
|
4118
|
-
```
|
|
4119
|
-
|
|
4120
4126
|
See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request/4368838#4368838) for more information.
|
|
4121
4127
|
|
|
4122
4128
|
## Performance Monitoring
|
|
@@ -4125,27 +4131,30 @@ See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-
|
|
|
4125
4131
|
|
|
4126
4132
|
Grape has built-in support for [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) which provides simple hook points to instrument key parts of your application.
|
|
4127
4133
|
|
|
4128
|
-
The following are currently supported:
|
|
4129
4134
|
|
|
4130
|
-
####
|
|
4135
|
+
#### Hook Points
|
|
4136
|
+
|
|
4137
|
+
The following hook points are currently supported:
|
|
4138
|
+
|
|
4139
|
+
##### endpoint_run.grape
|
|
4131
4140
|
|
|
4132
4141
|
The main execution of an endpoint, includes filters and rendering.
|
|
4133
4142
|
|
|
4134
4143
|
* *endpoint* - The endpoint instance
|
|
4135
4144
|
|
|
4136
|
-
|
|
4145
|
+
##### endpoint_render.grape
|
|
4137
4146
|
|
|
4138
4147
|
The execution of the main content block of the endpoint.
|
|
4139
4148
|
|
|
4140
4149
|
* *endpoint* - The endpoint instance
|
|
4141
4150
|
|
|
4142
|
-
|
|
4151
|
+
##### endpoint_run_filters.grape
|
|
4143
4152
|
|
|
4144
4153
|
* *endpoint* - The endpoint instance
|
|
4145
4154
|
* *filters* - The filters being executed
|
|
4146
4155
|
* *type* - The type of filters (before, before_validation, after_validation, after)
|
|
4147
4156
|
|
|
4148
|
-
|
|
4157
|
+
##### endpoint_run_validators.grape
|
|
4149
4158
|
|
|
4150
4159
|
The execution of validators.
|
|
4151
4160
|
|
|
@@ -4153,7 +4162,7 @@ The execution of validators.
|
|
|
4153
4162
|
* *validators* - The validators being executed
|
|
4154
4163
|
* *request* - The request being validated
|
|
4155
4164
|
|
|
4156
|
-
|
|
4165
|
+
##### format_response.grape
|
|
4157
4166
|
|
|
4158
4167
|
Serialization or template rendering.
|
|
4159
4168
|
|
|
@@ -4162,12 +4171,44 @@ Serialization or template rendering.
|
|
|
4162
4171
|
|
|
4163
4172
|
See the [ActiveSupport::Notifications documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) for information on how to subscribe to these events.
|
|
4164
4173
|
|
|
4174
|
+
#### Subscribe to Hooks
|
|
4175
|
+
|
|
4176
|
+
Once subscribed to the instrumentation, you can intercept the events reported above.
|
|
4177
|
+
|
|
4178
|
+
```ruby
|
|
4179
|
+
ActiveSupport::Notifications.subscribe(/<api_path>/) do |name, start, finish, id, payload|
|
|
4180
|
+
# your code to intercept the notification
|
|
4181
|
+
end
|
|
4182
|
+
```
|
|
4183
|
+
|
|
4184
|
+
The request data, the API’s internal data, and the response can be retrieved from the payload.
|
|
4185
|
+
|
|
4186
|
+
You can use `payload.fetch(:endpoint)` or directly `payload[:endpoint]`.
|
|
4187
|
+
|
|
4188
|
+
The `:endpoint` contains the data currently being processed, and access to attributes such as `body`, `request`, `params`, `headers`, `cookies` and `response_cookies`
|
|
4189
|
+
|
|
4190
|
+
For example, `payload[:endpoint].body` provides the current state of the response.
|
|
4191
|
+
|
|
4192
|
+
```ruby
|
|
4193
|
+
ActiveSupport::Notifications.subscribe(/v1/) do |name, start, finish, id, payload|
|
|
4194
|
+
hook_record = {
|
|
4195
|
+
hook: name
|
|
4196
|
+
status: payload[:env]&.dig("api.endpoint")&.status
|
|
4197
|
+
format: payload[:env]&.dig("api.format")
|
|
4198
|
+
body: payload[:endpoint]&.body
|
|
4199
|
+
duration: (finish - start) * 1000
|
|
4200
|
+
}
|
|
4201
|
+
# your code to save the notification
|
|
4202
|
+
end
|
|
4203
|
+
```
|
|
4204
|
+
|
|
4165
4205
|
### Monitoring Products
|
|
4166
4206
|
|
|
4167
4207
|
Grape integrates with following third-party tools:
|
|
4168
4208
|
|
|
4169
4209
|
* **New Relic** - [built-in support](https://docs.newrelic.com/docs/agents/ruby-agent/frameworks/grape-instrumentation) from v3.10.0 of the official [newrelic_rpm](https://github.com/newrelic/rpm) gem, also [newrelic-grape](https://github.com/xinminlabs/newrelic-grape) gem
|
|
4170
4210
|
* **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
|
|
4211
|
+
* **Rails Performance** - [rails_performance](https://github.com/igorkasyanchuk/rails_performance) gem
|
|
4171
4212
|
* **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
|
|
4172
4213
|
* **[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)
|
|
4173
4214
|
* **[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)
|
data/UPGRADING.md
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
Upgrading Grape
|
|
2
2
|
===============
|
|
3
3
|
|
|
4
|
+
### Upgrading to >= 3.0.0
|
|
5
|
+
|
|
6
|
+
#### Ruby 3+ Argument Delegation Modernization
|
|
7
|
+
|
|
8
|
+
Grape has been modernized to use Ruby 3+'s preferred argument delegation patterns. This change replaces `args.extract_options!` with explicit `**kwargs` parameters throughout the codebase.
|
|
9
|
+
|
|
10
|
+
- All DSL methods now use explicit keyword arguments (`**kwargs`) instead of extracting options from mixed argument lists
|
|
11
|
+
- Method signatures are now more explicit and follow Ruby 3+ best practices
|
|
12
|
+
- The `active_support/core_ext/array/extract_options` dependency has been removed
|
|
13
|
+
|
|
14
|
+
This is a modernization effort that improves code quality while maintaining full backward compatibility.
|
|
15
|
+
|
|
16
|
+
See [#2618](https://github.com/ruby-grape/grape/pull/2618) for more information.
|
|
17
|
+
|
|
18
|
+
#### Configuration API Migration from ActiveSupport::Configurable to Dry::Configurable
|
|
19
|
+
|
|
20
|
+
Grape has migrated from `ActiveSupport::Configurable` to `Dry::Configurable` for its configuration system since its [deprecated](https://github.com/rails/rails/blob/1cdd190a25e483b65f1f25bbd0f13a25d696b461/activesupport/lib/active_support/configurable.rb#L3-L7).
|
|
21
|
+
|
|
22
|
+
See [#2617](https://github.com/ruby-grape/grape/pull/2617) for more information.
|
|
23
|
+
|
|
24
|
+
#### Endpoint execution simplified and `return` deprecated
|
|
25
|
+
|
|
26
|
+
Executing a endpoint's block has been simplified and calling `return` in it has been deprecated. Use `next` instead.
|
|
27
|
+
|
|
28
|
+
See [#2577](https://github.com/ruby-grape/grape/pull/2577) for more information.
|
|
29
|
+
|
|
30
|
+
#### Old Deprecations Clean Up
|
|
31
|
+
|
|
32
|
+
- `rack_response` has been removed in favor of using `error!`.
|
|
33
|
+
- `Grape::Exceptions::MissingGroupType` and `Grape::Exceptions::UnsupportedGroupType` aliases `MissingGroupTypeError and `UnsupportedGroupType` have been removed.
|
|
34
|
+
- `Grape::Validations::Base` has been removed in favor of `Grape::Validations::Validators::Base`.
|
|
35
|
+
|
|
36
|
+
See [2573](https://github.com/ruby-grape/grape/pull/2573) for more information.
|
|
37
|
+
|
|
4
38
|
### Upgrading to >= 2.4.0
|
|
5
39
|
|
|
6
40
|
#### Grape::Middleware::Auth::Base
|
data/grape.gemspec
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require 'grape/version'
|
|
3
|
+
require_relative 'lib/grape/version'
|
|
5
4
|
|
|
6
5
|
Gem::Specification.new do |s|
|
|
7
6
|
s.name = 'grape'
|
|
@@ -21,7 +20,8 @@ Gem::Specification.new do |s|
|
|
|
21
20
|
'rubygems_mfa_required' => 'true'
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
s.add_dependency 'activesupport', '>=
|
|
23
|
+
s.add_dependency 'activesupport', '>= 7.0'
|
|
24
|
+
s.add_dependency 'dry-configurable'
|
|
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'
|
|
@@ -29,5 +29,5 @@ Gem::Specification.new do |s|
|
|
|
29
29
|
|
|
30
30
|
s.files = Dir['lib/**/*', 'CHANGELOG.md', 'CONTRIBUTING.md', 'README.md', 'grape.png', 'UPGRADING.md', 'LICENSE', 'grape.gemspec']
|
|
31
31
|
s.require_paths = ['lib']
|
|
32
|
-
s.required_ruby_version = '>=
|
|
32
|
+
s.required_ruby_version = '>= 3.0'
|
|
33
33
|
end
|
data/lib/grape/api/instance.rb
CHANGED
|
@@ -5,15 +5,30 @@ 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::DSL::Settings
|
|
9
|
+
extend Grape::DSL::Desc
|
|
10
|
+
extend Grape::DSL::Validations
|
|
11
|
+
extend Grape::DSL::Callbacks
|
|
12
|
+
extend Grape::DSL::Logger
|
|
13
|
+
extend Grape::DSL::Middleware
|
|
14
|
+
extend Grape::DSL::RequestResponse
|
|
15
|
+
extend Grape::DSL::Routing
|
|
16
|
+
extend Grape::DSL::Helpers
|
|
8
17
|
extend Grape::Middleware::Auth::DSL
|
|
9
|
-
|
|
18
|
+
|
|
19
|
+
Boolean = Grape::API::Boolean
|
|
10
20
|
|
|
11
21
|
class << self
|
|
22
|
+
extend Forwardable
|
|
12
23
|
attr_reader :instance, :base
|
|
13
24
|
attr_accessor :configuration
|
|
14
25
|
|
|
26
|
+
def_delegators :base, :to_s
|
|
27
|
+
|
|
15
28
|
def given(conditional_option, &block)
|
|
16
|
-
|
|
29
|
+
return unless conditional_option
|
|
30
|
+
|
|
31
|
+
mounted(&block)
|
|
17
32
|
end
|
|
18
33
|
|
|
19
34
|
def mounted(&block)
|
|
@@ -25,10 +40,6 @@ module Grape
|
|
|
25
40
|
grape_api.instances << self
|
|
26
41
|
end
|
|
27
42
|
|
|
28
|
-
def to_s
|
|
29
|
-
base&.to_s || super
|
|
30
|
-
end
|
|
31
|
-
|
|
32
43
|
def base_instance?
|
|
33
44
|
self == base.base_instance
|
|
34
45
|
end
|
|
@@ -50,11 +61,6 @@ module Grape
|
|
|
50
61
|
@instance ||= new # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
51
62
|
end
|
|
52
63
|
|
|
53
|
-
# Wipe the compiled API so we can recompile after changes were made.
|
|
54
|
-
def change!
|
|
55
|
-
@instance = nil
|
|
56
|
-
end
|
|
57
|
-
|
|
58
64
|
# This is the interface point between Rack and Grape; it accepts a request
|
|
59
65
|
# from Rack and ultimately returns an array of three values: the status,
|
|
60
66
|
# the headers, and the body. See [the rack specification]
|
|
@@ -71,11 +77,9 @@ module Grape
|
|
|
71
77
|
|
|
72
78
|
# (see #cascade?)
|
|
73
79
|
def cascade(value = nil)
|
|
74
|
-
if value.nil?
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
namespace_inheritable(:cascade, value)
|
|
78
|
-
end
|
|
80
|
+
return inheritable_setting.namespace_inheritable.key?(:cascade) ? !inheritable_setting.namespace_inheritable(:cascade).nil? : true if value.nil?
|
|
81
|
+
|
|
82
|
+
inheritable_setting.namespace_inheritable[:cascade] = value
|
|
79
83
|
end
|
|
80
84
|
|
|
81
85
|
def compile!
|
|
@@ -92,45 +96,6 @@ module Grape
|
|
|
92
96
|
|
|
93
97
|
protected
|
|
94
98
|
|
|
95
|
-
def prepare_routes
|
|
96
|
-
endpoints.map(&:routes).flatten
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Execute first the provided block, then each of the
|
|
100
|
-
# block passed in. Allows for simple 'before' setups
|
|
101
|
-
# of settings stack pushes.
|
|
102
|
-
def nest(*blocks, &block)
|
|
103
|
-
blocks.compact!
|
|
104
|
-
if blocks.any?
|
|
105
|
-
evaluate_as_instance_with_configuration(block) if block
|
|
106
|
-
blocks.each { |b| evaluate_as_instance_with_configuration(b) }
|
|
107
|
-
reset_validations!
|
|
108
|
-
else
|
|
109
|
-
instance_eval(&block)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def evaluate_as_instance_with_configuration(block, lazy: false)
|
|
114
|
-
lazy_block = Grape::Util::Lazy::Block.new do |configuration|
|
|
115
|
-
value_for_configuration = configuration
|
|
116
|
-
self.configuration = value_for_configuration.evaluate if value_for_configuration.try(:lazy?)
|
|
117
|
-
response = instance_eval(&block)
|
|
118
|
-
self.configuration = value_for_configuration
|
|
119
|
-
response
|
|
120
|
-
end
|
|
121
|
-
if base && base_instance? && lazy
|
|
122
|
-
lazy_block
|
|
123
|
-
else
|
|
124
|
-
lazy_block.evaluate_from(configuration)
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def inherited(subclass)
|
|
129
|
-
super
|
|
130
|
-
subclass.reset!
|
|
131
|
-
subclass.logger = logger.clone
|
|
132
|
-
end
|
|
133
|
-
|
|
134
99
|
def inherit_settings(other_settings)
|
|
135
100
|
top_level_setting.inherit_from other_settings.point_in_time_copy
|
|
136
101
|
|
|
@@ -143,6 +108,19 @@ module Grape
|
|
|
143
108
|
|
|
144
109
|
reset_routes!
|
|
145
110
|
end
|
|
111
|
+
|
|
112
|
+
# Wipe the compiled API so we can recompile after changes were made.
|
|
113
|
+
def change!
|
|
114
|
+
@instance = nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def inherited(subclass)
|
|
120
|
+
super
|
|
121
|
+
subclass.reset!
|
|
122
|
+
subclass.logger logger.clone
|
|
123
|
+
end
|
|
146
124
|
end
|
|
147
125
|
|
|
148
126
|
attr_reader :router
|
|
@@ -180,8 +158,9 @@ module Grape
|
|
|
180
158
|
# errors from reaching upstream. This is effectivelly done by unsetting
|
|
181
159
|
# X-Cascade. Default :cascade is true.
|
|
182
160
|
def cascade?
|
|
183
|
-
|
|
184
|
-
return
|
|
161
|
+
namespace_inheritable = self.class.inheritable_setting.namespace_inheritable
|
|
162
|
+
return namespace_inheritable[:cascade] if namespace_inheritable.key?(:cascade)
|
|
163
|
+
return namespace_inheritable[:version_options][:cascade] if namespace_inheritable[:version_options]&.key?(:cascade)
|
|
185
164
|
|
|
186
165
|
true
|
|
187
166
|
end
|
|
@@ -212,11 +191,12 @@ module Grape
|
|
|
212
191
|
last_route = routes.last # Most of the configuration is taken from the last endpoint
|
|
213
192
|
next if routes.any? { |route| route.request_method == '*' }
|
|
214
193
|
|
|
194
|
+
namespace_inheritable = self.class.inheritable_setting.namespace_inheritable
|
|
215
195
|
allowed_methods = routes.map(&:request_method)
|
|
216
|
-
allowed_methods |= [Rack::HEAD] if !
|
|
196
|
+
allowed_methods |= [Rack::HEAD] if !namespace_inheritable[:do_not_route_head] && allowed_methods.include?(Rack::GET)
|
|
217
197
|
|
|
218
|
-
allow_header =
|
|
219
|
-
last_route.app.options[:options_route_enabled] = true unless
|
|
198
|
+
allow_header = namespace_inheritable[:do_not_route_options] ? allowed_methods : [Rack::OPTIONS] | allowed_methods
|
|
199
|
+
last_route.app.options[:options_route_enabled] = true unless namespace_inheritable[:do_not_route_options] || allowed_methods.include?(Rack::OPTIONS)
|
|
220
200
|
|
|
221
201
|
@router.associate_routes(last_route.pattern, {
|
|
222
202
|
endpoint: last_route.app,
|
|
@@ -225,22 +205,19 @@ module Grape
|
|
|
225
205
|
end
|
|
226
206
|
end
|
|
227
207
|
|
|
208
|
+
ROOT_PREFIX_VERSIONING_KEY = %i[version version_options root_prefix].freeze
|
|
209
|
+
private_constant :ROOT_PREFIX_VERSIONING_KEY
|
|
210
|
+
|
|
228
211
|
# Allows definition of endpoints that ignore the versioning configuration
|
|
229
212
|
# used by the rest of your API.
|
|
230
213
|
def without_root_prefix_and_versioning
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
old_root_prefix = self.class.namespace_inheritable(:root_prefix)
|
|
234
|
-
|
|
235
|
-
self.class.namespace_inheritable_to_nil(:version)
|
|
236
|
-
self.class.namespace_inheritable_to_nil(:version_options)
|
|
237
|
-
self.class.namespace_inheritable_to_nil(:root_prefix)
|
|
238
|
-
|
|
214
|
+
inheritable_setting = self.class.inheritable_setting
|
|
215
|
+
deleted_values = inheritable_setting.namespace_inheritable.delete(*ROOT_PREFIX_VERSIONING_KEY)
|
|
239
216
|
yield
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
217
|
+
ensure
|
|
218
|
+
ROOT_PREFIX_VERSIONING_KEY.each_with_index do |key, index|
|
|
219
|
+
inheritable_setting.namespace_inheritable[key] = deleted_values[index]
|
|
220
|
+
end
|
|
244
221
|
end
|
|
245
222
|
end
|
|
246
223
|
end
|
data/lib/grape/api.rb
CHANGED
|
@@ -5,7 +5,9 @@ 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 recognize_path].freeze
|
|
8
|
+
NON_OVERRIDABLE = %i[call call! configuration compile! inherited recognize_path routes].freeze
|
|
9
|
+
|
|
10
|
+
Helpers = Grape::DSL::Helpers::BaseHelper
|
|
9
11
|
|
|
10
12
|
class Boolean
|
|
11
13
|
def self.build(val)
|
|
@@ -15,32 +17,18 @@ module Grape
|
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
class Instance
|
|
19
|
-
Boolean = Grape::API::Boolean
|
|
20
|
-
end
|
|
21
|
-
|
|
22
20
|
class << self
|
|
23
21
|
extend Forwardable
|
|
24
22
|
attr_accessor :base_instance, :instances
|
|
25
23
|
|
|
26
24
|
delegate_missing_to :base_instance
|
|
27
|
-
def_delegators :base_instance, :new, :configuration
|
|
28
25
|
|
|
29
26
|
# This is the interface point between Rack and Grape; it accepts a request
|
|
30
27
|
# from Rack and ultimately returns an array of three values: the status,
|
|
31
28
|
# the headers, and the body. See [the rack specification]
|
|
32
|
-
# (
|
|
29
|
+
# (https://github.com/rack/rack/blob/main/SPEC.rdoc) for more.
|
|
33
30
|
# NOTE: This will only be called on an API directly mounted on RACK
|
|
34
|
-
def_delegators :
|
|
35
|
-
|
|
36
|
-
# When inherited, will create a list of all instances (times the API was mounted)
|
|
37
|
-
# It will listen to the setup required to mount that endpoint, and replicate it on any new instance
|
|
38
|
-
def inherited(api)
|
|
39
|
-
super
|
|
40
|
-
|
|
41
|
-
api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance)
|
|
42
|
-
api.override_all_methods!
|
|
43
|
-
end
|
|
31
|
+
def_delegators :base_instance, :new, :configuration, :call, :compile!
|
|
44
32
|
|
|
45
33
|
# Initialize the instance variables on the remountable class, and the base_instance
|
|
46
34
|
# an instance that will be used to create the set up but will not be mounted
|
|
@@ -54,8 +42,8 @@ module Grape
|
|
|
54
42
|
# Redefines all methods so that are forwarded to add_setup and be recorded
|
|
55
43
|
def override_all_methods!
|
|
56
44
|
(base_instance.methods - Class.methods - NON_OVERRIDABLE).each do |method_override|
|
|
57
|
-
define_singleton_method(method_override) do |*args, &block|
|
|
58
|
-
add_setup(method: method_override, args: args, block: block)
|
|
45
|
+
define_singleton_method(method_override) do |*args, **kwargs, &block|
|
|
46
|
+
add_setup(method: method_override, args: args, kwargs: kwargs, block: block)
|
|
59
47
|
end
|
|
60
48
|
end
|
|
61
49
|
end
|
|
@@ -89,6 +77,15 @@ module Grape
|
|
|
89
77
|
|
|
90
78
|
private
|
|
91
79
|
|
|
80
|
+
# When inherited, will create a list of all instances (times the API was mounted)
|
|
81
|
+
# It will listen to the setup required to mount that endpoint, and replicate it on any new instance
|
|
82
|
+
def inherited(api)
|
|
83
|
+
super
|
|
84
|
+
|
|
85
|
+
api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance)
|
|
86
|
+
api.override_all_methods!
|
|
87
|
+
end
|
|
88
|
+
|
|
92
89
|
# Replays the set up to produce an API as defined in this class, can be called
|
|
93
90
|
# on classes that inherit from Grape::API
|
|
94
91
|
def replay_setup_on(instance)
|
|
@@ -97,16 +94,8 @@ module Grape
|
|
|
97
94
|
end
|
|
98
95
|
end
|
|
99
96
|
|
|
100
|
-
def instance_for_rack
|
|
101
|
-
if never_mounted?
|
|
102
|
-
base_instance
|
|
103
|
-
else
|
|
104
|
-
mounted_instances.first
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
97
|
# Adds a new stage to the set up require to get a Grape::API up and running
|
|
109
|
-
def add_setup(step)
|
|
98
|
+
def add_setup(**step)
|
|
110
99
|
@setup << step
|
|
111
100
|
last_response = nil
|
|
112
101
|
@instances.each do |instance|
|
|
@@ -130,12 +119,13 @@ module Grape
|
|
|
130
119
|
end
|
|
131
120
|
end
|
|
132
121
|
|
|
133
|
-
def replay_step_on(instance, method:, args:, block:)
|
|
134
|
-
return if skip_immediate_run?(instance, args)
|
|
122
|
+
def replay_step_on(instance, method:, args:, kwargs:, block:)
|
|
123
|
+
return if skip_immediate_run?(instance, args, kwargs)
|
|
135
124
|
|
|
136
125
|
eval_args = evaluate_arguments(instance.configuration, *args)
|
|
137
|
-
|
|
138
|
-
|
|
126
|
+
eval_kwargs = kwargs.deep_transform_values { |v| evaluate_arguments(instance.configuration, v).first }
|
|
127
|
+
response = instance.__send__(method, *eval_args, **eval_kwargs, &block)
|
|
128
|
+
if skip_immediate_run?(instance, [response], kwargs)
|
|
139
129
|
response
|
|
140
130
|
else
|
|
141
131
|
evaluate_arguments(instance.configuration, response).first
|
|
@@ -143,9 +133,9 @@ module Grape
|
|
|
143
133
|
end
|
|
144
134
|
|
|
145
135
|
# Skips steps that contain arguments to be lazily executed (on re-mount time)
|
|
146
|
-
def skip_immediate_run?(instance, args)
|
|
136
|
+
def skip_immediate_run?(instance, args, kwargs)
|
|
147
137
|
instance.base_instance? &&
|
|
148
|
-
(any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) })
|
|
138
|
+
(any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) } || any_lazy?(kwargs.values))
|
|
149
139
|
end
|
|
150
140
|
|
|
151
141
|
def any_lazy?(args)
|