grape 2.2.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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +41 -18
  5. data/UPGRADING.md +75 -1
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +25 -60
  8. data/lib/grape/api.rb +44 -76
  9. data/lib/grape/cookies.rb +31 -25
  10. data/lib/grape/dsl/api.rb +0 -2
  11. data/lib/grape/dsl/desc.rb +27 -24
  12. data/lib/grape/dsl/headers.rb +1 -1
  13. data/lib/grape/dsl/helpers.rb +1 -1
  14. data/lib/grape/dsl/inside_route.rb +17 -40
  15. data/lib/grape/dsl/parameters.rb +5 -5
  16. data/lib/grape/dsl/routing.rb +14 -13
  17. data/lib/grape/endpoint.rb +100 -106
  18. data/lib/grape/error_formatter/base.rb +51 -21
  19. data/lib/grape/error_formatter/json.rb +7 -24
  20. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  21. data/lib/grape/error_formatter/txt.rb +13 -20
  22. data/lib/grape/error_formatter/xml.rb +3 -13
  23. data/lib/grape/error_formatter.rb +4 -12
  24. data/lib/grape/exceptions/base.rb +18 -30
  25. data/lib/grape/exceptions/conflicting_types.rb +11 -0
  26. data/lib/grape/exceptions/invalid_parameters.rb +11 -0
  27. data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
  28. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  29. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  30. data/lib/grape/exceptions/validation.rb +5 -4
  31. data/lib/grape/exceptions/validation_errors.rb +2 -2
  32. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
  33. data/lib/grape/extensions/hash.rb +2 -1
  34. data/lib/grape/extensions/hashie/mash.rb +3 -5
  35. data/lib/grape/formatter/base.rb +16 -0
  36. data/lib/grape/formatter/json.rb +4 -6
  37. data/lib/grape/formatter/serializable_hash.rb +1 -1
  38. data/lib/grape/formatter/txt.rb +3 -5
  39. data/lib/grape/formatter/xml.rb +4 -6
  40. data/lib/grape/formatter.rb +4 -12
  41. data/lib/grape/locale/en.yml +44 -44
  42. data/lib/grape/middleware/auth/base.rb +11 -32
  43. data/lib/grape/middleware/auth/dsl.rb +23 -29
  44. data/lib/grape/middleware/base.rb +30 -11
  45. data/lib/grape/middleware/error.rb +18 -24
  46. data/lib/grape/middleware/formatter.rb +39 -73
  47. data/lib/grape/middleware/stack.rb +26 -36
  48. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -3
  49. data/lib/grape/middleware/versioner/base.rb +74 -0
  50. data/lib/grape/middleware/versioner/header.rb +4 -10
  51. data/lib/grape/middleware/versioner/param.rb +2 -5
  52. data/lib/grape/middleware/versioner/path.rb +0 -2
  53. data/lib/grape/middleware/versioner.rb +5 -3
  54. data/lib/grape/namespace.rb +1 -1
  55. data/lib/grape/params_builder/base.rb +18 -0
  56. data/lib/grape/params_builder/hash.rb +11 -0
  57. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  58. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  59. data/lib/grape/params_builder.rb +32 -0
  60. data/lib/grape/parser/base.rb +16 -0
  61. data/lib/grape/parser/json.rb +6 -8
  62. data/lib/grape/parser/xml.rb +6 -8
  63. data/lib/grape/parser.rb +5 -7
  64. data/lib/grape/path.rb +39 -56
  65. data/lib/grape/request.rb +162 -23
  66. data/lib/grape/router/base_route.rb +2 -2
  67. data/lib/grape/router/greedy_route.rb +2 -2
  68. data/lib/grape/router/pattern.rb +23 -18
  69. data/lib/grape/router/route.rb +14 -6
  70. data/lib/grape/router.rb +30 -12
  71. data/lib/grape/util/registry.rb +27 -0
  72. data/lib/grape/validations/contract_scope.rb +2 -39
  73. data/lib/grape/validations/params_scope.rb +15 -14
  74. data/lib/grape/validations/types/dry_type_coercer.rb +10 -6
  75. data/lib/grape/validations/validator_factory.rb +2 -2
  76. data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
  77. data/lib/grape/validations/validators/base.rb +7 -11
  78. data/lib/grape/validations/validators/coerce_validator.rb +1 -1
  79. data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
  80. data/lib/grape/validations/validators/default_validator.rb +1 -1
  81. data/lib/grape/validations/validators/except_values_validator.rb +2 -2
  82. data/lib/grape/validations/validators/length_validator.rb +1 -1
  83. data/lib/grape/validations/validators/presence_validator.rb +1 -1
  84. data/lib/grape/validations/validators/regexp_validator.rb +2 -2
  85. data/lib/grape/validations/validators/values_validator.rb +15 -57
  86. data/lib/grape/validations.rb +8 -17
  87. data/lib/grape/version.rb +1 -1
  88. data/lib/grape.rb +14 -2
  89. metadata +24 -16
  90. data/lib/grape/http/headers.rb +0 -55
  91. data/lib/grape/middleware/helpers.rb +0 -12
  92. data/lib/grape/middleware/versioner_helpers.rb +0 -75
  93. data/lib/grape/util/lazy/object.rb +0 -45
  94. data/lib/grape/validations/types/build_coercer.rb +0 -92
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc04f424b8181e92cc304d02d2923952f07e2532ef0291233596813726a2cb68
4
- data.tar.gz: 46729a20982fc16129540a81061bea229fd3ba5dd81426c1830119b52fe6ccd2
3
+ metadata.gz: 83e82bddb3698a0f659d7c58edcb20c2d7edd44cce6e78f31139a2cf3452da0e
4
+ data.tar.gz: d47076d4e1b7445029b7695b5cfcb0ddebf72a1106743817eb583f79e636c8fc
5
5
  SHA512:
6
- metadata.gz: 45e47b5059a37bbf75331c41bb1fdda2487610e3713baf5b8181dcbbae1727c47995503f5dc8a12375fd0765908140540100579afe834dfe96dc4c07cd771b2f
7
- data.tar.gz: 9692f64fc61714c33035ef59c0d68a9c34fb3725a5842e0fe16cbd4d7025265bef50ea5086a3f61d5ed1f0fd8740435619ff616eed55e45fd3cc48e090a02b7d
6
+ metadata.gz: a22fbee812a61d12aa8ca151cc53835df146d840173fc92d9bb4ae049b5b14a70f6d399fcd06440167312af7bd60116e8382f21424a7c8b494b0251a82e7cea5
7
+ data.tar.gz: cd3d6d737305696db1cb703639dcdc3d7a6a4e32c02a25fd3942bef3e91529a8acaf11bb5cd4039b108ef2c908461c233a54207d2c4f528959fd944afeac0b22
data/CHANGELOG.md CHANGED
@@ -1,3 +1,58 @@
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
+
30
+ ### 2.3.0 (2025-02-08)
31
+
32
+ #### Features
33
+
34
+ * [#2497](https://github.com/ruby-grape/grape/pull/2497): Update RuboCop to 1.66.1 - [@ericproulx](https://github.com/ericproulx).
35
+ * [#2500](https://github.com/ruby-grape/grape/pull/2500): Remove deprecated `file` method - [@ericproulx](https://github.com/ericproulx).
36
+ * [#2501](https://github.com/ruby-grape/grape/pull/2501): Remove deprecated `except` and `proc` options in values validator - [@ericproulx](https://github.com/ericproulx).
37
+ * [#2502](https://github.com/ruby-grape/grape/pull/2502): Remove deprecation `options` in `desc` - [@ericproulx](https://github.com/ericproulx).
38
+ * [#2512](https://github.com/ruby-grape/grape/pull/2512): Optimize hash alloc - [@ericproulx](https://github.com/ericproulx).
39
+ * [#2513](https://github.com/ruby-grape/grape/pull/2513): Optimize Grape::Path - [@ericproulx](https://github.com/ericproulx).
40
+ * [#2514](https://github.com/ruby-grape/grape/pull/2514): Add rails 8.0 to CI - [@ericproulx](https://github.com/ericproulx).
41
+ * [#2516](https://github.com/ruby-grape/grape/pull/2516): Dynamic registration for parsers, formatters, versioners - [@ericproulx](https://github.com/ericproulx).
42
+ * [#2518](https://github.com/ruby-grape/grape/pull/2518): Add ruby 3.4 to CI - [@ericproulx](https://github.com/ericproulx).
43
+
44
+ #### Fixes
45
+
46
+ * [#2504](https://github.com/ruby-grape/grape/pull/2504): Fix leaky modules in specs - [@ericproulx](https://github.com/ericproulx).
47
+ * [#2506](https://github.com/ruby-grape/grape/pull/2506): Fix fetch_formatter api_format - [@ericproulx](https://github.com/ericproulx).
48
+ * [#2507](https://github.com/ruby-grape/grape/pull/2507): Fix type: Set with values - [@nikolai-b](https://github.com/nikolai-b).
49
+ * [#2510](https://github.com/ruby-grape/grape/pull/2510): Fix ContractScope's validator inheritance - [@ericproulx](https://github.com/ericproulx).
50
+ * [#2521](https://github.com/ruby-grape/grape/pull/2521): Fixed typo in README - [@datpmt](https://github.com/datpmt).
51
+ * [#2525](https://github.com/ruby-grape/grape/pull/2525): Require logger before active_support - [@ericproulx](https://github.com/ericproulx).
52
+ * [#2524](https://github.com/ruby-grape/grape/pull/2524): Fix validators bad encoding - [@ericproulx](https://github.com/ericproulx).
53
+ * [#2530](https://github.com/ruby-grape/grape/pull/2530): Fix endpoint's status when rescue_from without a block - [@ericproulx](https://github.com/ericproulx).
54
+ * [#2529](https://github.com/ruby-grape/grape/pull/2529): Fix missing settings on mounted routes (when settings are identical) - [@Haerezis](https://github.com/Haerezis).
55
+
1
56
  ### 2.2.0 (2024-09-14)
2
57
 
3
58
  #### 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). When in doubt, ask a question in the [Grape Google Group](http://groups.google.com/group/ruby-grape).
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
  ![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://github.com/ruby-grape/grape/workflows/test/badge.svg?branch=master)](https://github.com/ruby-grape/grape/actions)
5
- [![Code Climate](https://codeclimate.com/github/ruby-grape/grape.svg)](https://codeclimate.com/github/ruby-grape/grape)
4
+ [![test](https://github.com/ruby-grape/grape/actions/workflows/test.yml/badge.svg)](https://github.com/ruby-grape/grape/actions/workflows/test.yml)
6
5
  [![Coverage Status](https://coveralls.io/repos/github/ruby-grape/grape/badge.svg?branch=master)](https://coveralls.io/github/ruby-grape/grape?branch=master)
7
- [![Inline docs](https://inch-ci.org/github/ruby-grape/grape.svg)](https://inch-ci.org/github/ruby-grape/grape)
8
- [![Join the chat at https://gitter.im/ruby-grape/grape](https://badges.gitter.im/ruby-grape/grape.svg)](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,13 +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 stable release of Grape, 2.2.0. Please read UPGRADING 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.
161
161
 
162
162
  ## Project Resources
163
163
 
164
164
  * [Grape Website](http://www.ruby-grape.org)
165
165
  * [Documentation](http://www.rubydoc.info/gems/grape)
166
- * Need help? Try [Grape Google Group](http://groups.google.com/group/ruby-grape) or [Gitter](https://gitter.im/ruby-grape/grape)
166
+ * Need help? [Open an Issue](https://github.com/ruby-grape/grape/issues)
167
167
  * [Follow us on Twitter](https://twitter.com/grapeframework)
168
168
 
169
169
  ## Grape for Enterprise
@@ -271,7 +271,7 @@ Grape's [deprecator](https://api.rubyonrails.org/v7.1.0/classes/ActiveSupport/De
271
271
  ### All
272
272
 
273
273
 
274
- 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.
275
275
 
276
276
  ```ruby
277
277
  Twitter::API.compile!
@@ -652,6 +652,27 @@ version 'v1', using: :param, parameter: 'v'
652
652
  curl http://localhost:9292/statuses/public_timeline?v=v1
653
653
 
654
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
+
655
676
  ## Describing Methods
656
677
 
657
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.
@@ -718,10 +739,13 @@ For example, for the `param_builder`, the following code could run in an initial
718
739
 
719
740
  ```ruby
720
741
  Grape.configure do |config|
721
- config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
742
+ config.param_builder = :hashie_mash
722
743
  end
723
744
  ```
724
745
 
746
+ Available parameter builders are `:hash`, `:hash_with_indifferent_access`, and `:hashie_mash`.
747
+ See [params_builder](lib/grape/params_builder).
748
+
725
749
  You can also configure a single API:
726
750
 
727
751
  ```ruby
@@ -788,7 +812,7 @@ By default parameters are available as `ActiveSupport::HashWithIndifferentAccess
788
812
 
789
813
  ```ruby
790
814
  class API < Grape::API
791
- include Grape::Extensions::Hashie::Mash::ParamBuilder
815
+ build_with :hashie_mash
792
816
 
793
817
  params do
794
818
  optional :color, type: String
@@ -802,16 +826,15 @@ The class can also be overridden on individual parameter blocks using `build_wit
802
826
 
803
827
  ```ruby
804
828
  params do
805
- build_with Grape::Extensions::Hash::ParamBuilder
829
+ build_with :hash
806
830
  optional :color, type: String
807
831
  end
808
832
  ```
809
833
 
810
- Or globally with the [Configuration](#configuration) `Grape.configure.param_builder`.
811
-
812
834
  In the example above, `params["color"]` will return `nil` since `params` is a plain `Hash`.
813
835
 
814
- Available parameter builders are `Grape::Extensions::Hash::ParamBuilder`, `Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder` and `Grape::Extensions::Hashie::Mash::ParamBuilder`.
836
+ Available parameter builders are `:hash`, `:hash_with_indifferent_access`, and `:hashie_mash`.
837
+ See [params_builder](lib/grape/params_builder).
815
838
 
816
839
  ### Declared
817
840
 
@@ -2045,7 +2068,7 @@ end
2045
2068
  ```ruby
2046
2069
  params do
2047
2070
  requires :code, type: String, length: { is: 2, message: 'code is expected to be exactly 2 characters long' }
2048
- requires :str, type: String, length: { min: 5, message: 'str is expected to be atleast 5 characters long' }
2071
+ requires :str, type: String, length: { min: 5, message: 'str is expected to be at least 5 characters long' }
2049
2072
  requires :list, type: [Integer], length: { min: 2, max: 3, message: 'list is expected to have between 2 and 3 elements' }
2050
2073
  end
2051
2074
  ```
@@ -3065,7 +3088,7 @@ end
3065
3088
  * `GET /hello.xls` with an `Accept: application/xml` header has an unrecognized extension, but the `Accept` header corresponds to a recognized format, so it will respond with XML.
3066
3089
  * `GET /hello.xls` with an `Accept: text/plain` header has an unrecognized extension *and* an unrecognized `Accept` header, so it will respond with JSON (the default format).
3067
3090
 
3068
- You can override this process explicitly by specifying `env['api.format']` in the API itself.
3091
+ You can override this process explicitly by calling `api_format` in the API itself.
3069
3092
  For example, the following API will let you upload arbitrary files and return their contents as an attachment with the correct MIME type.
3070
3093
 
3071
3094
  ```ruby
@@ -3073,7 +3096,7 @@ class Twitter::API < Grape::API
3073
3096
  post 'attachment' do
3074
3097
  filename = params[:file][:filename]
3075
3098
  content_type MIME::Types.type_for(filename)[0].to_s
3076
- env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
3099
+ api_format :binary # there's no formatter for :binary, data will be returned "as is"
3077
3100
  header 'Content-Disposition', "attachment; filename*=UTF-8''#{CGI.escape(filename)}"
3078
3101
  params[:file][:tempfile].read
3079
3102
  end
@@ -3535,8 +3558,8 @@ Please use `Route#xyz` instead.
3535
3558
 
3536
3559
  Note that difference of `Route#options` and `Route#settings`.
3537
3560
 
3538
- The `options` can be referred from your route, it should be set by specifing key and value on verb methods such as `get`, `post` and `put`.
3539
- The `settings` can also be referred from your route, but it should be set by specifing key and value on `route_setting`.
3561
+ The `options` can be referred from your route, it should be set by specifying key and value on verb methods such as `get`, `post` and `put`.
3562
+ The `settings` can also be referred from your route, but it should be set by specifying key and value on `route_setting`.
3540
3563
 
3541
3564
  ## Current Route and Endpoint
3542
3565
 
data/UPGRADING.md CHANGED
@@ -1,6 +1,80 @@
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
+
59
+ ### Upgrading to >= 2.3.0
60
+
61
+ ### `content_type` vs `api.format` inside API
62
+
63
+ Before 2.3.0, `content_type` had priority over `env['api.format']` when set in an API, which was incorrect. The priority has been flipped and `env['api.format']` will be checked first.
64
+ In addition, the function `api_format` has been added. Instead of setting `env['api.format']` directly, you can call `api_format`.
65
+ See [#2506](https://github.com/ruby-grape/grape/pull/2506) for more information.
66
+
67
+ #### Remove Deprecated Methods and Options
68
+
69
+ - Deprecated `file` method has been removed. Use `send_file` or `stream`.
70
+ See [#2500](https://github.com/ruby-grape/grape/pull/2500) for more information.
71
+
72
+ - The `except` and `proc` options have been removed from the `values` validator. Use `except_values` validator or assign `proc` directly to `values`.
73
+ See [#2501](https://github.com/ruby-grape/grape/pull/2501) for more information.
74
+
75
+ - `Passing an options hash and a block to 'desc'` deprecation has been removed. Move all hash options to block instead.
76
+ See [#2502](https://github.com/ruby-grape/grape/pull/2502) for more information.
77
+
4
78
  ### Upgrading to >= 2.2.0
5
79
 
6
80
  ### `Length` validator
@@ -64,7 +138,7 @@ When using together with `Grape::Extensions::Hash::ParamBuilder`, `route_param`
64
138
  This was a regression introduced by [#2326](https://github.com/ruby-grape/grape/pull/2326) in Grape v1.8.0.
65
139
 
66
140
  ```ruby
67
- grape.configure do |config|
141
+ Grape.configure do |config|
68
142
  config.param_builder = Grape::Extensions::Hash::ParamBuilder
69
143
  end
70
144
 
data/grape.gemspec CHANGED
@@ -21,11 +21,11 @@ Gem::Specification.new do |s|
21
21
  'rubygems_mfa_required' => 'true'
22
22
  }
23
23
 
24
- s.add_runtime_dependency 'activesupport', '>= 6'
25
- s.add_runtime_dependency 'dry-types', '>= 1.1'
26
- s.add_runtime_dependency 'mustermann-grape', '~> 1.1.0'
27
- s.add_runtime_dependency 'rack', '>= 2'
28
- s.add_runtime_dependency 'zeitwerk'
24
+ s.add_dependency 'activesupport', '>= 6.1'
25
+ s.add_dependency 'dry-types', '>= 1.1'
26
+ s.add_dependency 'mustermann-grape', '~> 1.1.0'
27
+ s.add_dependency 'rack', '>= 2'
28
+ s.add_dependency 'zeitwerk'
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']
@@ -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.respond_to?(:lazy?) && value_for_configuration.lazy?
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(Grape::Http::Headers::X_CASCADE)
168
+ headers.delete('X-Cascade')
168
169
  end
169
170
 
170
171
  [status, headers, response]
@@ -194,88 +195,52 @@ module Grape
194
195
  # will return an HTTP 405 response for any HTTP method that the resource
195
196
  # cannot handle.
196
197
  def add_head_not_allowed_methods_and_options_methods
197
- versioned_route_configs = collect_route_config_per_pattern
198
198
  # The paths we collected are prepared (cf. Path#prepare), so they
199
199
  # contain already versioning information when using path versioning.
200
+ all_routes = self.class.endpoints.map(&:routes).flatten
201
+
200
202
  # Disable versioning so adding a route won't prepend versioning
201
203
  # informations again.
202
- without_root_prefix do
203
- without_versioning do
204
- versioned_route_configs.each do |config|
205
- next if config[:options][:matching_wildchar]
206
-
207
- allowed_methods = config[:methods].dup
208
-
209
- allowed_methods |= [Rack::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Rack::GET)
210
-
211
- allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Rack::OPTIONS] | allowed_methods)
212
-
213
- config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Rack::OPTIONS)
214
-
215
- attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
216
- generate_not_allowed_method(config[:pattern], **attributes)
217
- end
218
- end
219
- end
204
+ without_root_prefix_and_versioning { collect_route_config_per_pattern(all_routes) }
220
205
  end
221
206
 
222
- def collect_route_config_per_pattern
223
- all_routes = self.class.endpoints.map(&:routes).flatten
207
+ def collect_route_config_per_pattern(all_routes)
224
208
  routes_by_regexp = all_routes.group_by(&:pattern_regexp)
225
209
 
226
210
  # Build the configuration based on the first endpoint and the collection of methods supported.
227
- routes_by_regexp.values.map do |routes|
228
- last_route = routes.last # Most of the configuration is taken from the last endpoint
229
- matching_wildchar = routes.any? { |route| route.request_method == '*' }
230
- {
231
- options: { matching_wildchar: matching_wildchar },
232
- pattern: last_route.pattern,
233
- requirements: last_route.requirements,
234
- path: last_route.origin,
235
- endpoint: last_route.app,
236
- methods: matching_wildchar ? Grape::Http::Headers::SUPPORTED_METHODS : routes.map(&:request_method)
237
- }
238
- end
239
- end
211
+ routes_by_regexp.each_value do |routes|
212
+ last_route = routes.last # Most of the configuration is taken from the last endpoint
213
+ next if routes.any? { |route| route.request_method == '*' }
240
214
 
241
- # Generate a route that returns an HTTP 405 response for a user defined
242
- # path on methods not specified
243
- def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
244
- supported_methods =
245
- if self.class.namespace_inheritable(:do_not_route_options)
246
- Grape::Http::Headers::SUPPORTED_METHODS
247
- else
248
- Grape::Http::Headers::SUPPORTED_METHODS_WITHOUT_OPTIONS
249
- end
250
- not_allowed_methods = supported_methods - allowed_methods
251
- @router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
215
+ allowed_methods = routes.map(&:request_method)
216
+ allowed_methods |= [Rack::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Rack::GET)
217
+
218
+ allow_header = self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Rack::OPTIONS] | allowed_methods
219
+ last_route.app.options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Rack::OPTIONS)
220
+
221
+ @router.associate_routes(last_route.pattern, {
222
+ endpoint: last_route.app,
223
+ allow_header: allow_header
224
+ })
225
+ end
252
226
  end
253
227
 
254
228
  # Allows definition of endpoints that ignore the versioning configuration
255
229
  # used by the rest of your API.
256
- def without_versioning(&_block)
230
+ def without_root_prefix_and_versioning
257
231
  old_version = self.class.namespace_inheritable(:version)
258
232
  old_version_options = self.class.namespace_inheritable(:version_options)
233
+ old_root_prefix = self.class.namespace_inheritable(:root_prefix)
259
234
 
260
235
  self.class.namespace_inheritable_to_nil(:version)
261
236
  self.class.namespace_inheritable_to_nil(:version_options)
237
+ self.class.namespace_inheritable_to_nil(:root_prefix)
262
238
 
263
239
  yield
264
240
 
265
241
  self.class.namespace_inheritable(:version, old_version)
266
242
  self.class.namespace_inheritable(:version_options, old_version_options)
267
- end
268
-
269
- # Allows definition of endpoints that ignore the root prefix used by the
270
- # rest of your API.
271
- def without_root_prefix(&_block)
272
- old_prefix = self.class.namespace_inheritable(:root_prefix)
273
-
274
- self.class.namespace_inheritable_to_nil(:root_prefix)
275
-
276
- yield
277
-
278
- self.class.namespace_inheritable(:root_prefix, old_prefix)
243
+ self.class.namespace_inheritable(:root_prefix, old_root_prefix)
279
244
  end
280
245
  end
281
246
  end