grape 3.1.0 → 3.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/README.md +76 -161
  4. data/UPGRADING.md +106 -0
  5. data/grape.gemspec +2 -2
  6. data/lib/grape/api/instance.rb +1 -1
  7. data/lib/grape/api.rb +1 -1
  8. data/lib/grape/declared_params_handler.rb +3 -3
  9. data/lib/grape/dsl/declared.rb +1 -1
  10. data/lib/grape/dsl/desc.rb +1 -1
  11. data/lib/grape/dsl/inside_route.rb +9 -9
  12. data/lib/grape/dsl/parameters.rb +14 -14
  13. data/lib/grape/dsl/routing.rb +8 -8
  14. data/lib/grape/endpoint.rb +45 -50
  15. data/lib/grape/error_formatter/base.rb +2 -2
  16. data/lib/grape/exceptions/base.rb +18 -44
  17. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  18. data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
  19. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  20. data/lib/grape/exceptions/invalid_message_body.rb +1 -1
  21. data/lib/grape/exceptions/invalid_version_header.rb +1 -1
  22. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  23. data/lib/grape/exceptions/method_not_allowed.rb +1 -1
  24. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  25. data/lib/grape/exceptions/request_error.rb +11 -0
  26. data/lib/grape/exceptions/unknown_auth_strategy.rb +1 -1
  27. data/lib/grape/exceptions/unknown_parameter.rb +1 -1
  28. data/lib/grape/exceptions/unknown_params_builder.rb +1 -1
  29. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  30. data/lib/grape/exceptions/validation.rb +7 -4
  31. data/lib/grape/exceptions/validation_errors.rb +13 -7
  32. data/lib/grape/locale/en.yml +0 -5
  33. data/lib/grape/middleware/auth/base.rb +2 -0
  34. data/lib/grape/middleware/base.rb +2 -4
  35. data/lib/grape/middleware/error.rb +2 -2
  36. data/lib/grape/middleware/formatter.rb +1 -1
  37. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
  38. data/lib/grape/request.rb +2 -10
  39. data/lib/grape/router/pattern.rb +1 -1
  40. data/lib/grape/router.rb +4 -2
  41. data/lib/grape/util/api_description.rb +1 -1
  42. data/lib/grape/util/deep_freeze.rb +35 -0
  43. data/lib/grape/util/inheritable_setting.rb +1 -1
  44. data/lib/grape/util/media_type.rb +1 -1
  45. data/lib/grape/util/translation.rb +42 -0
  46. data/lib/grape/validations/attributes_iterator.rb +33 -18
  47. data/lib/grape/validations/contract_scope.rb +1 -7
  48. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  49. data/lib/grape/validations/param_scope_tracker.rb +57 -0
  50. data/lib/grape/validations/params_scope.rb +111 -107
  51. data/lib/grape/validations/single_attribute_iterator.rb +2 -2
  52. data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
  53. data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
  54. data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
  55. data/lib/grape/validations/validators/base.rb +95 -18
  56. data/lib/grape/validations/validators/coerce_validator.rb +15 -35
  57. data/lib/grape/validations/validators/contract_scope_validator.rb +10 -8
  58. data/lib/grape/validations/validators/default_validator.rb +12 -18
  59. data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
  60. data/lib/grape/validations/validators/except_values_validator.rb +13 -4
  61. data/lib/grape/validations/validators/length_validator.rb +21 -22
  62. data/lib/grape/validations/validators/multiple_params_base.rb +5 -5
  63. data/lib/grape/validations/validators/mutually_exclusive_validator.rb +3 -1
  64. data/lib/grape/validations/validators/presence_validator.rb +4 -2
  65. data/lib/grape/validations/validators/regexp_validator.rb +8 -10
  66. data/lib/grape/validations/validators/same_as_validator.rb +6 -15
  67. data/lib/grape/validations/validators/values_validator.rb +29 -21
  68. data/lib/grape/version.rb +1 -1
  69. data/lib/grape.rb +18 -1
  70. metadata +12 -14
  71. data/lib/grape/exceptions/conflicting_types.rb +0 -11
  72. data/lib/grape/exceptions/empty_message_body.rb +0 -11
  73. data/lib/grape/exceptions/invalid_parameters.rb +0 -11
  74. data/lib/grape/exceptions/too_deep_parameters.rb +0 -11
  75. data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
  76. data/lib/grape/validations/validator_factory.rb +0 -15
@@ -60,7 +60,7 @@ module Grape
60
60
  end
61
61
  end
62
62
 
63
- @versions.last if instance_variable_defined?(:@versions) && @versions
63
+ @versions&.last
64
64
  end
65
65
 
66
66
  # Define a root URL prefix for your entire API.
@@ -136,8 +136,8 @@ module Grape
136
136
  endpoints << Grape::Endpoint.new(
137
137
  in_setting,
138
138
  method: :any,
139
- path: path,
140
- app: app,
139
+ path:,
140
+ app:,
141
141
  route_options: { anchor: false },
142
142
  forward_match: !app.respond_to?(:inheritable_setting),
143
143
  for: self
@@ -167,13 +167,13 @@ module Grape
167
167
 
168
168
  new_endpoint = Grape::Endpoint.new(
169
169
  inheritable_setting,
170
- method: method,
170
+ method:,
171
171
  path: paths,
172
172
  for: self,
173
173
  route_options: all_route_options,
174
174
  &
175
175
  )
176
- endpoints << new_endpoint unless endpoints.any? { |e| e.equals?(new_endpoint) }
176
+ endpoints << new_endpoint unless endpoints.include?(new_endpoint)
177
177
 
178
178
  inheritable_setting.route_end
179
179
  reset_validations!
@@ -203,7 +203,7 @@ module Grape
203
203
 
204
204
  within_namespace do
205
205
  nest(block) do
206
- inheritable_setting.namespace_stackable[:namespace] = Grape::Namespace.new(space, requirements: requirements, **options) if space
206
+ inheritable_setting.namespace_stackable[:namespace] = Grape::Namespace.new(space, requirements:, **options) if space
207
207
  end
208
208
  end
209
209
  end
@@ -223,14 +223,14 @@ module Grape
223
223
  #
224
224
  # @param param [Symbol] The name of the parameter you wish to declare.
225
225
  # @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
226
- def route_param(param, requirements: nil, type: nil, **options, &)
226
+ def route_param(param, requirements: nil, type: nil, **, &)
227
227
  requirements = { param.to_sym => requirements } if requirements.is_a?(Regexp)
228
228
 
229
229
  Grape::Validations::ParamsScope.new(api: self) do
230
230
  requires param, type: type
231
231
  end if type
232
232
 
233
- namespace(":#{param}", requirements: requirements, **options, &)
233
+ namespace(":#{param}", requirements:, **, &)
234
234
  end
235
235
 
236
236
  # @return array of defined versions
@@ -11,7 +11,7 @@ module Grape
11
11
  include Grape::DSL::Headers
12
12
  include Grape::DSL::InsideRoute
13
13
 
14
- attr_reader :env, :request, :source, :options
14
+ attr_reader :env, :request, :source, :options, :endpoints
15
15
 
16
16
  def_delegators :request, :params, :headers, :cookies
17
17
  def_delegator :cookies, :response_cookies
@@ -23,8 +23,10 @@ module Grape
23
23
  return @before_each unless block
24
24
 
25
25
  @before_each << block
26
- else
26
+ elsif new_setup
27
27
  @before_each = [new_setup]
28
+ else
29
+ @before_each.clear
28
30
  end
29
31
  end
30
32
 
@@ -64,20 +66,21 @@ module Grape
64
66
  inheritable_setting.route[:declared_params] = inheritable_setting.namespace_stackable[:declared_params].flatten
65
67
  inheritable_setting.route[:saved_validations] = inheritable_setting.namespace_stackable[:validations]
66
68
 
67
- inheritable_setting.namespace_stackable[:representations] = [] unless inheritable_setting.namespace_stackable[:representations]
68
- inheritable_setting.namespace_inheritable[:default_error_status] = 500 unless inheritable_setting.namespace_inheritable[:default_error_status]
69
+ inheritable_setting.namespace_stackable[:representations] ||= []
70
+ inheritable_setting.namespace_inheritable[:default_error_status] ||= 500
69
71
 
70
72
  @options = options
71
73
 
72
- @options[:path] = Array(options[:path])
73
- @options[:path] << '/' if options[:path].empty?
74
- @options[:method] = Array(options[:method])
74
+ @options[:path] = Array(@options[:path])
75
+ @options[:path] << '/' if @options[:path].empty?
76
+ @options[:method] = Array(@options[:method])
75
77
 
76
78
  @status = nil
77
79
  @stream = nil
78
80
  @body = nil
79
81
  @source = self.class.block_to_unbound_method(block)
80
82
  @before_filter_passed = false
83
+ @endpoints = @options[:app].endpoints if @options[:app].respond_to?(:endpoints)
81
84
  end
82
85
 
83
86
  # Update our settings from a given set of stackable parameters. Used when
@@ -92,7 +95,7 @@ module Grape
92
95
  end
93
96
 
94
97
  def routes
95
- @routes ||= endpoints&.collect(&:routes)&.flatten || to_routes
98
+ @routes ||= endpoints&.flat_map(&:routes) || to_routes
96
99
  end
97
100
 
98
101
  def reset_routes!
@@ -111,7 +114,7 @@ module Grape
111
114
  compile!
112
115
  routes.each do |route|
113
116
  router.append(route.apply(self))
114
- next unless !inheritable_setting.namespace_inheritable[:do_not_route_head] && route.request_method == Rack::GET
117
+ next if inheritable_setting.namespace_inheritable[:do_not_route_head] || route.request_method != Rack::GET
115
118
 
116
119
  route.dup.then do |head_route|
117
120
  head_route.convert_to_head_request!
@@ -136,15 +139,12 @@ module Grape
136
139
  @app.call(env)
137
140
  end
138
141
 
139
- # Return the collection of endpoints within this endpoint.
140
- # This is the case when an Grape::API mounts another Grape::API.
141
- def endpoints
142
- @endpoints ||= options[:app].respond_to?(:endpoints) ? options[:app].endpoints : nil
143
- end
144
-
145
- def equals?(endpoint)
146
- (options == endpoint.options) && (inheritable_setting.to_hash == endpoint.inheritable_setting.to_hash)
142
+ def ==(other)
143
+ other.is_a?(self.class) &&
144
+ options == other.options &&
145
+ inheritable_setting.to_hash == other.inheritable_setting.to_hash
147
146
  end
147
+ alias eql? ==
148
148
 
149
149
  # The purpose of this override is solely for stripping internals when an error occurs while calling
150
150
  # an endpoint through an api. See https://github.com/ruby-grape/grape/issues/2398
@@ -158,7 +158,7 @@ module Grape
158
158
  protected
159
159
 
160
160
  def run
161
- ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
161
+ ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env:) do
162
162
  @request = Grape::Request.new(env, build_params_with: inheritable_setting.namespace_inheritable[:build_params_with])
163
163
  begin
164
164
  self.class.run_before_each(self)
@@ -174,7 +174,7 @@ module Grape
174
174
  status 204
175
175
  else
176
176
  run_filters before_validations, :before_validation
177
- run_validators validations, request
177
+ run_validators(request:)
178
178
  run_filters after_validations, :after_validation
179
179
  response_object = execute
180
180
  end
@@ -196,35 +196,40 @@ module Grape
196
196
  end
197
197
 
198
198
  def execute
199
- return unless @source
199
+ return unless source
200
200
 
201
201
  ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: self) do
202
- @source.bind_call(self)
202
+ source.bind_call(self)
203
203
  end
204
204
  end
205
205
 
206
- def run_validators(validators, request)
206
+ def run_validators(request:)
207
+ validators = inheritable_setting.route[:saved_validations]
208
+ return if validators.empty?
209
+
207
210
  validation_errors = []
208
211
 
209
- ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do
210
- validators.each do |validator|
211
- validator.validate(request)
212
- rescue Grape::Exceptions::Validation => e
213
- validation_errors << e
214
- break if validator.fail_fast?
215
- rescue Grape::Exceptions::ValidationArrayErrors => e
216
- validation_errors.concat e.errors
217
- break if validator.fail_fast?
212
+ Grape::Validations::ParamScopeTracker.track do
213
+ ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators:, request:) do
214
+ validators.each do |validator|
215
+ validator.validate(request)
216
+ rescue Grape::Exceptions::Validation => e
217
+ validation_errors << e
218
+ break if validator.fail_fast?
219
+ rescue Grape::Exceptions::ValidationArrayErrors => e
220
+ validation_errors.concat e.errors
221
+ break if validator.fail_fast?
222
+ end
218
223
  end
219
224
  end
220
225
 
221
- validation_errors.any? && raise(Grape::Exceptions::ValidationErrors.new(errors: validation_errors, headers: header))
226
+ raise(Grape::Exceptions::ValidationErrors.new(errors: validation_errors, headers: header)) if validation_errors.any?
222
227
  end
223
228
 
224
229
  def run_filters(filters, type = :other)
225
230
  return unless filters
226
231
 
227
- ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters: filters, type: type) do
232
+ ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters:, type:) do
228
233
  filters.each { |filter| instance_eval(&filter) }
229
234
  end
230
235
  end
@@ -235,16 +240,6 @@ module Grape
235
240
  end
236
241
  end
237
242
 
238
- def validations
239
- saved_validations = inheritable_setting.route[:saved_validations]
240
- return if saved_validations.nil?
241
- return enum_for(:validations) unless block_given?
242
-
243
- saved_validations.each do |saved_validation|
244
- yield Grape::Validations::ValidatorFactory.create_validator(saved_validation)
245
- end
246
- end
247
-
248
243
  def options?
249
244
  options[:options_route_enabled] &&
250
245
  env[Rack::REQUEST_METHOD] == Rack::OPTIONS
@@ -284,7 +279,7 @@ module Grape
284
279
 
285
280
  def prepare_default_route_attributes(route_options)
286
281
  {
287
- namespace: namespace,
282
+ namespace:,
288
283
  version: prepare_version(inheritable_setting.namespace_inheritable[:version]),
289
284
  requirements: prepare_routes_requirements(route_options[:requirements]),
290
285
  prefix: inheritable_setting.namespace_inheritable[:root_prefix],
@@ -321,15 +316,15 @@ module Grape
321
316
  stack.use Rack::Head
322
317
  stack.use Rack::Lint if lint?
323
318
  stack.use Grape::Middleware::Error,
324
- format: format,
325
- content_types: content_types,
319
+ format:,
320
+ content_types:,
326
321
  default_status: inheritable_setting.namespace_inheritable[:default_error_status],
327
322
  rescue_all: inheritable_setting.namespace_inheritable[:rescue_all],
328
323
  rescue_grape_exceptions: inheritable_setting.namespace_inheritable[:rescue_grape_exceptions],
329
324
  default_error_formatter: inheritable_setting.namespace_inheritable[:default_error_formatter],
330
325
  error_formatters: inheritable_setting.namespace_stackable_with_hash(:error_formatters),
331
326
  rescue_options: inheritable_setting.namespace_stackable_with_hash(:rescue_options),
332
- rescue_handlers: rescue_handlers,
327
+ rescue_handlers:,
333
328
  base_only_rescue_handlers: inheritable_setting.namespace_stackable_with_hash(:base_only_rescue_handlers),
334
329
  all_rescue_handler: inheritable_setting.namespace_inheritable[:all_rescue_handler],
335
330
  grape_exceptions_rescue_handler: inheritable_setting.namespace_inheritable[:grape_exceptions_rescue_handler]
@@ -345,9 +340,9 @@ module Grape
345
340
  end
346
341
 
347
342
  stack.use Grape::Middleware::Formatter,
348
- format: format,
343
+ format:,
349
344
  default_format: inheritable_setting.namespace_inheritable[:default_format] || :txt,
350
- content_types: content_types,
345
+ content_types:,
351
346
  formatters: inheritable_setting.namespace_stackable_with_hash(:formatters),
352
347
  parsers: inheritable_setting.namespace_stackable_with_hash(:parsers)
353
348
 
@@ -365,7 +360,7 @@ module Grape
365
360
 
366
361
  def build_response_cookies
367
362
  response_cookies do |name, value|
368
- cookie_value = value.is_a?(Hash) ? value : { value: value }
363
+ cookie_value = value.is_a?(Hash) ? value : { value: }
369
364
  Rack::Utils.set_cookie_header! header, name, cookie_value
370
365
  end
371
366
  end
@@ -40,7 +40,7 @@ module Grape
40
40
  end
41
41
 
42
42
  if presenter
43
- embeds = { env: env }
43
+ embeds = { env: }
44
44
  embeds[:version] = env[Grape::Env::API_VERSION] if env.key?(Grape::Env::API_VERSION)
45
45
  presented_message = presenter.represent(presented_message, embeds).serializable_hash
46
46
  end
@@ -51,7 +51,7 @@ module Grape
51
51
  def wrap_message(message)
52
52
  return message if message.is_a?(Hash)
53
53
 
54
- { message: message }
54
+ { message: }
55
55
  end
56
56
 
57
57
  def format_structured_message(_structured_message)
@@ -3,9 +3,9 @@
3
3
  module Grape
4
4
  module Exceptions
5
5
  class Base < StandardError
6
- BASE_MESSAGES_KEY = 'grape.errors.messages'
7
- BASE_ATTRIBUTES_KEY = 'grape.errors.attributes'
8
- FALLBACK_LOCALE = :en
6
+ include Grape::Util::Translation
7
+
8
+ MESSAGE_STEPS = %w[problem summary resolution].to_h { |s| [s, s.capitalize] }.freeze
9
9
 
10
10
  attr_reader :status, :headers
11
11
 
@@ -20,55 +20,29 @@ module Grape
20
20
  __send__ index
21
21
  end
22
22
 
23
- protected
23
+ private
24
24
 
25
- # TODO: translate attribute first
26
- # if BASE_ATTRIBUTES_KEY.key respond to a string message, then short_message is returned
27
- # if BASE_ATTRIBUTES_KEY.key respond to a Hash, means it may have problem , summary and resolution
28
- def compose_message(key, **attributes)
29
- short_message = translate_message(key, attributes)
25
+ def compose_message(key, **)
26
+ short_message = translate_message(key, **)
30
27
  return short_message unless short_message.is_a?(Hash)
31
28
 
32
- each_steps(key, attributes).with_object(+'') do |detail_array, message|
33
- message << "\n#{detail_array[0]}:\n #{detail_array[1]}" unless detail_array[1].blank?
34
- end
35
- end
36
-
37
- def each_steps(key, attributes)
38
- return enum_for(:each_steps, key, attributes) unless block_given?
39
-
40
- yield 'Problem', translate_message(:"#{key}.problem", attributes)
41
- yield 'Summary', translate_message(:"#{key}.summary", attributes)
42
- yield 'Resolution', translate_message(:"#{key}.resolution", attributes)
43
- end
44
-
45
- def translate_attributes(keys, options = {})
46
- keys.map do |key|
47
- translate("#{BASE_ATTRIBUTES_KEY}.#{key}", options.merge(default: key.to_s))
48
- end.join(', ')
29
+ MESSAGE_STEPS.filter_map do |step, label|
30
+ detail = translate_message(:"#{key}.#{step}", **)
31
+ "\n#{label}:\n #{detail}" if detail.present?
32
+ end.join
49
33
  end
50
34
 
51
- def translate_message(key, options = {})
52
- case key
35
+ def translate_message(translation_key, **)
36
+ case translation_key
53
37
  when Symbol
54
- translate("#{BASE_MESSAGES_KEY}.#{key}", options.merge(default: ''))
38
+ translate(translation_key, **)
39
+ when Hash
40
+ translation_key => { key:, **opts }
41
+ translate(key, **opts)
55
42
  when Proc
56
- key.call
57
- else
58
- key
59
- end
60
- end
61
-
62
- def translate(key, options)
63
- message = ::I18n.translate(key, **options)
64
- message.presence || fallback_message(key, options)
65
- end
66
-
67
- def fallback_message(key, options)
68
- if ::I18n.enforce_available_locales && !::I18n.available_locales.include?(FALLBACK_LOCALE)
69
- key
43
+ translation_key.call
70
44
  else
71
- ::I18n.translate(key, locale: FALLBACK_LOCALE, **options)
45
+ translation_key
72
46
  end
73
47
  end
74
48
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class IncompatibleOptionValues < Base
6
6
  def initialize(option1, value1, option2, value2)
7
- super(message: compose_message(:incompatible_option_values, option1: option1, value1: value1, option2: option2, value2: value2))
7
+ super(message: compose_message(:incompatible_option_values, option1:, value1:, option2:, value2:))
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class InvalidAcceptHeader < Base
6
6
  def initialize(message, headers)
7
- super(message: compose_message(:invalid_accept_header, message: message), status: 406, headers: headers)
7
+ super(message: compose_message(:invalid_accept_header, message:), status: 406, headers:)
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class InvalidFormatter < Base
6
6
  def initialize(klass, to_format)
7
- super(message: compose_message(:invalid_formatter, klass: klass, to_format: to_format))
7
+ super(message: compose_message(:invalid_formatter, klass:, to_format:))
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class InvalidMessageBody < Base
6
6
  def initialize(body_format)
7
- super(message: compose_message(:invalid_message_body, body_format: body_format), status: 400)
7
+ super(message: compose_message(:invalid_message_body, body_format:), status: 400)
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class InvalidVersionHeader < Base
6
6
  def initialize(message, headers)
7
- super(message: compose_message(:invalid_version_header, message: message), status: 406, headers: headers)
7
+ super(message: compose_message(:invalid_version_header, message:), status: 406, headers:)
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class InvalidVersionerOption < Base
6
6
  def initialize(strategy)
7
- super(message: compose_message(:invalid_versioner_option, strategy: strategy))
7
+ super(message: compose_message(:invalid_versioner_option, strategy:))
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class MethodNotAllowed < Base
6
6
  def initialize(headers)
7
- super(message: '405 Not Allowed', status: 405, headers: headers)
7
+ super(message: '405 Not Allowed', status: 405, headers:)
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class MissingMimeType < Base
6
6
  def initialize(new_format)
7
- super(message: compose_message(:missing_mime_type, new_format: new_format))
7
+ super(message: compose_message(:missing_mime_type, new_format:))
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Exceptions
5
+ class RequestError < Base
6
+ def initialize(status: 400)
7
+ super(message: $ERROR_INFO&.message, status:)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class UnknownAuthStrategy < Base
6
6
  def initialize(strategy:)
7
- super(message: compose_message(:unknown_auth_strategy, strategy: strategy))
7
+ super(message: compose_message(:unknown_auth_strategy, strategy:))
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class UnknownParameter < Base
6
6
  def initialize(param)
7
- super(message: compose_message(:unknown_parameter, param: param))
7
+ super(message: compose_message(:unknown_parameter, param:))
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class UnknownParamsBuilder < Base
6
6
  def initialize(params_builder_type)
7
- super(message: compose_message(:unknown_params_builder, params_builder_type: params_builder_type))
7
+ super(message: compose_message(:unknown_params_builder, params_builder_type:))
8
8
  end
9
9
  end
10
10
  end
@@ -4,7 +4,7 @@ module Grape
4
4
  module Exceptions
5
5
  class UnknownValidator < Base
6
6
  def initialize(validator_type)
7
- super(message: compose_message(:unknown_validator, validator_type: validator_type))
7
+ super(message: compose_message(:unknown_validator, validator_type:))
8
8
  end
9
9
  end
10
10
  end
@@ -3,16 +3,19 @@
3
3
  module Grape
4
4
  module Exceptions
5
5
  class Validation < Base
6
- attr_accessor :params, :message_key
6
+ attr_reader :params, :message_key
7
7
 
8
8
  def initialize(params:, message: nil, status: nil, headers: nil)
9
- @params = params
9
+ @params = Array(params)
10
10
  if message
11
- @message_key = message if message.is_a?(Symbol)
11
+ @message_key = case message
12
+ when Symbol then message
13
+ when Hash then message[:key]
14
+ end
12
15
  message = translate_message(message)
13
16
  end
14
17
 
15
- super(status: status, message: message, headers: headers)
18
+ super(status:, message:, headers:)
16
19
  end
17
20
 
18
21
  # Remove all the unnecessary stuff from Grape::Exceptions::Base like status
@@ -3,16 +3,13 @@
3
3
  module Grape
4
4
  module Exceptions
5
5
  class ValidationErrors < Base
6
- ERRORS_FORMAT_KEY = 'grape.errors.format'
7
- DEFAULT_ERRORS_FORMAT = '%<attributes>s %<message>s'
8
-
9
6
  include Enumerable
10
7
 
11
8
  attr_reader :errors
12
9
 
13
10
  def initialize(errors: [], headers: {})
14
11
  @errors = errors.group_by(&:params)
15
- super(message: full_messages.join(', '), status: 400, headers: headers)
12
+ super(message: full_messages.join(', '), status: 400, headers:)
16
13
  end
17
14
 
18
15
  def each
@@ -38,9 +35,10 @@ module Grape
38
35
 
39
36
  def full_messages
40
37
  messages = map do |attributes, error|
41
- I18n.t(
42
- ERRORS_FORMAT_KEY,
43
- default: DEFAULT_ERRORS_FORMAT,
38
+ translate(
39
+ :format,
40
+ scope: 'grape.errors',
41
+ default: '%<attributes>s %<message>s',
44
42
  attributes: translate_attributes(attributes),
45
43
  message: error.message
46
44
  )
@@ -48,6 +46,14 @@ module Grape
48
46
  messages.uniq!
49
47
  messages
50
48
  end
49
+
50
+ private
51
+
52
+ def translate_attributes(keys)
53
+ keys.map do |key|
54
+ translate(key, scope: 'grape.errors.attributes', default: key.to_s)
55
+ end.join(', ')
56
+ end
51
57
  end
52
58
  end
53
59
  end
@@ -8,8 +8,6 @@ en:
8
8
  at_least_one: 'are missing, at least one parameter must be provided'
9
9
  blank: 'is empty'
10
10
  coerce: 'is invalid'
11
- conflicting_types: 'query params contains conflicting types'
12
- empty_message_body: 'empty message body supplied with %{body_format} content-type'
13
11
  exactly_one: 'are missing, exactly one parameter must be provided'
14
12
  except_values: 'has a value not allowed'
15
13
  incompatible_option_values: '%{option1}: %{value1} is incompatible with %{option2}: %{value2}'
@@ -20,7 +18,6 @@ en:
20
18
  invalid_message_body:
21
19
  problem: 'message body does not match declared format'
22
20
  resolution: 'when specifying %{body_format} as content-type, you must pass valid %{body_format} in the request''s ''body'' '
23
- invalid_parameters: 'query params contains invalid format or byte sequence'
24
21
  invalid_response: 'Invalid response'
25
22
  invalid_version_header:
26
23
  problem: 'invalid version header'
@@ -48,8 +45,6 @@ en:
48
45
  presence: 'is missing'
49
46
  regexp: 'is invalid'
50
47
  same_as: 'is not the same as %{parameter}'
51
- too_deep_parameters: 'query params are recursively nested over the specified limit (%{limit})'
52
- too_many_multipart_files: 'the number of uploaded files exceeded the system''s configured limit (%{limit})'
53
48
  unknown_auth_strategy: 'unknown auth strategy: %{strategy}'
54
49
  unknown_options: 'unknown options: %{options}'
55
50
  unknown_parameter: 'unknown parameter: %{param}'
@@ -6,6 +6,8 @@ module Grape
6
6
  class Base < Grape::Middleware::Base
7
7
  def initialize(app, **options)
8
8
  super
9
+ return unless options.key?(:type)
10
+
9
11
  @auth_strategy = Grape::Middleware::Auth::Strategies[options[:type]].tap do |auth_strategy|
10
12
  raise Grape::Exceptions::UnknownAuthStrategy.new(strategy: options[:type]) unless auth_strategy
11
13
  end
@@ -79,10 +79,8 @@ module Grape
79
79
 
80
80
  def query_params
81
81
  rack_request.GET
82
- rescue Rack::QueryParser::ParamsTooDeepError
83
- raise Grape::Exceptions::TooDeepParameters.new(Rack::Utils.param_depth_limit)
84
- rescue Rack::Utils::ParameterTypeError
85
- raise Grape::Exceptions::ConflictingTypes
82
+ rescue *Grape::RACK_ERRORS
83
+ raise Grape::Exceptions::RequestError
86
84
  end
87
85
 
88
86
  private
@@ -38,8 +38,8 @@ module Grape
38
38
  throw :error,
39
39
  status: 406,
40
40
  message: "The requested format '#{format}' is not supported.",
41
- backtrace: backtrace,
42
- original_exception: original_exception
41
+ backtrace:,
42
+ original_exception:
43
43
  end
44
44
 
45
45
  def find_handler(klass)