grape 2.4.0 → 3.2.1

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +109 -0
  3. data/CONTRIBUTING.md +2 -10
  4. data/README.md +137 -181
  5. data/UPGRADING.md +154 -0
  6. data/grape.gemspec +4 -4
  7. data/lib/grape/api/instance.rb +51 -104
  8. data/lib/grape/api.rb +29 -42
  9. data/lib/grape/content_types.rb +1 -4
  10. data/lib/grape/declared_params_handler.rb +118 -0
  11. data/lib/grape/dry_types.rb +48 -4
  12. data/lib/grape/dsl/callbacks.rb +8 -58
  13. data/lib/grape/dsl/declared.rb +35 -0
  14. data/lib/grape/dsl/desc.rb +8 -67
  15. data/lib/grape/dsl/helpers.rb +59 -64
  16. data/lib/grape/dsl/inside_route.rb +28 -189
  17. data/lib/grape/dsl/logger.rb +3 -6
  18. data/lib/grape/dsl/middleware.rb +22 -40
  19. data/lib/grape/dsl/parameters.rb +24 -51
  20. data/lib/grape/dsl/request_response.rb +136 -139
  21. data/lib/grape/dsl/routing.rb +240 -200
  22. data/lib/grape/dsl/settings.rb +23 -135
  23. data/lib/grape/dsl/validations.rb +38 -44
  24. data/lib/grape/endpoint.rb +169 -205
  25. data/lib/grape/error_formatter/base.rb +4 -2
  26. data/lib/grape/exceptions/base.rb +19 -45
  27. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  28. data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
  29. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  30. data/lib/grape/exceptions/invalid_message_body.rb +1 -1
  31. data/lib/grape/exceptions/invalid_version_header.rb +1 -1
  32. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  33. data/lib/grape/exceptions/method_not_allowed.rb +1 -1
  34. data/lib/grape/exceptions/missing_group_type.rb +0 -2
  35. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  36. data/lib/grape/exceptions/request_error.rb +11 -0
  37. data/lib/grape/exceptions/unknown_auth_strategy.rb +1 -1
  38. data/lib/grape/exceptions/unknown_parameter.rb +1 -1
  39. data/lib/grape/exceptions/unknown_params_builder.rb +1 -1
  40. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  41. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  42. data/lib/grape/exceptions/validation.rb +7 -4
  43. data/lib/grape/exceptions/validation_errors.rb +13 -7
  44. data/lib/grape/locale/en.yml +0 -5
  45. data/lib/grape/middleware/auth/base.rb +2 -0
  46. data/lib/grape/middleware/auth/dsl.rb +9 -10
  47. data/lib/grape/middleware/base.rb +6 -4
  48. data/lib/grape/middleware/error.rb +4 -14
  49. data/lib/grape/middleware/formatter.rb +11 -7
  50. data/lib/grape/middleware/stack.rb +3 -2
  51. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -3
  52. data/lib/grape/middleware/versioner/base.rb +42 -40
  53. data/lib/grape/middleware/versioner/header.rb +2 -18
  54. data/lib/grape/middleware/versioner/param.rb +2 -2
  55. data/lib/grape/middleware/versioner/path.rb +2 -2
  56. data/lib/grape/namespace.rb +15 -8
  57. data/lib/grape/params_builder/base.rb +2 -0
  58. data/lib/grape/params_builder.rb +2 -19
  59. data/lib/grape/request.rb +2 -10
  60. data/lib/grape/router/base_route.rb +14 -5
  61. data/lib/grape/router/greedy_route.rb +11 -5
  62. data/lib/grape/router/pattern.rb +6 -20
  63. data/lib/grape/router/route.rb +7 -11
  64. data/lib/grape/router.rb +42 -65
  65. data/lib/grape/util/api_description.rb +58 -0
  66. data/lib/grape/util/base_inheritable.rb +5 -2
  67. data/lib/grape/util/cache.rb +1 -0
  68. data/lib/grape/util/deep_freeze.rb +35 -0
  69. data/lib/grape/util/inheritable_setting.rb +8 -1
  70. data/lib/grape/util/media_type.rb +2 -2
  71. data/lib/grape/util/registry.rb +1 -1
  72. data/lib/grape/util/translation.rb +42 -0
  73. data/lib/grape/validations/attributes_iterator.rb +35 -20
  74. data/lib/grape/validations/contract_scope.rb +2 -8
  75. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  76. data/lib/grape/validations/param_scope_tracker.rb +57 -0
  77. data/lib/grape/validations/params_documentation.rb +50 -0
  78. data/lib/grape/validations/params_scope.rb +147 -156
  79. data/lib/grape/validations/single_attribute_iterator.rb +2 -2
  80. data/lib/grape/validations/types/array_coercer.rb +2 -3
  81. data/lib/grape/validations/types/dry_type_coercer.rb +4 -11
  82. data/lib/grape/validations/types/primitive_coercer.rb +1 -28
  83. data/lib/grape/validations/types.rb +10 -25
  84. data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
  85. data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
  86. data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
  87. data/lib/grape/validations/validators/base.rb +95 -25
  88. data/lib/grape/validations/validators/coerce_validator.rb +15 -35
  89. data/lib/grape/validations/validators/contract_scope_validator.rb +9 -5
  90. data/lib/grape/validations/validators/default_validator.rb +12 -18
  91. data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
  92. data/lib/grape/validations/validators/except_values_validator.rb +13 -4
  93. data/lib/grape/validations/validators/length_validator.rb +21 -22
  94. data/lib/grape/validations/validators/multiple_params_base.rb +5 -5
  95. data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
  96. data/lib/grape/validations/validators/presence_validator.rb +4 -2
  97. data/lib/grape/validations/validators/regexp_validator.rb +11 -3
  98. data/lib/grape/validations/validators/same_as_validator.rb +6 -15
  99. data/lib/grape/validations/validators/values_validator.rb +29 -21
  100. data/lib/grape/version.rb +1 -1
  101. data/lib/grape.rb +29 -23
  102. metadata +31 -26
  103. data/lib/grape/api/helpers.rb +0 -9
  104. data/lib/grape/dsl/api.rb +0 -17
  105. data/lib/grape/dsl/configuration.rb +0 -15
  106. data/lib/grape/exceptions/conflicting_types.rb +0 -11
  107. data/lib/grape/exceptions/empty_message_body.rb +0 -11
  108. data/lib/grape/exceptions/invalid_parameters.rb +0 -11
  109. data/lib/grape/exceptions/missing_option.rb +0 -11
  110. data/lib/grape/exceptions/too_deep_parameters.rb +0 -11
  111. data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
  112. data/lib/grape/exceptions/unknown_options.rb +0 -11
  113. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -24
  114. data/lib/grape/extensions/hash.rb +0 -27
  115. data/lib/grape/extensions/hashie/mash.rb +0 -24
  116. data/lib/grape/types/invalid_value.rb +0 -8
  117. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  118. data/lib/grape/validations/attributes_doc.rb +0 -60
  119. data/lib/grape/validations/validator_factory.rb +0 -15
@@ -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
 
@@ -17,58 +17,32 @@ module Grape
17
17
  end
18
18
 
19
19
  def [](index)
20
- send index
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.exclude?(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
@@ -9,5 +9,3 @@ module Grape
9
9
  end
10
10
  end
11
11
  end
12
-
13
- Grape::Exceptions::MissingGroupTypeError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Grape::Exceptions::MissingGroupTypeError', 'Grape::Exceptions::MissingGroupType', Grape.deprecator)
@@ -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
@@ -9,5 +9,3 @@ module Grape
9
9
  end
10
10
  end
11
11
  end
12
-
13
- Grape::Exceptions::UnsupportedGroupTypeError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Grape::Exceptions::UnsupportedGroupTypeError', 'Grape::Exceptions::UnsupportedGroupType', Grape.deprecator)
@@ -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
@@ -5,24 +5,23 @@ module Grape
5
5
  module Auth
6
6
  module DSL
7
7
  def auth(type = nil, options = {}, &block)
8
- if type
9
- namespace_inheritable(:auth, options.reverse_merge(type: type.to_sym, proc: block))
10
- use Grape::Middleware::Auth::Base, namespace_inheritable(:auth)
11
- else
12
- namespace_inheritable(:auth)
13
- end
8
+ namespace_inheritable = inheritable_setting.namespace_inheritable
9
+ return namespace_inheritable[:auth] unless type
10
+
11
+ namespace_inheritable[:auth] = options.reverse_merge(type: type.to_sym, proc: block)
12
+ use Grape::Middleware::Auth::Base, namespace_inheritable[:auth]
14
13
  end
15
14
 
16
15
  # Add HTTP Basic authorization to the API.
17
16
  #
18
17
  # @param [Hash] options A hash of options.
19
18
  # @option options [String] :realm "API Authorization" The HTTP Basic realm.
20
- def http_basic(options = {}, &block)
19
+ def http_basic(options = {}, &)
21
20
  options[:realm] ||= 'API Authorization'
22
- auth :http_basic, options, &block
21
+ auth(:http_basic, options, &)
23
22
  end
24
23
 
25
- def http_digest(options = {}, &block)
24
+ def http_digest(options = {}, &)
26
25
  options[:realm] ||= 'API Authorization'
27
26
 
28
27
  if options[:realm].respond_to?(:values_at)
@@ -31,7 +30,7 @@ module Grape
31
30
  options[:opaque] ||= 'secret'
32
31
  end
33
32
 
34
- auth :http_digest, options, &block
33
+ auth(:http_digest, options, &)
35
34
  end
36
35
  end
37
36
  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
@@ -109,6 +107,10 @@ module Grape
109
107
  options
110
108
  end
111
109
  end
110
+
111
+ def try_scrub(obj)
112
+ obj.respond_to?(:valid_encoding?) && !obj.valid_encoding? ? obj.scrub : obj
113
+ end
112
114
  end
113
115
  end
114
116
  end
@@ -16,11 +16,6 @@ module Grape
16
16
  }.freeze
17
17
  }.freeze
18
18
 
19
- def initialize(app, **options)
20
- super
21
- self.class.include(options[:helpers]) if options[:helpers]
22
- end
23
-
24
19
  def call!(env)
25
20
  @env = env
26
21
  error_response(catch(:error) { return @app.call(@env) })
@@ -43,8 +38,8 @@ module Grape
43
38
  throw :error,
44
39
  status: 406,
45
40
  message: "The requested format '#{format}' is not supported.",
46
- backtrace: backtrace,
47
- original_exception: original_exception
41
+ backtrace:,
42
+ original_exception:
48
43
  end
49
44
 
50
45
  def find_handler(klass)
@@ -63,7 +58,7 @@ module Grape
63
58
  h.merge!(error[:headers]) if error[:headers].is_a?(Hash)
64
59
  end
65
60
  backtrace = error[:backtrace] || error[:original_exception]&.backtrace || []
66
- original_exception = error.is_a?(Exception) ? error : error[:original_exception] || nil
61
+ original_exception = error.is_a?(Exception) ? error : error[:original_exception]
67
62
  rack_response(status, headers, format_message(message, backtrace, original_exception))
68
63
  end
69
64
 
@@ -103,12 +98,7 @@ module Grape
103
98
  end
104
99
 
105
100
  def run_rescue_handler(handler, error, endpoint)
106
- if handler.instance_of?(Symbol)
107
- raise NoMethodError, "undefined method '#{handler}'" unless respond_to?(handler)
108
-
109
- handler = public_method(handler)
110
- end
111
-
101
+ handler = endpoint.public_method(handler) if handler.instance_of?(Symbol)
112
102
  response = catch(:error) do
113
103
  handler.arity.zero? ? endpoint.instance_exec(&handler) : endpoint.instance_exec(error, &handler)
114
104
  end
@@ -7,6 +7,8 @@ module Grape
7
7
  default_format: :txt
8
8
  }.freeze
9
9
 
10
+ ALL_MEDIA_TYPES = '*/*'
11
+
10
12
  def before
11
13
  negotiate_content_type
12
14
  read_body_input
@@ -36,7 +38,7 @@ module Grape
36
38
  else
37
39
  # Allow content-type to be explicitly overwritten
38
40
  formatter = fetch_formatter(headers, options)
39
- bodymap = ActiveSupport::Notifications.instrument('format_response.grape', formatter: formatter, env: env) do
41
+ bodymap = ActiveSupport::Notifications.instrument('format_response.grape', formatter:, env:) do
40
42
  bodies.collect { |body| formatter.call(body, env) }
41
43
  end
42
44
  Rack::Response.new(bodymap, status, headers)
@@ -67,12 +69,14 @@ module Grape
67
69
  return if input.nil?
68
70
  return unless read_body_input?
69
71
 
70
- input.try(:rewind)
72
+ rewind = input.respond_to?(:rewind)
73
+
74
+ input.rewind if rewind
71
75
  body = env[Grape::Env::API_REQUEST_INPUT] = input.read
72
76
  begin
73
77
  read_rack_input(body)
74
78
  ensure
75
- input.try(:rewind)
79
+ input.rewind if rewind
76
80
  end
77
81
  end
78
82
 
@@ -128,17 +132,17 @@ module Grape
128
132
  end
129
133
 
130
134
  def format_from_extension
131
- request_path = rack_request.path.try(:scrub)
135
+ request_path = try_scrub(rack_request.path)
132
136
  dot_pos = request_path.rindex('.')
133
137
  return unless dot_pos
134
138
 
135
- extension = request_path[dot_pos + 1..]
139
+ extension = request_path[(dot_pos + 1)..]
136
140
  extension if content_type_for(extension)
137
141
  end
138
142
 
139
143
  def format_from_header
140
- accept_header = env['HTTP_ACCEPT'].try(:scrub)
141
- return if accept_header.blank?
144
+ accept_header = try_scrub(env['HTTP_ACCEPT'])
145
+ return if accept_header.blank? || accept_header == ALL_MEDIA_TYPES
142
146
 
143
147
  media_type = Rack::Utils.best_q_match(accept_header, mime_types.keys)
144
148
  mime_types[media_type] if media_type
@@ -6,6 +6,7 @@ module Grape
6
6
  # It allows to insert and insert after
7
7
  class Stack
8
8
  extend Forwardable
9
+
9
10
  class Middleware
10
11
  attr_reader :args, :block, :klass
11
12
 
@@ -59,9 +60,9 @@ module Grape
59
60
 
60
61
  alias insert_before insert
61
62
 
62
- def insert_after(index, *args, &block)
63
+ def insert_after(index, ...)
63
64
  index = assert_index(index, :after)
64
- insert(index + 1, *args, &block)
65
+ insert(index + 1, ...)
65
66
  end
66
67
 
67
68
  def use(klass, *args, &block)
@@ -18,8 +18,8 @@ module Grape
18
18
  # route.
19
19
  class AcceptVersionHeader < Base
20
20
  def before
21
- potential_version = env['HTTP_ACCEPT_VERSION'].try(:scrub)
22
- not_acceptable!('Accept-Version header must be set.') if strict? && potential_version.blank?
21
+ potential_version = try_scrub(env['HTTP_ACCEPT_VERSION'])
22
+ not_acceptable!('Accept-Version header must be set.') if strict && potential_version.blank?
23
23
 
24
24
  return if potential_version.blank?
25
25
 
@@ -30,7 +30,7 @@ module Grape
30
30
  private
31
31
 
32
32
  def not_acceptable!(message)
33
- throw :error, status: 406, headers: error_headers, message: message
33
+ throw :error, status: 406, headers: error_headers, message:
34
34
  end
35
35
  end
36
36
  end