grape 2.0.0 → 2.2.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +96 -1
  3. data/README.md +364 -317
  4. data/UPGRADING.md +205 -7
  5. data/grape.gemspec +7 -7
  6. data/lib/grape/api/instance.rb +14 -11
  7. data/lib/grape/api.rb +19 -10
  8. data/lib/grape/content_types.rb +13 -10
  9. data/lib/grape/cookies.rb +2 -1
  10. data/lib/grape/dry_types.rb +0 -2
  11. data/lib/grape/dsl/desc.rb +22 -20
  12. data/lib/grape/dsl/headers.rb +1 -1
  13. data/lib/grape/dsl/helpers.rb +7 -3
  14. data/lib/grape/dsl/inside_route.rb +51 -15
  15. data/lib/grape/dsl/parameters.rb +5 -4
  16. data/lib/grape/dsl/request_response.rb +14 -18
  17. data/lib/grape/dsl/routing.rb +20 -4
  18. data/lib/grape/dsl/validations.rb +13 -0
  19. data/lib/grape/endpoint.rb +43 -35
  20. data/lib/grape/{util/env.rb → env.rb} +0 -5
  21. data/lib/grape/error_formatter/json.rb +13 -4
  22. data/lib/grape/error_formatter/txt.rb +11 -10
  23. data/lib/grape/error_formatter.rb +13 -25
  24. data/lib/grape/exceptions/base.rb +3 -3
  25. data/lib/grape/exceptions/validation.rb +0 -2
  26. data/lib/grape/exceptions/validation_array_errors.rb +1 -0
  27. data/lib/grape/exceptions/validation_errors.rb +2 -4
  28. data/lib/grape/extensions/hash.rb +5 -1
  29. data/lib/grape/formatter.rb +15 -25
  30. data/lib/grape/http/headers.rb +18 -34
  31. data/lib/grape/{util/json.rb → json.rb} +1 -3
  32. data/lib/grape/locale/en.yml +4 -0
  33. data/lib/grape/middleware/auth/base.rb +0 -2
  34. data/lib/grape/middleware/auth/dsl.rb +0 -2
  35. data/lib/grape/middleware/base.rb +14 -15
  36. data/lib/grape/middleware/error.rb +61 -54
  37. data/lib/grape/middleware/formatter.rb +18 -15
  38. data/lib/grape/middleware/globals.rb +1 -3
  39. data/lib/grape/middleware/stack.rb +4 -5
  40. data/lib/grape/middleware/versioner/accept_version_header.rb +8 -33
  41. data/lib/grape/middleware/versioner/header.rb +62 -123
  42. data/lib/grape/middleware/versioner/param.rb +5 -23
  43. data/lib/grape/middleware/versioner/path.rb +11 -33
  44. data/lib/grape/middleware/versioner.rb +5 -14
  45. data/lib/grape/middleware/versioner_helpers.rb +75 -0
  46. data/lib/grape/namespace.rb +3 -4
  47. data/lib/grape/parser.rb +8 -24
  48. data/lib/grape/path.rb +24 -29
  49. data/lib/grape/request.rb +4 -12
  50. data/lib/grape/router/base_route.rb +39 -0
  51. data/lib/grape/router/greedy_route.rb +20 -0
  52. data/lib/grape/router/pattern.rb +39 -30
  53. data/lib/grape/router/route.rb +22 -59
  54. data/lib/grape/router.rb +32 -37
  55. data/lib/grape/util/base_inheritable.rb +4 -4
  56. data/lib/grape/util/cache.rb +0 -3
  57. data/lib/grape/util/endpoint_configuration.rb +1 -1
  58. data/lib/grape/util/header.rb +13 -0
  59. data/lib/grape/util/inheritable_values.rb +0 -2
  60. data/lib/grape/util/lazy/block.rb +29 -0
  61. data/lib/grape/util/lazy/object.rb +45 -0
  62. data/lib/grape/util/lazy/value.rb +38 -0
  63. data/lib/grape/util/lazy/value_array.rb +21 -0
  64. data/lib/grape/util/lazy/value_enumerable.rb +34 -0
  65. data/lib/grape/util/lazy/value_hash.rb +21 -0
  66. data/lib/grape/util/media_type.rb +70 -0
  67. data/lib/grape/util/reverse_stackable_values.rb +1 -6
  68. data/lib/grape/util/stackable_values.rb +1 -6
  69. data/lib/grape/util/strict_hash_configuration.rb +3 -3
  70. data/lib/grape/validations/attributes_doc.rb +38 -36
  71. data/lib/grape/validations/attributes_iterator.rb +1 -0
  72. data/lib/grape/validations/contract_scope.rb +71 -0
  73. data/lib/grape/validations/params_scope.rb +22 -19
  74. data/lib/grape/validations/types/array_coercer.rb +0 -2
  75. data/lib/grape/validations/types/build_coercer.rb +69 -71
  76. data/lib/grape/validations/types/dry_type_coercer.rb +1 -11
  77. data/lib/grape/validations/types/json.rb +0 -2
  78. data/lib/grape/validations/types/primitive_coercer.rb +0 -2
  79. data/lib/grape/validations/types/set_coercer.rb +0 -3
  80. data/lib/grape/validations/types.rb +0 -3
  81. data/lib/grape/validations/validators/base.rb +1 -0
  82. data/lib/grape/validations/validators/default_validator.rb +5 -1
  83. data/lib/grape/validations/validators/exactly_one_of_validator.rb +1 -1
  84. data/lib/grape/validations/validators/length_validator.rb +49 -0
  85. data/lib/grape/validations/validators/values_validator.rb +6 -1
  86. data/lib/grape/validations.rb +3 -7
  87. data/lib/grape/version.rb +1 -1
  88. data/lib/grape/{util/xml.rb → xml.rb} +1 -1
  89. data/lib/grape.rb +30 -274
  90. metadata +31 -38
  91. data/lib/grape/eager_load.rb +0 -20
  92. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
  93. data/lib/grape/router/attribute_translator.rb +0 -63
  94. data/lib/grape/util/lazy_block.rb +0 -27
  95. data/lib/grape/util/lazy_object.rb +0 -43
  96. data/lib/grape/util/lazy_value.rb +0 -91
  97. data/lib/grape/util/registrable.rb +0 -15
data/UPGRADING.md CHANGED
@@ -1,6 +1,207 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 2.2.0
5
+
6
+ ### `Length` validator
7
+
8
+ After Grape 2.2.0, `length` validator will only take effect for parameters with types that support `#length` method, will not throw `ArgumentError` exception.
9
+
10
+ See [#2464](https://github.com/ruby-grape/grape/pull/2464) for more information.
11
+
12
+ ### Upgrading to >= 2.1.0
13
+
14
+ #### Optional Builder
15
+
16
+ The `builder` gem dependency has been made optional as it's only used when generating XML. If your code does, add `builder` to your `Gemfile`.
17
+
18
+ See [#2445](https://github.com/ruby-grape/grape/pull/2445) for more information.
19
+
20
+ #### Deep Merging of Parameter Attributes
21
+
22
+ Grape now uses `deep_merge` to combine parameter attributes within the `with` method. Previously, attributes defined at the parameter level would override those defined at the group level.
23
+ With deep merge, attributes are now combined, allowing for more detailed and nuanced API specifications.
24
+
25
+ For example:
26
+
27
+ ```ruby
28
+ with(documentation: { in: 'body' }) do
29
+ optional :vault, documentation: { default: 33 }
30
+ end
31
+ ```
32
+
33
+ Before it was equivalent to:
34
+
35
+ ```ruby
36
+ optional :vault, documentation: { default: 33 }
37
+ ```
38
+
39
+ After it is an equivalent of:
40
+
41
+ ```ruby
42
+ optional :vault, documentation: { in: 'body', default: 33 }
43
+ ```
44
+
45
+ See [#2432](https://github.com/ruby-grape/grape/pull/2432) for more information.
46
+
47
+ #### Zeitwerk
48
+
49
+ Grape's autoloader has been updated and it's now based on [Zeitwerk](https://github.com/fxn/zeitwerk).
50
+ If you MP (Monkey Patch) some files and you're not following the [file structure](https://github.com/fxn/zeitwerk?tab=readme-ov-file#file-structure), you might end up with a Zeitwerk error.
51
+
52
+ See [#2363](https://github.com/ruby-grape/grape/pull/2363) for more information.
53
+
54
+ #### Changes in rescue_from
55
+
56
+ The `rack_response` method has been deprecated and the `error_response` method has been removed. Use `error!` instead.
57
+
58
+ See [#2414](https://github.com/ruby-grape/grape/pull/2414) for more information.
59
+
60
+ #### Change in parameters precedence
61
+
62
+ When using together with `Grape::Extensions::Hash::ParamBuilder`, `route_param` takes higher precedence over a regular parameter defined with same name, which now matches the default param builder behavior.
63
+
64
+ This was a regression introduced by [#2326](https://github.com/ruby-grape/grape/pull/2326) in Grape v1.8.0.
65
+
66
+ ```ruby
67
+ grape.configure do |config|
68
+ config.param_builder = Grape::Extensions::Hash::ParamBuilder
69
+ end
70
+
71
+ params do
72
+ requires :foo, type: String
73
+ end
74
+ route_param :foo do
75
+ get do
76
+ { value: params[:foo] }
77
+ end
78
+ end
79
+ ```
80
+
81
+ Request:
82
+
83
+ ```bash
84
+ curl -X POST -H "Content-Type: application/json" localhost:9292/bar -d '{"foo": "baz"}'
85
+ ```
86
+
87
+ Response prior to v1.8.0:
88
+
89
+ ```json
90
+ {
91
+ "value": "bar"
92
+ }
93
+ ```
94
+
95
+ v1.8.0..v2.0.0:
96
+
97
+ ```json
98
+ {
99
+ "value": "baz"
100
+ }
101
+ ```
102
+
103
+ v2.1.0+:
104
+
105
+ ```json
106
+ {
107
+ "value": "bar"
108
+ }
109
+ ```
110
+
111
+ See [#2378](https://github.com/ruby-grape/grape/pull/2378) for details.
112
+
113
+ #### Grape::Router::Route.route_xxx methods have been removed
114
+
115
+ - `route_method` is accessible through `request_method`
116
+ - `route_path` is accessible through `path`
117
+ - Any other `route_xyz` are accessible through `options[xyz]`
118
+
119
+ #### Instance variables scope
120
+
121
+ Due to the changes done in [#2377](https://github.com/ruby-grape/grape/pull/2377), the instance variables defined inside each of the endpoints (or inside a `before` validator) are now accessible inside the `rescue_from`. The behavior of the instance variables was undefined until `2.1.0`.
122
+
123
+ If you were using the same variable name defined inside an endpoint or `before` validator inside a `rescue_from` handler, you need to take in mind that you can start getting different values or you can be overriding values.
124
+
125
+ Before:
126
+ ```ruby
127
+ class TwitterAPI < Grape::API
128
+ before do
129
+ @var = 1
130
+ end
131
+
132
+ get '/' do
133
+ puts @var # => 1
134
+ raise
135
+ end
136
+
137
+ rescue_from :all do
138
+ puts @var # => nil
139
+ end
140
+ end
141
+ ```
142
+
143
+ After:
144
+ ```ruby
145
+ class TwitterAPI < Grape::API
146
+ before do
147
+ @var = 1
148
+ end
149
+
150
+ get '/' do
151
+ puts @var # => 1
152
+ raise
153
+ end
154
+
155
+ rescue_from :all do
156
+ puts @var # => 1
157
+ end
158
+ end
159
+ ```
160
+
161
+ #### Recognizing Path
162
+
163
+ Grape now considers the types of the configured `route_params` in order to determine the endpoint that matches with the performed request.
164
+
165
+ So taking into account this `Grape::API` class
166
+
167
+ ```ruby
168
+ class Books < Grape::API
169
+ resource :books do
170
+ route_param :id, type: Integer do
171
+ # GET /books/:id
172
+ get do
173
+ #...
174
+ end
175
+ end
176
+
177
+ resource :share do
178
+ # POST /books/share
179
+ post do
180
+ # ....
181
+ end
182
+ end
183
+ end
184
+ end
185
+ ```
186
+
187
+ Before:
188
+ ```ruby
189
+ API.recognize_path '/books/1' # => /books/:id
190
+ API.recognize_path '/books/share' # => /books/:id
191
+ API.recognize_path '/books/other' # => /books/:id
192
+ ```
193
+
194
+ After:
195
+ ```ruby
196
+ API.recognize_path '/books/1' # => /books/:id
197
+ API.recognize_path '/books/share' # => /books/share
198
+ API.recognize_path '/books/other' # => nil
199
+ ```
200
+
201
+ This implies that before this changes, when you performed `/books/other` and it matched with the `/books/:id` endpoint, you get a `400 Bad Request` response because the type of the provided `:id` param was not an `Integer`. However, after upgrading to version `2.1.0` you will get a `404 Not Found` response, because there is not a defined endpoint that matches with `/books/other`.
202
+
203
+ See [#2379](https://github.com/ruby-grape/grape/pull/2379) for more information.
204
+
4
205
  ### Upgrading to >= 2.0.0
5
206
 
6
207
  #### Headers
@@ -19,7 +220,7 @@ If you are using Rack 3 in your application then the headers will be set to:
19
220
  { "content-type" => "application/json", "secret-password" => "foo"}
20
221
  ```
21
222
 
22
- This means if you are checking for header values in your application, you would need to change your code to use downcased keys.
223
+ This means if you are checking for header values in your application, you would need to change your code to use downcased keys.
23
224
 
24
225
  ```ruby
25
226
  get do
@@ -474,8 +675,7 @@ end
474
675
 
475
676
  ##### `name` (and other caveats) of the mounted API
476
677
 
477
- After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
478
- which inherit from `Grape::API::Instance`.
678
+ After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class which inherit from `Grape::API::Instance`.
479
679
 
480
680
  What this means in practice, is:
481
681
 
@@ -855,8 +1055,7 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
855
1055
 
856
1056
  #### Bypasses formatters when status code indicates no content
857
1057
 
858
- To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now
859
- be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
1058
+ To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
860
1059
 
861
1060
  See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
862
1061
 
@@ -1297,8 +1496,7 @@ As replacement can be used
1297
1496
  * `Grape::Middleware::Auth::Digest` => [`Rack::Auth::Digest::MD5`](https://github.com/rack/rack/blob/master/lib/rack/auth/digest/md5.rb)
1298
1497
  * `Grape::Middleware::Auth::OAuth2` => [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2)
1299
1498
 
1300
- If this is not possible you can extract the middleware files from [grape v0.7.0](https://github.com/ruby-grape/grape/tree/v0.7.0/lib/grape/middleware/auth)
1301
- and host these files within your application
1499
+ If this is not possible you can extract the middleware files from [grape v0.7.0](https://github.com/ruby-grape/grape/tree/v0.7.0/lib/grape/middleware/auth) and host these files within your application
1302
1500
 
1303
1501
  See [#703](https://github.com/ruby-grape/Grape/pull/703) for more information.
1304
1502
 
data/grape.gemspec CHANGED
@@ -17,17 +17,17 @@ 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', '>= 5'
24
- s.add_runtime_dependency 'builder'
24
+ s.add_runtime_dependency 'activesupport', '>= 6'
25
25
  s.add_runtime_dependency 'dry-types', '>= 1.1'
26
- s.add_runtime_dependency 'mustermann-grape', '~> 1.0.0'
27
- s.add_runtime_dependency 'rack', '>= 1.3.0'
28
- s.add_runtime_dependency 'rack-accept'
26
+ s.add_runtime_dependency 'mustermann-grape', '~> 1.1.0'
27
+ s.add_runtime_dependency 'rack', '>= 2'
28
+ s.add_runtime_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']
32
- s.required_ruby_version = '>= 2.6.0'
32
+ s.required_ruby_version = '>= 2.7.0'
33
33
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/router'
4
-
5
3
  module Grape
6
4
  class API
7
5
  # The API Instance class, is the engine behind Grape::API. Each class that inherits
@@ -48,7 +46,7 @@ module Grape
48
46
  # Parses the API's definition and compiles it into an instance of
49
47
  # Grape::API.
50
48
  def compile
51
- @instance ||= new
49
+ @instance ||= new # rubocop:disable Naming/MemoizedInstanceVariableName
52
50
  end
53
51
 
54
52
  # Wipe the compiled API so we can recompile after changes were made.
@@ -112,7 +110,7 @@ module Grape
112
110
  end
113
111
 
114
112
  def evaluate_as_instance_with_configuration(block, lazy: false)
115
- lazy_block = Grape::Util::LazyBlock.new do |configuration|
113
+ lazy_block = Grape::Util::Lazy::Block.new do |configuration|
116
114
  value_for_configuration = configuration
117
115
  self.configuration = value_for_configuration.evaluate if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
118
116
  response = instance_eval(&block)
@@ -127,6 +125,7 @@ module Grape
127
125
  end
128
126
 
129
127
  def inherited(subclass)
128
+ super
130
129
  subclass.reset!
131
130
  subclass.logger = logger.clone
132
131
  end
@@ -162,9 +161,13 @@ module Grape
162
161
 
163
162
  # Handle a request. See Rack documentation for what `env` is.
164
163
  def call(env)
165
- result = @router.call(env)
166
- result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
167
- result
164
+ status, headers, response = @router.call(env)
165
+ unless cascade?
166
+ headers = Grape::Util::Header.new.merge(headers)
167
+ headers.delete(Grape::Http::Headers::X_CASCADE)
168
+ end
169
+
170
+ [status, headers, response]
168
171
  end
169
172
 
170
173
  # Some requests may return a HTTP 404 error if grape cannot find a matching
@@ -203,11 +206,11 @@ module Grape
203
206
 
204
207
  allowed_methods = config[:methods].dup
205
208
 
206
- allowed_methods |= [Grape::Http::Headers::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Grape::Http::Headers::GET)
209
+ allowed_methods |= [Rack::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Rack::GET)
207
210
 
208
- allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods)
211
+ allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Rack::OPTIONS] | allowed_methods)
209
212
 
210
- config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
213
+ config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Rack::OPTIONS)
211
214
 
212
215
  attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
213
216
  generate_not_allowed_method(config[:pattern], **attributes)
@@ -218,7 +221,7 @@ module Grape
218
221
 
219
222
  def collect_route_config_per_pattern
220
223
  all_routes = self.class.endpoints.map(&:routes).flatten
221
- routes_by_regexp = all_routes.group_by { |route| route.pattern.to_regexp }
224
+ routes_by_regexp = all_routes.group_by(&:pattern_regexp)
222
225
 
223
226
  # Build the configuration based on the first endpoint and the collection of methods supported.
224
227
  routes_by_regexp.values.map do |routes|
data/lib/grape/api.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/router'
4
- require 'grape/api/instance'
5
-
6
3
  module Grape
7
4
  # The API class is the primary entry point for creating Grape APIs. Users
8
5
  # should subclass this class in order to build an API.
@@ -26,8 +23,8 @@ module Grape
26
23
  attr_accessor :base_instance, :instances
27
24
 
28
25
  # Rather than initializing an object of type Grape::API, create an object of type Instance
29
- def new(*args, &block)
30
- base_instance.new(*args, &block)
26
+ def new(...)
27
+ base_instance.new(...)
31
28
  end
32
29
 
33
30
  # When inherited, will create a list of all instances (times the API was mounted)
@@ -35,7 +32,7 @@ module Grape
35
32
  def inherited(api)
36
33
  super
37
34
 
38
- api.initial_setup(Grape::API == self ? Grape::API::Instance : @base_instance)
35
+ api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance)
39
36
  api.override_all_methods!
40
37
  end
41
38
 
@@ -77,8 +74,8 @@ module Grape
77
74
  # the headers, and the body. See [the rack specification]
78
75
  # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
79
76
  # NOTE: This will only be called on an API directly mounted on RACK
80
- def call(*args, &block)
81
- instance_for_rack.call(*args, &block)
77
+ def call(...)
78
+ instance_for_rack.call(...)
82
79
  end
83
80
 
84
81
  # Alleviates problems with autoloading by tring to search for the constant
@@ -111,7 +108,7 @@ module Grape
111
108
  end
112
109
 
113
110
  def respond_to?(method, include_private = false)
114
- super(method, include_private) || base_instance.respond_to?(method, include_private)
111
+ super || base_instance.respond_to?(method, include_private)
115
112
  end
116
113
 
117
114
  def respond_to_missing?(method, include_private = false)
@@ -128,7 +125,6 @@ module Grape
128
125
  end
129
126
 
130
127
  def compile!
131
- require 'grape/eager_load'
132
128
  instance_for_rack.compile! # See API::Instance.compile!
133
129
  end
134
130
 
@@ -150,6 +146,19 @@ module Grape
150
146
  @instances.each do |instance|
151
147
  last_response = replay_step_on(instance, setup_step)
152
148
  end
149
+
150
+ # Updating all previously mounted classes in the case that new methods have been executed.
151
+ if method != :mount && @setup.any?
152
+ previous_mount_steps = @setup.select { |step| step[:method] == :mount }
153
+ previous_mount_steps.each do |mount_step|
154
+ refresh_mount_step = mount_step.merge(method: :refresh_mounted_api)
155
+ @setup += [refresh_mount_step]
156
+ @instances.each do |instance|
157
+ replay_step_on(instance, refresh_mount_step)
158
+ end
159
+ end
160
+ end
161
+
153
162
  last_response
154
163
  end
155
164
 
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/util/registrable'
4
-
5
3
  module Grape
6
4
  module ContentTypes
7
- extend Util::Registrable
5
+ module_function
8
6
 
9
7
  # Content types are listed in order of preference.
10
- CONTENT_TYPES = {
8
+ DEFAULTS = {
11
9
  xml: 'application/xml',
12
10
  serializable_hash: 'application/json',
13
11
  json: 'application/json',
@@ -15,13 +13,18 @@ module Grape
15
13
  txt: 'text/plain'
16
14
  }.freeze
17
15
 
18
- class << self
19
- def content_types_for_settings(settings)
20
- settings&.inject(:merge!)
21
- 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
22
24
 
23
- def content_types_for(from_settings)
24
- 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
25
28
  end
26
29
  end
27
30
  end
data/lib/grape/cookies.rb CHANGED
@@ -33,9 +33,10 @@ module Grape
33
33
  @cookies.each(&block)
34
34
  end
35
35
 
36
+ # see https://github.com/rack/rack/blob/main/lib/rack/utils.rb#L338-L340
36
37
  # rubocop:disable Layout/SpaceBeforeBrackets
37
38
  def delete(name, **opts)
38
- options = opts.merge(value: 'deleted', expires: Time.at(0))
39
+ options = opts.merge(max_age: '0', value: '', expires: Time.at(0))
39
40
  self.[]=(name, options)
40
41
  end
41
42
  # rubocop:enable Layout/SpaceBeforeBrackets
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry-types'
4
-
5
3
  module Grape
6
4
  module DryTypes
7
5
  # Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
@@ -5,6 +5,27 @@ module Grape
5
5
  module Desc
6
6
  include Grape::DSL::Settings
7
7
 
8
+ ROUTE_ATTRIBUTES = %i[
9
+ body_name
10
+ consumes
11
+ default
12
+ deprecated
13
+ description
14
+ detail
15
+ entity
16
+ headers
17
+ hidden
18
+ http_codes
19
+ is_array
20
+ named
21
+ nickname
22
+ params
23
+ produces
24
+ security
25
+ summary
26
+ tags
27
+ ].freeze
28
+
8
29
  # Add a description to the next namespace or function.
9
30
  # @param description [String] descriptive string for this endpoint
10
31
  # or namespace
@@ -81,26 +102,7 @@ module Grape
81
102
  # Returns an object which configures itself via an instance-context DSL.
82
103
  def desc_container(endpoint_configuration)
83
104
  Module.new do
84
- include Grape::Util::StrictHashConfiguration.module(
85
- :summary,
86
- :description,
87
- :detail,
88
- :params,
89
- :entity,
90
- :http_codes,
91
- :named,
92
- :body_name,
93
- :headers,
94
- :hidden,
95
- :deprecated,
96
- :is_array,
97
- :nickname,
98
- :produces,
99
- :consumes,
100
- :security,
101
- :tags,
102
- :default
103
- )
105
+ include Grape::Util::StrictHashConfiguration.module(*ROUTE_ATTRIBUTES)
104
106
  config_context.define_singleton_method(:configuration) do
105
107
  endpoint_configuration
106
108
  end
@@ -12,7 +12,7 @@ module Grape
12
12
  if key
13
13
  val ? header[key.to_s] = val : header.delete(key.to_s)
14
14
  else
15
- @header ||= {}
15
+ @header ||= Grape::Util::Header.new
16
16
  end
17
17
  end
18
18
  alias headers header
@@ -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