grape 2.1.3 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +9 -7
  4. data/UPGRADING.md +27 -0
  5. data/grape.gemspec +7 -6
  6. data/lib/grape/api/instance.rb +22 -58
  7. data/lib/grape/api.rb +2 -11
  8. data/lib/grape/content_types.rb +13 -8
  9. data/lib/grape/dsl/desc.rb +27 -24
  10. data/lib/grape/dsl/helpers.rb +7 -3
  11. data/lib/grape/dsl/inside_route.rb +18 -24
  12. data/lib/grape/dsl/parameters.rb +2 -2
  13. data/lib/grape/dsl/request_response.rb +14 -18
  14. data/lib/grape/dsl/routing.rb +5 -12
  15. data/lib/grape/endpoint.rb +90 -82
  16. data/lib/grape/error_formatter/base.rb +51 -21
  17. data/lib/grape/error_formatter/json.rb +7 -15
  18. data/lib/grape/error_formatter/jsonapi.rb +7 -0
  19. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  20. data/lib/grape/error_formatter/txt.rb +13 -20
  21. data/lib/grape/error_formatter/xml.rb +3 -13
  22. data/lib/grape/error_formatter.rb +5 -25
  23. data/lib/grape/exceptions/base.rb +18 -30
  24. data/lib/grape/exceptions/validation.rb +5 -4
  25. data/lib/grape/exceptions/validation_errors.rb +2 -2
  26. data/lib/grape/formatter/base.rb +16 -0
  27. data/lib/grape/formatter/json.rb +4 -6
  28. data/lib/grape/formatter/serializable_hash.rb +1 -1
  29. data/lib/grape/formatter/txt.rb +3 -5
  30. data/lib/grape/formatter/xml.rb +4 -6
  31. data/lib/grape/formatter.rb +7 -25
  32. data/lib/grape/http/headers.rb +1 -0
  33. data/lib/grape/locale/en.yml +1 -0
  34. data/lib/grape/middleware/base.rb +14 -13
  35. data/lib/grape/middleware/error.rb +13 -9
  36. data/lib/grape/middleware/formatter.rb +3 -3
  37. data/lib/grape/middleware/versioner/accept_version_header.rb +7 -30
  38. data/lib/grape/middleware/versioner/base.rb +82 -0
  39. data/lib/grape/middleware/versioner/header.rb +89 -10
  40. data/lib/grape/middleware/versioner/param.rb +4 -22
  41. data/lib/grape/middleware/versioner/path.rb +10 -32
  42. data/lib/grape/middleware/versioner.rb +7 -14
  43. data/lib/grape/namespace.rb +1 -1
  44. data/lib/grape/parser/base.rb +16 -0
  45. data/lib/grape/parser/json.rb +6 -8
  46. data/lib/grape/parser/jsonapi.rb +7 -0
  47. data/lib/grape/parser/xml.rb +6 -8
  48. data/lib/grape/parser.rb +5 -23
  49. data/lib/grape/path.rb +39 -56
  50. data/lib/grape/request.rb +2 -2
  51. data/lib/grape/router/base_route.rb +2 -2
  52. data/lib/grape/router/greedy_route.rb +2 -2
  53. data/lib/grape/router/pattern.rb +23 -18
  54. data/lib/grape/router/route.rb +13 -5
  55. data/lib/grape/router.rb +5 -5
  56. data/lib/grape/util/registry.rb +27 -0
  57. data/lib/grape/validations/contract_scope.rb +2 -39
  58. data/lib/grape/validations/params_scope.rb +7 -11
  59. data/lib/grape/validations/types/dry_type_coercer.rb +10 -6
  60. data/lib/grape/validations/validator_factory.rb +2 -2
  61. data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
  62. data/lib/grape/validations/validators/base.rb +5 -9
  63. data/lib/grape/validations/validators/coerce_validator.rb +1 -1
  64. data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
  65. data/lib/grape/validations/validators/default_validator.rb +1 -1
  66. data/lib/grape/validations/validators/except_values_validator.rb +1 -1
  67. data/lib/grape/validations/validators/length_validator.rb +11 -4
  68. data/lib/grape/validations/validators/regexp_validator.rb +1 -1
  69. data/lib/grape/validations/validators/values_validator.rb +15 -57
  70. data/lib/grape/validations.rb +8 -17
  71. data/lib/grape/version.rb +1 -1
  72. data/lib/grape.rb +1 -1
  73. metadata +15 -12
  74. data/lib/grape/util/accept_header_handler.rb +0 -105
  75. data/lib/grape/util/registrable.rb +0 -15
  76. 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: 172d51f08cb655e030afc83ecc06cb13ad5c72c9ad1d6f63d6f87d8b45a52772
4
- data.tar.gz: 2d946a6526a7cc848e441fac2d4e0169a0762025d34445307f3945abeeef2b72
3
+ metadata.gz: eefbc6eed618a448bcc9657c03ca73f5771f22b5f13b4c56cacb7ec7e05750ac
4
+ data.tar.gz: 6506e73d4ce49cdf54c142acee7085217bd6f55211f86b74a50d23a83ae48c02
5
5
  SHA512:
6
- metadata.gz: eb1ec3cd146f8e4a3f12314159e68177b92548fb7ea3eeac47a425addc3fa04a38c61c566ed3a0655d325bd5d24044b020449aa5702f997d746eef2c5d493928
7
- data.tar.gz: 8c44c721a9e0acbb00997ac40ea4b491c4a3c0315e6f4cc1432a2ea5cff102887e028e10df883eba0b50645219918a3fc5a3a66f36068a9d6abc81b29b75c61f
6
+ metadata.gz: 6e5dab9502a1484f267e9881b8a368cfe809f47eaf9870d14db2002c63c48863f92bbd561c54dd81b0c4e4ff416a7fa7abc121c0052576dc0d7cb088d425d282
7
+ data.tar.gz: e78c3fc83ae2a908ef510a387cd31060a13eeb23d7d209c842f2d7e64286ca21e506472fd7ca9f397e1169f7e74fc439a097c376d4ba6682b1c091009fc52acd
data/CHANGELOG.md CHANGED
@@ -1,3 +1,48 @@
1
+ ### 2.3.0 (2025-02-08)
2
+
3
+ #### Features
4
+
5
+ * [#2497](https://github.com/ruby-grape/grape/pull/2497): Update RuboCop to 1.66.1 - [@ericproulx](https://github.com/ericproulx).
6
+ * [#2500](https://github.com/ruby-grape/grape/pull/2500): Remove deprecated `file` method - [@ericproulx](https://github.com/ericproulx).
7
+ * [#2501](https://github.com/ruby-grape/grape/pull/2501): Remove deprecated `except` and `proc` options in values validator - [@ericproulx](https://github.com/ericproulx).
8
+ * [#2502](https://github.com/ruby-grape/grape/pull/2502): Remove deprecation `options` in `desc` - [@ericproulx](https://github.com/ericproulx).
9
+ * [#2512](https://github.com/ruby-grape/grape/pull/2512): Optimize hash alloc - [@ericproulx](https://github.com/ericproulx).
10
+ * [#2513](https://github.com/ruby-grape/grape/pull/2513): Optimize Grape::Path - [@ericproulx](https://github.com/ericproulx).
11
+ * [#2514](https://github.com/ruby-grape/grape/pull/2514): Add rails 8.0 to CI - [@ericproulx](https://github.com/ericproulx).
12
+ * [#2516](https://github.com/ruby-grape/grape/pull/2516): Dynamic registration for parsers, formatters, versioners - [@ericproulx](https://github.com/ericproulx).
13
+ * [#2518](https://github.com/ruby-grape/grape/pull/2518): Add ruby 3.4 to CI - [@ericproulx](https://github.com/ericproulx).
14
+
15
+ #### Fixes
16
+
17
+ * [#2504](https://github.com/ruby-grape/grape/pull/2504): Fix leaky modules in specs - [@ericproulx](https://github.com/ericproulx).
18
+ * [#2506](https://github.com/ruby-grape/grape/pull/2506): Fix fetch_formatter api_format - [@ericproulx](https://github.com/ericproulx).
19
+ * [#2507](https://github.com/ruby-grape/grape/pull/2507): Fix type: Set with values - [@nikolai-b](https://github.com/nikolai-b).
20
+ * [#2510](https://github.com/ruby-grape/grape/pull/2510): Fix ContractScope's validator inheritance - [@ericproulx](https://github.com/ericproulx).
21
+ * [#2521](https://github.com/ruby-grape/grape/pull/2521): Fixed typo in README - [@datpmt](https://github.com/datpmt).
22
+ * [#2525](https://github.com/ruby-grape/grape/pull/2525): Require logger before active_support - [@ericproulx](https://github.com/ericproulx).
23
+ * [#2524](https://github.com/ruby-grape/grape/pull/2524): Fix validators bad encoding - [@ericproulx](https://github.com/ericproulx).
24
+ * [#2530](https://github.com/ruby-grape/grape/pull/2530): Fix endpoint's status when rescue_from without a block - [@ericproulx](https://github.com/ericproulx).
25
+ * [#2529](https://github.com/ruby-grape/grape/pull/2529): Fix missing settings on mounted routes (when settings are identical) - [@Haerezis](https://github.com/Haerezis).
26
+
27
+ ### 2.2.0 (2024-09-14)
28
+
29
+ #### Features
30
+
31
+ * [#2475](https://github.com/ruby-grape/grape/pull/2475): Remove Grape::Util::Registrable - [@ericproulx](https://github.com/ericproulx).
32
+ * [#2484](https://github.com/ruby-grape/grape/pull/2484): Refactor versioner middlewares - [@ericproulx](https://github.com/ericproulx).
33
+ * [#2489](https://github.com/ruby-grape/grape/pull/2489): Add Rails 7.2 in CI workflow - [@ericproulx](https://github.com/ericproulx).
34
+ * [#2493](https://github.com/ruby-grape/grape/pull/2493): MFA required when releasing - [@ericproulx](https://github.com/ericproulx).
35
+
36
+ #### Fixes
37
+
38
+ * [#2471](https://github.com/ruby-grape/grape/pull/2471): Fix absence of original_exception and/or backtrace even if passed in error! - [@numbata](https://github.com/numbata).
39
+ * [#2478](https://github.com/ruby-grape/grape/pull/2478): Fix rescue_from with invalid response - [@ericproulx](https://github.com/ericproulx).
40
+ * [#2480](https://github.com/ruby-grape/grape/pull/2480): Fix rescue_from ValidationErrors exception - [@numbata](https://github.com/numbata).
41
+ * [#2464](https://github.com/ruby-grape/grape/pull/2464): The `length` validator only takes effect for parameters with types that support `#length` method - [@OuYangJinTing](https://github.com/OuYangJinTing).
42
+ * [#2485](https://github.com/ruby-grape/grape/pull/2485): Add `is:` param to length validator - [@dakad](https://github.com/dakad).
43
+ * [#2492](https://github.com/ruby-grape/grape/pull/2492): Fix `Grape::Endpoint#inspect` method - [@ericproulx](https://github.com/ericproulx).
44
+ * [#2496](https://github.com/ruby-grape/grape/pull/2496): Reduce object allocation when compiling - [@ericproulx](https://github.com/ericproulx).
45
+
1
46
  ### 2.1.3 (2024-07-13)
2
47
 
3
48
  #### Fixes
data/README.md CHANGED
@@ -157,7 +157,7 @@ Grape is a REST-like API framework for Ruby. It's designed to run on Rack or com
157
157
 
158
158
  ## Stable Release
159
159
 
160
- You're reading the documentation for the stable release of Grape, **2.1.3**.
160
+ You're reading the documentation for the next release of Grape, 2.3.0.
161
161
  Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
162
162
 
163
163
  ## Project Resources
@@ -1713,10 +1713,11 @@ end
1713
1713
 
1714
1714
  Parameters with types that support `#length` method can be restricted to have a specific length with the `:length` option.
1715
1715
 
1716
- The validator accepts `:min` or `:max` or both options to validate that the value of the parameter is within the given limits.
1716
+ The validator accepts `:min` or `:max` or both options or only `:is` to validate that the value of the parameter is within the given limits.
1717
1717
 
1718
1718
  ```ruby
1719
1719
  params do
1720
+ requires :code, type: String, length: { is: 2 }
1720
1721
  requires :str, type: String, length: { min: 3 }
1721
1722
  requires :list, type: [Integer], length: { min: 3, max: 5 }
1722
1723
  requires :hash, type: Hash, length: { max: 5 }
@@ -2044,7 +2045,8 @@ end
2044
2045
 
2045
2046
  ```ruby
2046
2047
  params do
2047
- requires :str, type: String, length: { min: 5, message: 'str is expected to be atleast 5 characters long' }
2048
+ requires :code, type: String, length: { is: 2, message: 'code is expected to be exactly 2 characters long' }
2049
+ requires :str, type: String, length: { min: 5, message: 'str is expected to be at least 5 characters long' }
2048
2050
  requires :list, type: [Integer], length: { min: 2, max: 3, message: 'list is expected to have between 2 and 3 elements' }
2049
2051
  end
2050
2052
  ```
@@ -3064,7 +3066,7 @@ end
3064
3066
  * `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.
3065
3067
  * `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).
3066
3068
 
3067
- You can override this process explicitly by specifying `env['api.format']` in the API itself.
3069
+ You can override this process explicitly by calling `api_format` in the API itself.
3068
3070
  For example, the following API will let you upload arbitrary files and return their contents as an attachment with the correct MIME type.
3069
3071
 
3070
3072
  ```ruby
@@ -3072,7 +3074,7 @@ class Twitter::API < Grape::API
3072
3074
  post 'attachment' do
3073
3075
  filename = params[:file][:filename]
3074
3076
  content_type MIME::Types.type_for(filename)[0].to_s
3075
- env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
3077
+ api_format :binary # there's no formatter for :binary, data will be returned "as is"
3076
3078
  header 'Content-Disposition', "attachment; filename*=UTF-8''#{CGI.escape(filename)}"
3077
3079
  params[:file][:tempfile].read
3078
3080
  end
@@ -3534,8 +3536,8 @@ Please use `Route#xyz` instead.
3534
3536
 
3535
3537
  Note that difference of `Route#options` and `Route#settings`.
3536
3538
 
3537
- 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`.
3538
- The `settings` can also be referred from your route, but it should be set by specifing key and value on `route_setting`.
3539
+ 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`.
3540
+ The `settings` can also be referred from your route, but it should be set by specifying key and value on `route_setting`.
3539
3541
 
3540
3542
  ## Current Route and Endpoint
3541
3543
 
data/UPGRADING.md CHANGED
@@ -1,6 +1,33 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 2.3.0
5
+
6
+ ### `content_type` vs `api.format` inside API
7
+
8
+ 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.
9
+ In addition, the function `api_format` has been added. Instead of setting `env['api.format']` directly, you can call `api_format`.
10
+ See [#2506](https://github.com/ruby-grape/grape/pull/2506) for more information.
11
+
12
+ #### Remove Deprecated Methods and Options
13
+
14
+ - Deprecated `file` method has been removed. Use `send_file` or `stream`.
15
+ See [#2500](https://github.com/ruby-grape/grape/pull/2500) for more information.
16
+
17
+ - The `except` and `proc` options have been removed from the `values` validator. Use `except_values` validator or assign `proc` directly to `values`.
18
+ See [#2501](https://github.com/ruby-grape/grape/pull/2501) for more information.
19
+
20
+ - `Passing an options hash and a block to 'desc'` deprecation has been removed. Move all hash options to block instead.
21
+ See [#2502](https://github.com/ruby-grape/grape/pull/2502) for more information.
22
+
23
+ ### Upgrading to >= 2.2.0
24
+
25
+ ### `Length` validator
26
+
27
+ After Grape 2.2.0, `length` validator will only take effect for parameters with types that support `#length` method, will not throw `ArgumentError` exception.
28
+
29
+ See [#2464](https://github.com/ruby-grape/grape/pull/2464) for more information.
30
+
4
31
  ### Upgrading to >= 2.1.0
5
32
 
6
33
  #### Optional Builder
data/grape.gemspec CHANGED
@@ -17,14 +17,15 @@ Gem::Specification.new do |s|
17
17
  'bug_tracker_uri' => 'https://github.com/ruby-grape/grape/issues',
18
18
  'changelog_uri' => "https://github.com/ruby-grape/grape/blob/v#{s.version}/CHANGELOG.md",
19
19
  'documentation_uri' => "https://www.rubydoc.info/gems/grape/#{s.version}",
20
- 'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}"
20
+ 'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}",
21
+ 'rubygems_mfa_required' => 'true'
21
22
  }
22
23
 
23
- s.add_runtime_dependency 'activesupport', '>= 6'
24
- s.add_runtime_dependency 'dry-types', '>= 1.1'
25
- s.add_runtime_dependency 'mustermann-grape', '~> 1.1.0'
26
- s.add_runtime_dependency 'rack', '>= 2'
27
- s.add_runtime_dependency 'zeitwerk'
24
+ s.add_dependency 'activesupport', '>= 6'
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'
28
29
 
29
30
  s.files = Dir['lib/**/*', 'CHANGELOG.md', 'CONTRIBUTING.md', 'README.md', 'grape.png', 'UPGRADING.md', 'LICENSE', 'grape.gemspec']
30
31
  s.require_paths = ['lib']
@@ -194,88 +194,52 @@ module Grape
194
194
  # will return an HTTP 405 response for any HTTP method that the resource
195
195
  # cannot handle.
196
196
  def add_head_not_allowed_methods_and_options_methods
197
- versioned_route_configs = collect_route_config_per_pattern
198
197
  # The paths we collected are prepared (cf. Path#prepare), so they
199
198
  # contain already versioning information when using path versioning.
199
+ all_routes = self.class.endpoints.map(&:routes).flatten
200
+
200
201
  # Disable versioning so adding a route won't prepend versioning
201
202
  # 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
203
+ without_root_prefix_and_versioning { collect_route_config_per_pattern(all_routes) }
220
204
  end
221
205
 
222
- def collect_route_config_per_pattern
223
- all_routes = self.class.endpoints.map(&:routes).flatten
206
+ def collect_route_config_per_pattern(all_routes)
224
207
  routes_by_regexp = all_routes.group_by(&:pattern_regexp)
225
208
 
226
209
  # 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
210
+ routes_by_regexp.each_value do |routes|
211
+ last_route = routes.last # Most of the configuration is taken from the last endpoint
212
+ next if routes.any? { |route| route.request_method == '*' }
240
213
 
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)
214
+ allowed_methods = routes.map(&:request_method)
215
+ allowed_methods |= [Rack::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Rack::GET)
216
+
217
+ allow_header = self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Rack::OPTIONS] | allowed_methods
218
+ last_route.app.options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Rack::OPTIONS)
219
+
220
+ @router.associate_routes(last_route.pattern, {
221
+ endpoint: last_route.app,
222
+ allow_header: allow_header
223
+ })
224
+ end
252
225
  end
253
226
 
254
227
  # Allows definition of endpoints that ignore the versioning configuration
255
228
  # used by the rest of your API.
256
- def without_versioning(&_block)
229
+ def without_root_prefix_and_versioning
257
230
  old_version = self.class.namespace_inheritable(:version)
258
231
  old_version_options = self.class.namespace_inheritable(:version_options)
232
+ old_root_prefix = self.class.namespace_inheritable(:root_prefix)
259
233
 
260
234
  self.class.namespace_inheritable_to_nil(:version)
261
235
  self.class.namespace_inheritable_to_nil(:version_options)
236
+ self.class.namespace_inheritable_to_nil(:root_prefix)
262
237
 
263
238
  yield
264
239
 
265
240
  self.class.namespace_inheritable(:version, old_version)
266
241
  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)
242
+ self.class.namespace_inheritable(:root_prefix, old_root_prefix)
279
243
  end
280
244
  end
281
245
  end
data/lib/grape/api.rb CHANGED
@@ -40,7 +40,7 @@ module Grape
40
40
  # an instance that will be used to create the set up but will not be mounted
41
41
  def initial_setup(base_instance_parent)
42
42
  @instances = []
43
- @setup = Set.new
43
+ @setup = []
44
44
  @base_parent = base_instance_parent
45
45
  @base_instance = mount_instance
46
46
  end
@@ -78,20 +78,11 @@ module Grape
78
78
  instance_for_rack.call(...)
79
79
  end
80
80
 
81
- # Alleviates problems with autoloading by tring to search for the constant
82
- def const_missing(*args)
83
- if base_instance.const_defined?(*args)
84
- base_instance.const_get(*args)
85
- else
86
- super
87
- end
88
- end
89
-
90
81
  # The remountable class can have a configuration hash to provide some dynamic class-level variables.
91
82
  # For instance, a description could be done using: `desc configuration[:description]` if it may vary
92
83
  # depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
93
84
  # too much, you may actually want to provide a new API rather than remount it.
94
- def mount_instance(**opts)
85
+ def mount_instance(opts = {})
95
86
  instance = Class.new(@base_parent)
96
87
  instance.configuration = Grape::Util::EndpointConfiguration.new(opts[:configuration] || {})
97
88
  instance.base = self
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Grape
4
4
  module ContentTypes
5
- extend Util::Registrable
5
+ module_function
6
6
 
7
7
  # Content types are listed in order of preference.
8
- CONTENT_TYPES = {
8
+ DEFAULTS = {
9
9
  xml: 'application/xml',
10
10
  serializable_hash: 'application/json',
11
11
  json: 'application/json',
@@ -13,13 +13,18 @@ module Grape
13
13
  txt: 'text/plain'
14
14
  }.freeze
15
15
 
16
- class << self
17
- def content_types_for_settings(settings)
18
- settings&.inject(:merge!)
19
- end
16
+ MIME_TYPES = Grape::ContentTypes::DEFAULTS.except(:serializable_hash).invert.freeze
17
+
18
+ def content_types_for(from_settings)
19
+ from_settings.presence || DEFAULTS
20
+ end
21
+
22
+ def mime_types_for(from_settings)
23
+ return MIME_TYPES if from_settings == Grape::ContentTypes::DEFAULTS
20
24
 
21
- def content_types_for(from_settings)
22
- from_settings.presence || Grape::ContentTypes::CONTENT_TYPES.merge(default_elements)
25
+ from_settings.each_with_object({}) do |(k, v), types_without_params|
26
+ # remove optional parameter
27
+ types_without_params[v.split(';', 2).first] = k
23
28
  end
24
29
  end
25
30
  end
@@ -70,33 +70,23 @@ module Grape
70
70
  # # ...
71
71
  # end
72
72
  #
73
- def desc(description, options = {}, &config_block)
74
- if config_block
75
- endpoint_configuration = if defined?(configuration)
76
- # When the instance is mounted - the configuration is executed on mount time
77
- if configuration.respond_to?(:evaluate)
78
- configuration.evaluate
79
- # Within `given` or `mounted blocks` the configuration is already evaluated
80
- elsif configuration.is_a?(Hash)
81
- configuration
82
- end
83
- end
84
- endpoint_configuration ||= {}
85
- config_class = desc_container(endpoint_configuration)
73
+ def desc(description, options = nil, &config_block)
74
+ opts =
75
+ if config_block
76
+ desc_container(endpoint_configuration).then do |config_class|
77
+ config_class.configure do
78
+ description(description)
79
+ end
86
80
 
87
- config_class.configure do
88
- description description
81
+ config_class.configure(&config_block)
82
+ config_class.settings
83
+ end
84
+ else
85
+ options&.merge(description: description) || { description: description }
89
86
  end
90
87
 
91
- config_class.configure(&config_block)
92
- Grape.deprecator.warn('Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.') if options.any?
93
- options = config_class.settings
94
- else
95
- options = options.merge(description: description)
96
- end
97
-
98
- namespace_setting :description, options
99
- route_setting :description, options
88
+ namespace_setting :description, opts
89
+ route_setting :description, opts
100
90
  end
101
91
 
102
92
  # Returns an object which configures itself via an instance-context DSL.
@@ -116,6 +106,19 @@ module Grape
116
106
  end
117
107
  end
118
108
  end
109
+
110
+ private
111
+
112
+ def endpoint_configuration
113
+ return {} unless defined?(configuration)
114
+
115
+ if configuration.respond_to?(:evaluate)
116
+ configuration.evaluate
117
+ # Within `given` or `mounted blocks` the configuration is already evaluated
118
+ elsif configuration.is_a?(Hash)
119
+ configuration
120
+ end
121
+ end
119
122
  end
120
123
  end
121
124
  end
@@ -33,18 +33,22 @@ module Grape
33
33
  # end
34
34
  #
35
35
  def helpers(*new_modules, &block)
36
- include_new_modules(new_modules) if new_modules.any?
37
- include_block(block) if block
36
+ include_new_modules(new_modules)
37
+ include_block(block)
38
38
  include_all_in_scope if !block && new_modules.empty?
39
39
  end
40
40
 
41
41
  protected
42
42
 
43
43
  def include_new_modules(modules)
44
+ return if modules.empty?
45
+
44
46
  modules.each { |mod| make_inclusion(mod) }
45
47
  end
46
48
 
47
49
  def include_block(block)
50
+ return unless block
51
+
48
52
  Module.new.tap do |mod|
49
53
  make_inclusion(mod) { mod.class_eval(&block) }
50
54
  end
@@ -58,7 +62,7 @@ module Grape
58
62
 
59
63
  def include_all_in_scope
60
64
  Module.new.tap do |mod|
61
- namespace_stackable(:helpers).each { |mod_to_include| mod.send :include, mod_to_include }
65
+ namespace_stackable(:helpers).each { |mod_to_include| mod.include mod_to_include }
62
66
  change!
63
67
  end
64
68
  end
@@ -26,8 +26,8 @@ module Grape
26
26
  # has completed
27
27
  module PostBeforeFilter
28
28
  def declared(passed_params, options = {}, declared_params = nil, params_nested_path = [])
29
- options = options.reverse_merge(include_missing: true, include_parent_namespaces: true, evaluate_given: false)
30
- declared_params ||= optioned_declared_params(**options)
29
+ options.reverse_merge!(include_missing: true, include_parent_namespaces: true, evaluate_given: false)
30
+ declared_params ||= optioned_declared_params(options[:include_parent_namespaces])
31
31
 
32
32
  res = if passed_params.is_a?(Array)
33
33
  declared_array(passed_params, options, declared_params, params_nested_path)
@@ -120,8 +120,8 @@ module Grape
120
120
  options[:stringify] ? declared_param.to_s : declared_param.to_sym
121
121
  end
122
122
 
123
- def optioned_declared_params(**options)
124
- declared_params = if options[:include_parent_namespaces]
123
+ def optioned_declared_params(include_parent_namespaces)
124
+ declared_params = if include_parent_namespaces
125
125
  # Declared params including parent namespaces
126
126
  route_setting(:declared_params)
127
127
  else
@@ -170,7 +170,12 @@ module Grape
170
170
  def error!(message, status = nil, additional_headers = nil, backtrace = nil, original_exception = nil)
171
171
  status = self.status(status || namespace_inheritable(:default_error_status))
172
172
  headers = additional_headers.present? ? header.merge(additional_headers) : header
173
- throw :error, message: message, status: status, headers: headers, backtrace: backtrace, original_exception: original_exception
173
+ throw :error,
174
+ message: message,
175
+ status: status,
176
+ headers: headers,
177
+ backtrace: backtrace,
178
+ original_exception: original_exception
174
179
  end
175
180
 
176
181
  # Creates a Rack response based on the provided message, status, and headers.
@@ -194,10 +199,9 @@ module Grape
194
199
  # Redirect to a new url.
195
200
  #
196
201
  # @param url [String] The url to be redirect.
197
- # @param options [Hash] The options used when redirect.
198
- # :permanent, default false.
199
- # :body, default a short message including the URL.
200
- def redirect(url, permanent: false, body: nil, **_options)
202
+ # @param permanent [Boolean] default false.
203
+ # @param body default a short message including the URL.
204
+ def redirect(url, permanent: false, body: nil)
201
205
  body_message = body
202
206
  if permanent
203
207
  status 301
@@ -300,20 +304,6 @@ module Grape
300
304
  body false
301
305
  end
302
306
 
303
- # Deprecated method to send files to the client. Use `sendfile` or `stream`
304
- def file(value = nil)
305
- if value.is_a?(String)
306
- Grape.deprecator.warn('Use sendfile or stream to send files.')
307
- sendfile(value)
308
- elsif !value.is_a?(NilClass)
309
- Grape.deprecator.warn('Use stream to use a Stream object.')
310
- stream(value)
311
- else
312
- Grape.deprecator.warn('Use sendfile or stream to send files.')
313
- sendfile
314
- end
315
- end
316
-
317
307
  # Allows you to send a file to the client via sendfile.
318
308
  #
319
309
  # @example
@@ -461,7 +451,11 @@ module Grape
461
451
  end
462
452
 
463
453
  def http_version
464
- env['HTTP_VERSION'] || env[Rack::SERVER_PROTOCOL]
454
+ env.fetch(Grape::Http::Headers::HTTP_VERSION) { env[Rack::SERVER_PROTOCOL] }
455
+ end
456
+
457
+ def api_format(format)
458
+ env[Grape::Env::API_FORMAT] = format
465
459
  end
466
460
 
467
461
  def context
@@ -136,7 +136,7 @@ module Grape
136
136
  require_required_and_optional_fields(attrs.first, opts)
137
137
  else
138
138
  validate_attributes(attrs, opts, &block)
139
- block ? new_scope(orig_attrs, &block) : push_declared_params(attrs, **opts.slice(:as))
139
+ block ? new_scope(orig_attrs, &block) : push_declared_params(attrs, opts.slice(:as))
140
140
  end
141
141
  end
142
142
 
@@ -162,7 +162,7 @@ module Grape
162
162
  else
163
163
  validate_attributes(attrs, opts, &block)
164
164
 
165
- block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
165
+ block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, opts.slice(:as))
166
166
  end
167
167
  end
168
168
 
@@ -17,18 +17,16 @@ module Grape
17
17
  # Specify the format for the API's serializers.
18
18
  # May be `:json`, `:xml`, `:txt`, etc.
19
19
  def format(new_format = nil)
20
- if new_format
21
- namespace_inheritable(:format, new_format.to_sym)
22
- # define the default error formatters
23
- namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(new_format, **{}))
24
- # define a single mime type
25
- mime_type = content_types[new_format.to_sym]
26
- raise Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type
27
-
28
- namespace_stackable(:content_types, new_format.to_sym => mime_type)
29
- else
30
- namespace_inheritable(:format)
31
- end
20
+ return namespace_inheritable(:format) unless new_format
21
+
22
+ symbolic_new_format = new_format.to_sym
23
+ namespace_inheritable(:format, symbolic_new_format)
24
+ namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(symbolic_new_format))
25
+
26
+ content_type = content_types[symbolic_new_format]
27
+ raise Grape::Exceptions::MissingMimeType.new(new_format) unless content_type
28
+
29
+ namespace_stackable(:content_types, symbolic_new_format => content_type)
32
30
  end
33
31
 
34
32
  # Specify a custom formatter for a content-type.
@@ -43,12 +41,10 @@ module Grape
43
41
 
44
42
  # Specify a default error formatter.
45
43
  def default_error_formatter(new_formatter_name = nil)
46
- if new_formatter_name
47
- new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name, **{})
48
- namespace_inheritable(:default_error_formatter, new_formatter)
49
- else
50
- namespace_inheritable(:default_error_formatter)
51
- end
44
+ return namespace_inheritable(:default_error_formatter) unless new_formatter_name
45
+
46
+ new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name)
47
+ namespace_inheritable(:default_error_formatter, new_formatter)
52
48
  end
53
49
 
54
50
  def error_formatter(format, options)
@@ -175,19 +175,12 @@ module Grape
175
175
  # end
176
176
  # end
177
177
  def namespace(space = nil, options = {}, &block)
178
- @namespace_description = nil unless instance_variable_defined?(:@namespace_description) && @namespace_description
179
-
180
- if space || block
181
- within_namespace do
182
- previous_namespace_description = @namespace_description
183
- @namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
184
- nest(block) do
185
- namespace_stackable(:namespace, Namespace.new(space, **options)) if space
186
- end
187
- @namespace_description = previous_namespace_description
178
+ return Namespace.joined_space_path(namespace_stackable(:namespace)) unless space || block
179
+
180
+ within_namespace do
181
+ nest(block) do
182
+ namespace_stackable(:namespace, Namespace.new(space, options)) if space
188
183
  end
189
- else
190
- Namespace.joined_space_path(namespace_stackable(:namespace))
191
184
  end
192
185
  end
193
186