grape 2.0.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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