grape 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/README.md +362 -316
  4. data/UPGRADING.md +197 -7
  5. data/grape.gemspec +5 -6
  6. data/lib/grape/api/instance.rb +13 -10
  7. data/lib/grape/api.rb +17 -8
  8. data/lib/grape/content_types.rb +0 -2
  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/inside_route.rb +38 -13
  14. data/lib/grape/dsl/parameters.rb +4 -3
  15. data/lib/grape/dsl/routing.rb +20 -4
  16. data/lib/grape/dsl/validations.rb +13 -0
  17. data/lib/grape/endpoint.rb +12 -15
  18. data/lib/grape/{util/env.rb → env.rb} +0 -5
  19. data/lib/grape/error_formatter/txt.rb +11 -10
  20. data/lib/grape/exceptions/base.rb +3 -3
  21. data/lib/grape/exceptions/validation.rb +0 -2
  22. data/lib/grape/exceptions/validation_array_errors.rb +1 -0
  23. data/lib/grape/exceptions/validation_errors.rb +1 -3
  24. data/lib/grape/extensions/hash.rb +5 -1
  25. data/lib/grape/http/headers.rb +18 -34
  26. data/lib/grape/{util/json.rb → json.rb} +1 -3
  27. data/lib/grape/locale/en.yml +3 -0
  28. data/lib/grape/middleware/auth/base.rb +0 -2
  29. data/lib/grape/middleware/auth/dsl.rb +0 -2
  30. data/lib/grape/middleware/base.rb +0 -2
  31. data/lib/grape/middleware/error.rb +55 -50
  32. data/lib/grape/middleware/formatter.rb +16 -13
  33. data/lib/grape/middleware/globals.rb +1 -3
  34. data/lib/grape/middleware/stack.rb +2 -3
  35. data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
  36. data/lib/grape/middleware/versioner/header.rb +17 -163
  37. data/lib/grape/middleware/versioner/param.rb +2 -4
  38. data/lib/grape/middleware/versioner/path.rb +1 -3
  39. data/lib/grape/namespace.rb +3 -4
  40. data/lib/grape/path.rb +24 -29
  41. data/lib/grape/request.rb +4 -12
  42. data/lib/grape/router/base_route.rb +39 -0
  43. data/lib/grape/router/greedy_route.rb +20 -0
  44. data/lib/grape/router/pattern.rb +39 -30
  45. data/lib/grape/router/route.rb +22 -59
  46. data/lib/grape/router.rb +30 -36
  47. data/lib/grape/util/accept/header.rb +19 -0
  48. data/lib/grape/util/accept_header_handler.rb +105 -0
  49. data/lib/grape/util/base_inheritable.rb +4 -4
  50. data/lib/grape/util/cache.rb +0 -3
  51. data/lib/grape/util/endpoint_configuration.rb +1 -1
  52. data/lib/grape/util/header.rb +13 -0
  53. data/lib/grape/util/inheritable_values.rb +0 -2
  54. data/lib/grape/util/lazy/block.rb +29 -0
  55. data/lib/grape/util/lazy/object.rb +45 -0
  56. data/lib/grape/util/lazy/value.rb +38 -0
  57. data/lib/grape/util/lazy/value_array.rb +21 -0
  58. data/lib/grape/util/lazy/value_enumerable.rb +34 -0
  59. data/lib/grape/util/lazy/value_hash.rb +21 -0
  60. data/lib/grape/util/media_type.rb +70 -0
  61. data/lib/grape/util/reverse_stackable_values.rb +1 -6
  62. data/lib/grape/util/stackable_values.rb +1 -6
  63. data/lib/grape/util/strict_hash_configuration.rb +3 -3
  64. data/lib/grape/validations/attributes_doc.rb +38 -36
  65. data/lib/grape/validations/contract_scope.rb +71 -0
  66. data/lib/grape/validations/params_scope.rb +10 -9
  67. data/lib/grape/validations/types/array_coercer.rb +0 -2
  68. data/lib/grape/validations/types/build_coercer.rb +69 -71
  69. data/lib/grape/validations/types/dry_type_coercer.rb +1 -11
  70. data/lib/grape/validations/types/json.rb +0 -2
  71. data/lib/grape/validations/types/primitive_coercer.rb +0 -2
  72. data/lib/grape/validations/types/set_coercer.rb +0 -3
  73. data/lib/grape/validations/types.rb +0 -3
  74. data/lib/grape/validations/validators/base.rb +1 -0
  75. data/lib/grape/validations/validators/default_validator.rb +5 -1
  76. data/lib/grape/validations/validators/length_validator.rb +42 -0
  77. data/lib/grape/validations/validators/values_validator.rb +6 -1
  78. data/lib/grape/validations.rb +3 -7
  79. data/lib/grape/version.rb +1 -1
  80. data/lib/grape/{util/xml.rb → xml.rb} +1 -1
  81. data/lib/grape.rb +30 -274
  82. metadata +31 -37
  83. data/lib/grape/eager_load.rb +0 -20
  84. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
  85. data/lib/grape/router/attribute_translator.rb +0 -63
  86. data/lib/grape/util/lazy_block.rb +0 -27
  87. data/lib/grape/util/lazy_object.rb +0 -43
  88. data/lib/grape/util/lazy_value.rb +0 -91
@@ -13,8 +13,8 @@ module Grape
13
13
  attr_reader :env, :request, :headers, :params
14
14
 
15
15
  class << self
16
- def new(*args, &block)
17
- self == Endpoint ? Class.new(Endpoint).new(*args, &block) : super
16
+ def new(...)
17
+ self == Endpoint ? Class.new(Endpoint).new(...) : super
18
18
  end
19
19
 
20
20
  def before_each(new_setup = false, &block)
@@ -55,7 +55,7 @@ module Grape
55
55
 
56
56
  proc do |endpoint_instance|
57
57
  ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: endpoint_instance) do
58
- method.bind(endpoint_instance).call
58
+ method.bind_call(endpoint_instance)
59
59
  end
60
60
  end
61
61
  end
@@ -151,7 +151,7 @@ module Grape
151
151
  reset_routes!
152
152
  routes.each do |route|
153
153
  methods = [route.request_method]
154
- methods << Grape::Http::Headers::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
154
+ methods << Rack::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Rack::GET
155
155
  methods.each do |method|
156
156
  route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h) unless route.request_method == method
157
157
  router.append(route.apply(self))
@@ -190,10 +190,11 @@ module Grape
190
190
  end
191
191
 
192
192
  def prepare_version
193
- version = namespace_inheritable(:version) || []
193
+ version = namespace_inheritable(:version)
194
+ return unless version
194
195
  return if version.empty?
195
196
 
196
- version.length == 1 ? version.first.to_s : version
197
+ version.length == 1 ? version.first : version
197
198
  end
198
199
 
199
200
  def merge_route_options(**default)
@@ -206,7 +207,7 @@ module Grape
206
207
 
207
208
  def prepare_path(path)
208
209
  path_settings = inheritable_setting.to_hash[:namespace_stackable].merge(inheritable_setting.to_hash[:namespace_inheritable])
209
- Path.prepare(path, namespace, path_settings)
210
+ Path.new(path, namespace, path_settings)
210
211
  end
211
212
 
212
213
  def namespace
@@ -238,7 +239,7 @@ module Grape
238
239
 
239
240
  def run
240
241
  ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
241
- @header = {}
242
+ @header = Grape::Util::Header.new
242
243
  @request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with))
243
244
  @params = @request.params
244
245
  @headers = @request.headers
@@ -401,15 +402,11 @@ module Grape
401
402
 
402
403
  def options?
403
404
  options[:options_route_enabled] &&
404
- env[Grape::Http::Headers::REQUEST_METHOD] == Grape::Http::Headers::OPTIONS
405
+ env[Rack::REQUEST_METHOD] == Rack::OPTIONS
405
406
  end
406
407
 
407
- def method_missing(name, *_args)
408
- raise NoMethodError.new("undefined method `#{name}' for #{self.class} in `#{route.origin}' endpoint")
409
- end
410
-
411
- def respond_to_missing?(method_name, include_private = false)
412
- super
408
+ def inspect
409
+ "#{self.class} in `#{route.origin}' endpoint"
413
410
  end
414
411
  end
415
412
  end
@@ -11,11 +11,6 @@ module Grape
11
11
  API_VENDOR = 'api.vendor'
12
12
  API_FORMAT = 'api.format'
13
13
 
14
- RACK_INPUT = 'rack.input'
15
- RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
16
- RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
17
- RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
18
-
19
14
  GRAPE_REQUEST = 'grape.request'
20
15
  GRAPE_REQUEST_HEADERS = 'grape.request.headers'
21
16
  GRAPE_REQUEST_PARAMS = 'grape.request.params'
@@ -10,16 +10,17 @@ module Grape
10
10
  message = present(message, env)
11
11
 
12
12
  result = message.is_a?(Hash) ? ::Grape::Json.dump(message) : message
13
- rescue_options = options[:rescue_options] || {}
14
- if rescue_options[:backtrace] && backtrace && !backtrace.empty?
15
- result += "\r\n backtrace:"
16
- result += backtrace.join("\r\n ")
17
- end
18
- if rescue_options[:original_exception] && original_exception
19
- result += "\r\n original exception:"
20
- result += "\r\n #{original_exception.inspect}"
21
- end
22
- result
13
+ Array.wrap(result).tap do |final_result|
14
+ rescue_options = options[:rescue_options] || {}
15
+ if rescue_options[:backtrace] && backtrace.present?
16
+ final_result << 'backtrace:'
17
+ final_result.concat(backtrace)
18
+ end
19
+ if rescue_options[:original_exception] && original_exception
20
+ final_result << 'original exception:'
21
+ final_result << original_exception.inspect
22
+ end
23
+ end.join("\r\n ")
23
24
  end
24
25
  end
25
26
  end
@@ -41,15 +41,15 @@ module Grape
41
41
  end
42
42
 
43
43
  def problem(key, **attributes)
44
- translate_message("#{key}.problem".to_sym, **attributes)
44
+ translate_message(:"#{key}.problem", **attributes)
45
45
  end
46
46
 
47
47
  def summary(key, **attributes)
48
- translate_message("#{key}.summary".to_sym, **attributes)
48
+ translate_message(:"#{key}.summary", **attributes)
49
49
  end
50
50
 
51
51
  def resolution(key, **attributes)
52
- translate_message("#{key}.resolution".to_sym, **attributes)
52
+ translate_message(:"#{key}.resolution", **attributes)
53
53
  end
54
54
 
55
55
  def translate_attributes(keys, **options)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/exceptions/base'
4
-
5
3
  module Grape
6
4
  module Exceptions
7
5
  class Validation < Grape::Exceptions::Base
@@ -6,6 +6,7 @@ module Grape
6
6
  attr_reader :errors
7
7
 
8
8
  def initialize(errors)
9
+ super()
9
10
  @errors = errors
10
11
  end
11
12
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/exceptions/base'
4
-
5
3
  module Grape
6
4
  module Exceptions
7
5
  class ValidationErrors < Grape::Exceptions::Base
@@ -14,7 +12,7 @@ module Grape
14
12
 
15
13
  def initialize(errors: [], headers: {}, **_options)
16
14
  @errors = errors.group_by(&:params)
17
- super message: full_messages.join(', '), status: 400, headers: headers
15
+ super(message: full_messages.join(', '), status: 400, headers: headers)
18
16
  end
19
17
 
20
18
  def each
@@ -12,8 +12,12 @@ module Grape
12
12
 
13
13
  def build_params
14
14
  rack_params.deep_dup.tap do |params|
15
- params.deep_merge!(grape_routing_args) if env.key?(Grape::Env::GRAPE_ROUTING_ARGS)
16
15
  params.deep_symbolize_keys!
16
+
17
+ if env.key?(Grape::Env::GRAPE_ROUTING_ARGS)
18
+ grape_routing_args.deep_symbolize_keys!
19
+ params.deep_merge!(grape_routing_args)
20
+ end
17
21
  end
18
22
  end
19
23
  end
@@ -1,46 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/util/lazy_object'
4
-
5
3
  module Grape
6
4
  module Http
7
5
  module Headers
8
- # https://github.com/rack/rack/blob/master/lib/rack.rb
9
- HTTP_VERSION = 'HTTP_VERSION'
10
- PATH_INFO = 'PATH_INFO'
11
- REQUEST_METHOD = 'REQUEST_METHOD'
12
- QUERY_STRING = 'QUERY_STRING'
13
-
14
- if Grape.lowercase_headers?
15
- ALLOW = 'allow'
16
- LOCATION = 'location'
17
- TRANSFER_ENCODING = 'transfer-encoding'
18
- X_CASCADE = 'x-cascade'
19
- else
20
- ALLOW = 'Allow'
21
- LOCATION = 'Location'
22
- TRANSFER_ENCODING = 'Transfer-Encoding'
23
- X_CASCADE = 'X-Cascade'
24
- end
25
-
26
- GET = 'GET'
27
- POST = 'POST'
28
- PUT = 'PUT'
29
- PATCH = 'PATCH'
30
- DELETE = 'DELETE'
31
- HEAD = 'HEAD'
32
- OPTIONS = 'OPTIONS'
6
+ HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION'
7
+ HTTP_ACCEPT = 'HTTP_ACCEPT'
8
+ HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING'
33
9
 
34
- SUPPORTED_METHODS = [GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS].freeze
35
- SUPPORTED_METHODS_WITHOUT_OPTIONS = Grape::Util::LazyObject.new { [GET, POST, PUT, PATCH, DELETE, HEAD].freeze }
10
+ ALLOW = 'Allow'
11
+ LOCATION = 'Location'
12
+ X_CASCADE = 'X-Cascade'
13
+ TRANSFER_ENCODING = 'Transfer-Encoding'
36
14
 
37
- HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION'
38
- HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING'
39
- HTTP_ACCEPT = 'HTTP_ACCEPT'
15
+ SUPPORTED_METHODS = [
16
+ Rack::GET,
17
+ Rack::POST,
18
+ Rack::PUT,
19
+ Rack::PATCH,
20
+ Rack::DELETE,
21
+ Rack::HEAD,
22
+ Rack::OPTIONS
23
+ ].freeze
40
24
 
41
- FORMAT = 'format'
25
+ SUPPORTED_METHODS_WITHOUT_OPTIONS = (SUPPORTED_METHODS - [Rack::OPTIONS]).freeze
42
26
 
43
- HTTP_HEADERS = Grape::Util::LazyObject.new do
27
+ HTTP_HEADERS = Grape::Util::Lazy::Object.new do
44
28
  common_http_headers = %w[
45
29
  Version
46
30
  Host
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
-
5
3
  module Grape
6
- if Object.const_defined? :MultiJson
4
+ if defined?(::MultiJson)
7
5
  Json = ::MultiJson
8
6
  else
9
7
  Json = ::JSON
@@ -10,6 +10,9 @@ en:
10
10
  values: 'does not have a valid value'
11
11
  except_values: 'has a value not allowed'
12
12
  same_as: 'is not the same as %{parameter}'
13
+ length: 'is expected to have length within %{min} and %{max}'
14
+ length_min: 'is expected to have length greater than or equal to %{min}'
15
+ length_max: 'is expected to have length less than or equal to %{max}'
13
16
  missing_vendor_option:
14
17
  problem: 'missing :vendor option'
15
18
  summary: 'when version using header, you must specify :vendor option'
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rack/auth/basic'
4
-
5
3
  module Grape
6
4
  module Middleware
7
5
  module Auth
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rack/auth/basic'
4
-
5
3
  module Grape
6
4
  module Middleware
7
5
  module Auth
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/dsl/headers'
4
-
5
3
  module Grape
6
4
  module Middleware
7
5
  class Base
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/middleware/base'
4
- require 'active_support/core_ext/string/output_safety'
5
-
6
3
  module Grape
7
4
  module Middleware
8
5
  class Error < Base
@@ -34,66 +31,59 @@ module Grape
34
31
 
35
32
  def call!(env)
36
33
  @env = env
37
- begin
38
- error_response(catch(:error) do
39
- return @app.call(@env)
40
- end)
41
- rescue Exception => e # rubocop:disable Lint/RescueException
42
- handler =
43
- rescue_handler_for_base_only_class(e.class) ||
44
- rescue_handler_for_class_or_its_ancestor(e.class) ||
45
- rescue_handler_for_grape_exception(e.class) ||
46
- rescue_handler_for_any_class(e.class) ||
47
- raise
48
-
49
- run_rescue_handler(handler, e)
50
- end
51
- end
52
-
53
- def error!(message, status = options[:default_status], headers = {}, backtrace = [], original_exception = nil)
54
- headers = headers.reverse_merge(Rack::CONTENT_TYPE => content_type)
55
- rack_response(format_message(message, backtrace, original_exception), status, headers)
34
+ error_response(catch(:error) { return @app.call(@env) })
35
+ rescue Exception => e # rubocop:disable Lint/RescueException
36
+ run_rescue_handler(find_handler(e.class), e, @env[Grape::Env::API_ENDPOINT])
56
37
  end
57
38
 
58
- def default_rescue_handler(e)
59
- error_response(message: e.message, backtrace: e.backtrace, original_exception: e)
60
- end
61
-
62
- # TODO: This method is deprecated. Refactor out.
63
- def error_response(error = {})
64
- status = error[:status] || options[:default_status]
65
- message = error[:message] || options[:default_message]
66
- headers = { Rack::CONTENT_TYPE => content_type }
67
- headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
68
- backtrace = error[:backtrace] || error[:original_exception]&.backtrace || []
69
- original_exception = error.is_a?(Exception) ? error : error[:original_exception] || nil
70
- rack_response(format_message(message, backtrace, original_exception), status, headers)
71
- end
39
+ private
72
40
 
73
- def rack_response(message, status = options[:default_status], headers = { Rack::CONTENT_TYPE => content_type })
74
- message = ERB::Util.html_escape(message) if headers[Rack::CONTENT_TYPE] == TEXT_HTML
75
- Rack::Response.new([message], Rack::Utils.status_code(status), headers)
41
+ def rack_response(status, headers, message)
42
+ message = Rack::Utils.escape_html(message) if headers[Rack::CONTENT_TYPE] == TEXT_HTML
43
+ Rack::Response.new(Array.wrap(message), Rack::Utils.status_code(status), Grape::Util::Header.new.merge(headers))
76
44
  end
77
45
 
78
46
  def format_message(message, backtrace, original_exception = nil)
79
47
  format = env[Grape::Env::API_FORMAT] || options[:format]
80
48
  formatter = Grape::ErrorFormatter.formatter_for(format, **options)
49
+ return formatter.call(message, backtrace, options, env, original_exception) if formatter
50
+
81
51
  throw :error,
82
52
  status: 406,
83
53
  message: "The requested format '#{format}' is not supported.",
84
54
  backtrace: backtrace,
85
- original_exception: original_exception unless formatter
86
- formatter.call(message, backtrace, options, env, original_exception)
55
+ original_exception: original_exception
87
56
  end
88
57
 
89
- private
58
+ def find_handler(klass)
59
+ rescue_handler_for_base_only_class(klass) ||
60
+ rescue_handler_for_class_or_its_ancestor(klass) ||
61
+ rescue_handler_for_grape_exception(klass) ||
62
+ rescue_handler_for_any_class(klass) ||
63
+ raise
64
+ end
65
+
66
+ def error_response(error = {})
67
+ status = error[:status] || options[:default_status]
68
+ message = error[:message] || options[:default_message]
69
+ headers = { Rack::CONTENT_TYPE => content_type }.tap do |h|
70
+ h.merge!(error[:headers]) if error[:headers].is_a?(Hash)
71
+ end
72
+ backtrace = error[:backtrace] || error[:original_exception]&.backtrace || []
73
+ original_exception = error.is_a?(Exception) ? error : error[:original_exception] || nil
74
+ rack_response(status, headers, format_message(message, backtrace, original_exception))
75
+ end
76
+
77
+ def default_rescue_handler(e)
78
+ error_response(message: e.message, backtrace: e.backtrace, original_exception: e)
79
+ end
90
80
 
91
81
  def rescue_handler_for_base_only_class(klass)
92
82
  error, handler = options[:base_only_rescue_handlers].find { |err, _handler| klass == err }
93
83
 
94
84
  return unless error
95
85
 
96
- handler || :default_rescue_handler
86
+ handler || method(:default_rescue_handler)
97
87
  end
98
88
 
99
89
  def rescue_handler_for_class_or_its_ancestor(klass)
@@ -101,39 +91,54 @@ module Grape
101
91
 
102
92
  return unless error
103
93
 
104
- handler || :default_rescue_handler
94
+ handler || method(:default_rescue_handler)
105
95
  end
106
96
 
107
97
  def rescue_handler_for_grape_exception(klass)
108
98
  return unless klass <= Grape::Exceptions::Base
109
- return :error_response if klass == Grape::Exceptions::InvalidVersionHeader
99
+ return method(:error_response) if klass == Grape::Exceptions::InvalidVersionHeader
110
100
  return unless options[:rescue_grape_exceptions] || !options[:rescue_all]
111
101
 
112
- options[:grape_exceptions_rescue_handler] || :error_response
102
+ options[:grape_exceptions_rescue_handler] || method(:error_response)
113
103
  end
114
104
 
115
105
  def rescue_handler_for_any_class(klass)
116
106
  return unless klass <= StandardError
117
107
  return unless options[:rescue_all] || options[:rescue_grape_exceptions]
118
108
 
119
- options[:all_rescue_handler] || :default_rescue_handler
109
+ options[:all_rescue_handler] || method(:default_rescue_handler)
120
110
  end
121
111
 
122
- def run_rescue_handler(handler, error)
112
+ def run_rescue_handler(handler, error, endpoint)
123
113
  if handler.instance_of?(Symbol)
124
114
  raise NoMethodError, "undefined method '#{handler}'" unless respond_to?(handler)
125
115
 
126
116
  handler = public_method(handler)
127
117
  end
128
118
 
129
- response = handler.arity.zero? ? instance_exec(&handler) : instance_exec(error, &handler)
119
+ response = catch(:error) do
120
+ handler.arity.zero? ? endpoint.instance_exec(&handler) : endpoint.instance_exec(error, &handler)
121
+ end
122
+
123
+ response = error!(response[:message], response[:status], response[:headers]) if error?(response)
130
124
 
131
125
  if response.is_a?(Rack::Response)
132
126
  response
133
127
  else
134
- run_rescue_handler(:default_rescue_handler, Grape::Exceptions::InvalidResponse.new)
128
+ run_rescue_handler(:default_rescue_handler, Grape::Exceptions::InvalidResponse.new, endpoint)
135
129
  end
136
130
  end
131
+
132
+ def error!(message, status = options[:default_status], headers = {}, backtrace = [], original_exception = nil)
133
+ rack_response(
134
+ status, headers.reverse_merge(Rack::CONTENT_TYPE => content_type),
135
+ format_message(message, backtrace, original_exception)
136
+ )
137
+ end
138
+
139
+ def error?(response)
140
+ response.is_a?(Hash) && response[:message] && response[:status] && response[:headers]
141
+ end
137
142
  end
138
143
  end
139
144
  end
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/middleware/base'
4
-
5
3
  module Grape
6
4
  module Middleware
7
5
  class Formatter < Base
8
6
  CHUNKED = 'chunked'
7
+ FORMAT = 'format'
9
8
 
10
9
  def default_options
11
10
  {
@@ -26,7 +25,7 @@ module Grape
26
25
  status, headers, bodies = *@app_response
27
26
 
28
27
  if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
29
- @app_response
28
+ [status, headers, []]
30
29
  else
31
30
  build_formatted_response(status, headers, bodies)
32
31
  end
@@ -82,14 +81,14 @@ module Grape
82
81
  !request.parseable_data? &&
83
82
  (request.content_length.to_i.positive? || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == CHUNKED)
84
83
 
85
- return unless (input = env[Grape::Env::RACK_INPUT])
84
+ return unless (input = env[Rack::RACK_INPUT])
86
85
 
87
- input.rewind
86
+ rewind_input input
88
87
  body = env[Grape::Env::API_REQUEST_INPUT] = input.read
89
88
  begin
90
89
  read_rack_input(body) if body && !body.empty?
91
90
  ensure
92
- input.rewind
91
+ rewind_input input
93
92
  end
94
93
  end
95
94
 
@@ -103,12 +102,12 @@ module Grape
103
102
  begin
104
103
  body = (env[Grape::Env::API_REQUEST_BODY] = parser.call(body, env))
105
104
  if body.is_a?(Hash)
106
- env[Grape::Env::RACK_REQUEST_FORM_HASH] = if env.key?(Grape::Env::RACK_REQUEST_FORM_HASH)
107
- env[Grape::Env::RACK_REQUEST_FORM_HASH].merge(body)
108
- else
109
- body
110
- end
111
- env[Grape::Env::RACK_REQUEST_FORM_INPUT] = env[Grape::Env::RACK_INPUT]
105
+ env[Rack::RACK_REQUEST_FORM_HASH] = if env.key?(Rack::RACK_REQUEST_FORM_HASH)
106
+ env[Rack::RACK_REQUEST_FORM_HASH].merge(body)
107
+ else
108
+ body
109
+ end
110
+ env[Rack::RACK_REQUEST_FORM_INPUT] = env[Rack::RACK_INPUT]
112
111
  end
113
112
  rescue Grape::Exceptions::Base => e
114
113
  raise e
@@ -141,7 +140,7 @@ module Grape
141
140
  end
142
141
 
143
142
  def format_from_params
144
- fmt = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[Grape::Http::Headers::FORMAT]
143
+ fmt = Rack::Utils.parse_nested_query(env[Rack::QUERY_STRING])[FORMAT]
145
144
  # avoid symbol memory leak on an unknown format
146
145
  return fmt.to_sym if content_type_for(fmt)
147
146
 
@@ -174,6 +173,10 @@ module Grape
174
173
  .sort_by { |_, quality_preference| -(quality_preference ? quality_preference.to_f : 1.0) }
175
174
  .flat_map { |mime, _| [mime, mime.sub(vendor_prefix_pattern, '')] }
176
175
  end
176
+
177
+ def rewind_input(input)
178
+ input.rewind if input.respond_to?(:rewind)
179
+ end
177
180
  end
178
181
  end
179
182
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/middleware/base'
4
-
5
3
  module Grape
6
4
  module Middleware
7
5
  class Globals < Base
@@ -9,7 +7,7 @@ module Grape
9
7
  request = Grape::Request.new(@env, build_params_with: @options[:build_params_with])
10
8
  @env[Grape::Env::GRAPE_REQUEST] = request
11
9
  @env[Grape::Env::GRAPE_REQUEST_HEADERS] = request.headers
12
- @env[Grape::Env::GRAPE_REQUEST_PARAMS] = request.params if @env[Grape::Env::RACK_INPUT]
10
+ @env[Grape::Env::GRAPE_REQUEST_PARAMS] = request.params if @env[Rack::RACK_INPUT]
13
11
  end
14
12
  end
15
13
  end
@@ -76,11 +76,10 @@ module Grape
76
76
  end
77
77
  ruby2_keywords :insert_after if respond_to?(:ruby2_keywords, true)
78
78
 
79
- def use(*args, &block)
80
- middleware = self.class::Middleware.new(*args, &block)
79
+ def use(...)
80
+ middleware = self.class::Middleware.new(...)
81
81
  middlewares.push(middleware)
82
82
  end
83
- ruby2_keywords :use if respond_to?(:ruby2_keywords, true)
84
83
 
85
84
  def merge_with(middleware_specs)
86
85
  middleware_specs.each do |operation, *args|
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/middleware/base'
4
-
5
3
  module Grape
6
4
  module Middleware
7
5
  module Versioner