grape 1.3.3 → 1.6.2

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 (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -2
  3. data/CONTRIBUTING.md +2 -1
  4. data/README.md +135 -23
  5. data/UPGRADING.md +237 -46
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +34 -42
  8. data/lib/grape/api.rb +21 -16
  9. data/lib/grape/cookies.rb +2 -0
  10. data/lib/grape/dsl/callbacks.rb +1 -1
  11. data/lib/grape/dsl/desc.rb +3 -5
  12. data/lib/grape/dsl/headers.rb +5 -2
  13. data/lib/grape/dsl/helpers.rb +8 -5
  14. data/lib/grape/dsl/inside_route.rb +72 -53
  15. data/lib/grape/dsl/middleware.rb +4 -4
  16. data/lib/grape/dsl/parameters.rb +11 -7
  17. data/lib/grape/dsl/request_response.rb +9 -6
  18. data/lib/grape/dsl/routing.rb +8 -9
  19. data/lib/grape/dsl/settings.rb +5 -5
  20. data/lib/grape/dsl/validations.rb +18 -1
  21. data/lib/grape/eager_load.rb +1 -1
  22. data/lib/grape/endpoint.rb +29 -42
  23. data/lib/grape/error_formatter/json.rb +2 -6
  24. data/lib/grape/error_formatter/xml.rb +2 -6
  25. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  26. data/lib/grape/exceptions/validation.rb +2 -3
  27. data/lib/grape/exceptions/validation_errors.rb +1 -1
  28. data/lib/grape/formatter/json.rb +1 -0
  29. data/lib/grape/formatter/serializable_hash.rb +2 -1
  30. data/lib/grape/formatter/xml.rb +1 -0
  31. data/lib/grape/locale/en.yml +1 -1
  32. data/lib/grape/middleware/auth/base.rb +3 -3
  33. data/lib/grape/middleware/auth/dsl.rb +7 -1
  34. data/lib/grape/middleware/base.rb +6 -3
  35. data/lib/grape/middleware/error.rb +11 -13
  36. data/lib/grape/middleware/formatter.rb +7 -7
  37. data/lib/grape/middleware/stack.rb +10 -3
  38. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  39. data/lib/grape/middleware/versioner/header.rb +6 -4
  40. data/lib/grape/middleware/versioner/param.rb +1 -0
  41. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  42. data/lib/grape/middleware/versioner/path.rb +2 -0
  43. data/lib/grape/parser/json.rb +1 -1
  44. data/lib/grape/parser/xml.rb +1 -1
  45. data/lib/grape/path.rb +1 -0
  46. data/lib/grape/request.rb +4 -1
  47. data/lib/grape/router/attribute_translator.rb +3 -3
  48. data/lib/grape/router/pattern.rb +1 -1
  49. data/lib/grape/router/route.rb +2 -2
  50. data/lib/grape/router.rb +31 -30
  51. data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
  52. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
  53. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
  54. data/lib/grape/util/base_inheritable.rb +2 -2
  55. data/lib/grape/util/inheritable_setting.rb +1 -3
  56. data/lib/grape/util/lazy_value.rb +4 -2
  57. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  58. data/lib/grape/validations/attributes_iterator.rb +8 -0
  59. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  60. data/lib/grape/validations/params_scope.rb +97 -62
  61. data/lib/grape/validations/single_attribute_iterator.rb +1 -1
  62. data/lib/grape/validations/types/custom_type_coercer.rb +16 -3
  63. data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
  64. data/lib/grape/validations/types/invalid_value.rb +24 -0
  65. data/lib/grape/validations/types/json.rb +2 -1
  66. data/lib/grape/validations/types/primitive_coercer.rb +4 -5
  67. data/lib/grape/validations/types.rb +1 -4
  68. data/lib/grape/validations/validator_factory.rb +1 -1
  69. data/lib/grape/validations/validators/all_or_none.rb +8 -5
  70. data/lib/grape/validations/validators/allow_blank.rb +9 -7
  71. data/lib/grape/validations/validators/as.rb +6 -8
  72. data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
  73. data/lib/grape/validations/validators/base.rb +74 -69
  74. data/lib/grape/validations/validators/coerce.rb +63 -76
  75. data/lib/grape/validations/validators/default.rb +36 -34
  76. data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
  77. data/lib/grape/validations/validators/except_values.rb +13 -11
  78. data/lib/grape/validations/validators/multiple_params_base.rb +24 -19
  79. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
  80. data/lib/grape/validations/validators/presence.rb +7 -4
  81. data/lib/grape/validations/validators/regexp.rb +8 -5
  82. data/lib/grape/validations/validators/same_as.rb +18 -15
  83. data/lib/grape/validations/validators/values.rb +61 -56
  84. data/lib/grape/validations.rb +6 -0
  85. data/lib/grape/version.rb +1 -1
  86. data/lib/grape.rb +7 -3
  87. data/spec/grape/api/custom_validations_spec.rb +77 -45
  88. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  89. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  90. data/spec/grape/api/invalid_format_spec.rb +2 -0
  91. data/spec/grape/api/recognize_path_spec.rb +1 -1
  92. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  93. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  94. data/spec/grape/api_remount_spec.rb +25 -19
  95. data/spec/grape/api_spec.rb +576 -211
  96. data/spec/grape/dsl/callbacks_spec.rb +2 -1
  97. data/spec/grape/dsl/headers_spec.rb +39 -9
  98. data/spec/grape/dsl/helpers_spec.rb +3 -2
  99. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  100. data/spec/grape/dsl/logger_spec.rb +16 -18
  101. data/spec/grape/dsl/middleware_spec.rb +2 -1
  102. data/spec/grape/dsl/parameters_spec.rb +2 -0
  103. data/spec/grape/dsl/request_response_spec.rb +1 -0
  104. data/spec/grape/dsl/routing_spec.rb +10 -7
  105. data/spec/grape/endpoint/declared_spec.rb +848 -0
  106. data/spec/grape/endpoint_spec.rb +77 -589
  107. data/spec/grape/entity_spec.rb +29 -23
  108. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  109. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  110. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  111. data/spec/grape/exceptions/validation_spec.rb +5 -3
  112. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  113. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  114. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  115. data/spec/grape/integration/rack_sendfile_spec.rb +13 -9
  116. data/spec/grape/loading_spec.rb +8 -8
  117. data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
  118. data/spec/grape/middleware/auth/strategies_spec.rb +61 -21
  119. data/spec/grape/middleware/base_spec.rb +24 -15
  120. data/spec/grape/middleware/error_spec.rb +3 -3
  121. data/spec/grape/middleware/exception_spec.rb +111 -161
  122. data/spec/grape/middleware/formatter_spec.rb +28 -7
  123. data/spec/grape/middleware/globals_spec.rb +7 -4
  124. data/spec/grape/middleware/stack_spec.rb +15 -12
  125. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  126. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  127. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  128. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  129. data/spec/grape/middleware/versioner_spec.rb +1 -1
  130. data/spec/grape/parser_spec.rb +4 -0
  131. data/spec/grape/path_spec.rb +52 -52
  132. data/spec/grape/presenters/presenter_spec.rb +7 -6
  133. data/spec/grape/request_spec.rb +6 -4
  134. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  135. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  136. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  137. data/spec/grape/util/stackable_values_spec.rb +7 -5
  138. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  139. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +14 -3
  140. data/spec/grape/validations/params_scope_spec.rb +72 -10
  141. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -6
  142. data/spec/grape/validations/types/primitive_coercer_spec.rb +63 -7
  143. data/spec/grape/validations/types_spec.rb +8 -8
  144. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  145. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  146. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  147. data/spec/grape/validations/validators/coerce_spec.rb +248 -33
  148. data/spec/grape/validations/validators/default_spec.rb +121 -78
  149. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  150. data/spec/grape/validations/validators/except_values_spec.rb +4 -3
  151. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  152. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  153. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  154. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  155. data/spec/grape/validations/validators/values_spec.rb +183 -178
  156. data/spec/grape/validations_spec.rb +342 -29
  157. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  158. data/spec/integration/multi_json/json_spec.rb +1 -1
  159. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  160. data/spec/shared/versioning_examples.rb +32 -29
  161. data/spec/spec_helper.rb +12 -12
  162. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  163. data/spec/support/chunks.rb +14 -0
  164. data/spec/support/versioned_helpers.rb +4 -6
  165. metadata +110 -102
@@ -20,7 +20,8 @@ module Grape
20
20
  def before_each(new_setup = false, &block)
21
21
  @before_each ||= []
22
22
  if new_setup == false
23
- return @before_each unless block_given?
23
+ return @before_each unless block
24
+
24
25
  @before_each << block
25
26
  else
26
27
  @before_each = [new_setup]
@@ -46,9 +47,7 @@ module Grape
46
47
  # @return [Proc]
47
48
  # @raise [NameError] an instance method with the same name already exists
48
49
  def generate_api_method(method_name, &block)
49
- if method_defined?(method_name)
50
- raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
51
- end
50
+ raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") if method_defined?(method_name)
52
51
 
53
52
  define_method(method_name, &block)
54
53
  method = instance_method(method_name)
@@ -80,7 +79,10 @@ module Grape
80
79
 
81
80
  self.inheritable_setting = new_settings.point_in_time_copy
82
81
 
83
- route_setting(:saved_declared_params, namespace_stackable(:declared_params))
82
+ # now +namespace_stackable(:declared_params)+ contains all params defined for
83
+ # this endpoint and its parents, but later it will be cleaned up,
84
+ # see +reset_validations!+ in lib/grape/dsl/validations.rb
85
+ route_setting(:declared_params, namespace_stackable(:declared_params).flatten)
84
86
  route_setting(:saved_validations, namespace_stackable(:validations))
85
87
 
86
88
  namespace_stackable(:representations, []) unless namespace_stackable(:representations)
@@ -99,11 +101,11 @@ module Grape
99
101
  @block = nil
100
102
 
101
103
  @status = nil
102
- @file = nil
104
+ @stream = nil
103
105
  @body = nil
104
106
  @proc = nil
105
107
 
106
- return unless block_given?
108
+ return unless block
107
109
 
108
110
  @source = block
109
111
  @block = self.class.generate_api_method(method_name, &block)
@@ -115,12 +117,9 @@ module Grape
115
117
  inheritable_setting.route[:saved_validations] += namespace_stackable[:validations]
116
118
  parent_declared_params = namespace_stackable[:declared_params]
117
119
 
118
- if parent_declared_params
119
- inheritable_setting.route[:declared_params] ||= []
120
- inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten)
121
- end
120
+ inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
122
121
 
123
- endpoints && endpoints.each { |e| e.inherit_settings(namespace_stackable) }
122
+ endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
124
123
  end
125
124
 
126
125
  def require_option(options, key)
@@ -140,7 +139,7 @@ module Grape
140
139
  end
141
140
 
142
141
  def reset_routes!
143
- endpoints.each(&:reset_routes!) if endpoints
142
+ endpoints&.each(&:reset_routes!)
144
143
  @namespace = nil
145
144
  @routes = nil
146
145
  end
@@ -152,13 +151,9 @@ module Grape
152
151
  reset_routes!
153
152
  routes.each do |route|
154
153
  methods = [route.request_method]
155
- if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
156
- methods << Grape::Http::Headers::HEAD
157
- end
154
+ methods << Grape::Http::Headers::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
158
155
  methods.each do |method|
159
- unless route.request_method == method
160
- route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h)
161
- end
156
+ route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h) unless route.request_method == method
162
157
  router.append(route.apply(self))
163
158
  end
164
159
  end
@@ -190,7 +185,7 @@ module Grape
190
185
  requirements: prepare_routes_requirements,
191
186
  prefix: namespace_inheritable(:root_prefix),
192
187
  anchor: options[:route_options].fetch(:anchor, true),
193
- settings: inheritable_setting.route.except(:saved_declared_params, :saved_validations),
188
+ settings: inheritable_setting.route.except(:declared_params, :saved_validations),
194
189
  forward_match: options[:forward_match]
195
190
  }
196
191
  end
@@ -198,6 +193,7 @@ module Grape
198
193
  def prepare_version
199
194
  version = namespace_inheritable(:version) || []
200
195
  return if version.empty?
196
+
201
197
  version.length == 1 ? version.first.to_s : version
202
198
  end
203
199
 
@@ -232,7 +228,7 @@ module Grape
232
228
  # Return the collection of endpoints within this endpoint.
233
229
  # This is the case when an Grape::API mounts another Grape::API.
234
230
  def endpoints
235
- options[:app].endpoints if options[:app] && options[:app].respond_to?(:endpoints)
231
+ options[:app].endpoints if options[:app].respond_to?(:endpoints)
236
232
  end
237
233
 
238
234
  def equals?(e)
@@ -253,14 +249,14 @@ module Grape
253
249
  run_filters befores, :before
254
250
 
255
251
  if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
256
- raise Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allowed_methods) unless options?
252
+ raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allowed_methods)) unless options?
253
+
257
254
  header 'Allow', allowed_methods
258
255
  response_object = ''
259
256
  status 204
260
257
  else
261
258
  run_filters before_validations, :before_validation
262
259
  run_validators validations, request
263
- remove_renamed_params
264
260
  run_filters after_validations, :after_validation
265
261
  response_object = execute
266
262
  end
@@ -271,8 +267,8 @@ module Grape
271
267
  # status verifies body presence when DELETE
272
268
  @body ||= response_object
273
269
 
274
- # The body commonly is an Array of Strings, the application instance itself, or a File-like object
275
- response_object = file || [body]
270
+ # The body commonly is an Array of Strings, the application instance itself, or a Stream-like object
271
+ response_object = stream || [body]
276
272
 
277
273
  [status, header, response_object]
278
274
  ensure
@@ -326,14 +322,7 @@ module Grape
326
322
  Module.new { helpers.each { |mod_to_include| include mod_to_include } }
327
323
  end
328
324
 
329
- def remove_renamed_params
330
- return unless route_setting(:renamed_params)
331
- route_setting(:renamed_params).flat_map(&:keys).each do |renamed_param|
332
- @params.delete(renamed_param)
333
- end
334
- end
335
-
336
- private :build_stack, :build_helpers, :remove_renamed_params
325
+ private :build_stack, :build_helpers
337
326
 
338
327
  def execute
339
328
  @block ? @block.call(self) : nil
@@ -363,15 +352,13 @@ module Grape
363
352
 
364
353
  ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do
365
354
  validators.each do |validator|
366
- begin
367
- validator.validate(request)
368
- rescue Grape::Exceptions::Validation => e
369
- validation_errors << e
370
- break if validator.fail_fast?
371
- rescue Grape::Exceptions::ValidationArrayErrors => e
372
- validation_errors.concat e.errors
373
- break if validator.fail_fast?
374
- end
355
+ validator.validate(request)
356
+ rescue Grape::Exceptions::Validation => e
357
+ validation_errors << e
358
+ break if validator.fail_fast?
359
+ rescue Grape::Exceptions::ValidationArrayErrors => e
360
+ validation_errors.concat e.errors
361
+ break if validator.fail_fast?
375
362
  end
376
363
  end
377
364
 
@@ -10,12 +10,8 @@ module Grape
10
10
  result = wrap_message(present(message, env))
11
11
 
12
12
  rescue_options = options[:rescue_options] || {}
13
- if rescue_options[:backtrace] && backtrace && !backtrace.empty?
14
- result = result.merge(backtrace: backtrace)
15
- end
16
- if rescue_options[:original_exception] && original_exception
17
- result = result.merge(original_exception: original_exception.inspect)
18
- end
13
+ result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
14
+ result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
19
15
  ::Grape::Json.dump(result)
20
16
  end
21
17
 
@@ -11,12 +11,8 @@ module Grape
11
11
 
12
12
  result = message.is_a?(Hash) ? message : { message: message }
13
13
  rescue_options = options[:rescue_options] || {}
14
- if rescue_options[:backtrace] && backtrace && !backtrace.empty?
15
- result = result.merge(backtrace: backtrace)
16
- end
17
- if rescue_options[:original_exception] && original_exception
18
- result = result.merge(original_exception: original_exception.inspect)
19
- end
14
+ result = result.merge(backtrace: backtrace) if rescue_options[:backtrace] && backtrace && !backtrace.empty?
15
+ result = result.merge(original_exception: original_exception.inspect) if rescue_options[:original_exception] && original_exception
20
16
  result.respond_to?(:to_xml) ? result.to_xml(root: :error) : result.to_s
21
17
  end
22
18
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Exceptions
5
+ class EmptyMessageBody < Base
6
+ def initialize(body_format)
7
+ super(message: compose_message(:empty_message_body, body_format: body_format), status: 400)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -5,8 +5,7 @@ require 'grape/exceptions/base'
5
5
  module Grape
6
6
  module Exceptions
7
7
  class Validation < Grape::Exceptions::Base
8
- attr_accessor :params
9
- attr_accessor :message_key
8
+ attr_accessor :params, :message_key
10
9
 
11
10
  def initialize(params:, message: nil, **args)
12
11
  @params = params
@@ -17,7 +16,7 @@ module Grape
17
16
  super(**args)
18
17
  end
19
18
 
20
- # remove all the unnecessary stuff from Grape::Exceptions::Base like status
19
+ # Remove all the unnecessary stuff from Grape::Exceptions::Base like status
21
20
  # and headers when converting a validation error to json or string
22
21
  def as_json(*_args)
23
22
  to_s
@@ -39,7 +39,7 @@ module Grape
39
39
  end
40
40
  end
41
41
 
42
- def to_json(**_opts)
42
+ def to_json(*_opts)
43
43
  as_json.to_json
44
44
  end
45
45
 
@@ -6,6 +6,7 @@ module Grape
6
6
  class << self
7
7
  def call(object, _env)
8
8
  return object.to_json if object.respond_to?(:to_json)
9
+
9
10
  ::Grape::Json.dump(object)
10
11
  end
11
12
  end
@@ -8,13 +8,14 @@ module Grape
8
8
  return object if object.is_a?(String)
9
9
  return ::Grape::Json.dump(serialize(object)) if serializable?(object)
10
10
  return object.to_json if object.respond_to?(:to_json)
11
+
11
12
  ::Grape::Json.dump(object)
12
13
  end
13
14
 
14
15
  private
15
16
 
16
17
  def serializable?(object)
17
- object.respond_to?(:serializable_hash) || object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash } || object.is_a?(Hash)
18
+ object.respond_to?(:serializable_hash) || (object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash }) || object.is_a?(Hash)
18
19
  end
19
20
 
20
21
  def serialize(object)
@@ -6,6 +6,7 @@ module Grape
6
6
  class << self
7
7
  def call(object, _env)
8
8
  return object.to_xml if object.respond_to?(:to_xml)
9
+
9
10
  raise Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
10
11
  end
11
12
  end
@@ -44,6 +44,7 @@ en:
44
44
  "when specifying %{body_format} as content-type, you must pass valid
45
45
  %{body_format} in the request's 'body'
46
46
  "
47
+ empty_message_body: 'Empty message body supplied with %{body_format} content-type'
47
48
  invalid_accept_header:
48
49
  problem: 'Invalid accept header'
49
50
  resolution: '%{message}'
@@ -51,4 +52,3 @@ en:
51
52
  problem: 'Invalid version header'
52
53
  resolution: '%{message}'
53
54
  invalid_response: 'Invalid response'
54
-
@@ -10,9 +10,9 @@ module Grape
10
10
 
11
11
  attr_accessor :options, :app, :env
12
12
 
13
- def initialize(app, **options)
13
+ def initialize(app, *options)
14
14
  @app = app
15
- @options = options
15
+ @options = options.shift
16
16
  end
17
17
 
18
18
  def call(env)
@@ -23,7 +23,7 @@ module Grape
23
23
  self.env = env
24
24
 
25
25
  if options.key?(:type)
26
- auth_proc = options[:proc]
26
+ auth_proc = options[:proc]
27
27
  auth_proc_context = context
28
28
 
29
29
  strategy_info = Grape::Middleware::Auth::Strategies[options[:type]]
@@ -32,7 +32,13 @@ module Grape
32
32
 
33
33
  def http_digest(options = {}, &block)
34
34
  options[:realm] ||= 'API Authorization'
35
- options[:opaque] ||= 'secret'
35
+
36
+ if options[:realm].respond_to?(:values_at)
37
+ options[:realm][:opaque] ||= 'secret'
38
+ else
39
+ options[:opaque] ||= 'secret'
40
+ end
41
+
36
42
  auth :http_digest, options, &block
37
43
  end
38
44
  end
@@ -8,15 +8,16 @@ module Grape
8
8
  include Helpers
9
9
 
10
10
  attr_reader :app, :env, :options
11
+
11
12
  TEXT_HTML = 'text/html'
12
13
 
13
14
  include Grape::DSL::Headers
14
15
 
15
16
  # @param [Rack Application] app The standard argument for a Rack middleware.
16
17
  # @param [Hash] options A hash of options, simply stored for use by subclasses.
17
- def initialize(app, **options)
18
+ def initialize(app, *options)
18
19
  @app = app
19
- @options = default_options.merge(**options)
20
+ @options = options.any? ? default_options.merge(options.shift) : default_options
20
21
  @app_response = nil
21
22
  end
22
23
 
@@ -58,7 +59,8 @@ module Grape
58
59
 
59
60
  def response
60
61
  return @app_response if @app_response.is_a?(Rack::Response)
61
- Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
62
+
63
+ @app_response = Rack::Response.new(@app_response[2], @app_response[0], @app_response[1])
62
64
  end
63
65
 
64
66
  def content_type_for(format)
@@ -83,6 +85,7 @@ module Grape
83
85
 
84
86
  def merge_headers(response)
85
87
  return unless headers.is_a?(Hash)
88
+
86
89
  case response
87
90
  when Rack::Response then response.headers.merge!(headers)
88
91
  when Array then response[1].merge!(headers)
@@ -19,15 +19,15 @@ module Grape
19
19
  rescue_subclasses: true, # rescue subclasses of exceptions listed
20
20
  rescue_options: {
21
21
  backtrace: false, # true to display backtrace, true to let Grape handle Grape::Exceptions
22
- original_exception: false, # true to display exception
22
+ original_exception: false # true to display exception
23
23
  },
24
24
  rescue_handlers: {}, # rescue handler blocks
25
25
  base_only_rescue_handlers: {}, # rescue handler blocks rescuing only the base class
26
- all_rescue_handler: nil, # rescue handler block to rescue from all exceptions
26
+ all_rescue_handler: nil # rescue handler block to rescue from all exceptions
27
27
  }
28
28
  end
29
29
 
30
- def initialize(app, **options)
30
+ def initialize(app, *options)
31
31
  super
32
32
  self.class.send(:include, @options[:helpers]) if @options[:helpers]
33
33
  end
@@ -38,15 +38,15 @@ module Grape
38
38
  error_response(catch(:error) do
39
39
  return @app.call(@env)
40
40
  end)
41
- rescue Exception => error # rubocop:disable Lint/RescueException
41
+ rescue Exception => e # rubocop:disable Lint/RescueException
42
42
  handler =
43
- rescue_handler_for_base_only_class(error.class) ||
44
- rescue_handler_for_class_or_its_ancestor(error.class) ||
45
- rescue_handler_for_grape_exception(error.class) ||
46
- rescue_handler_for_any_class(error.class) ||
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
47
  raise
48
48
 
49
- run_rescue_handler(handler, error)
49
+ run_rescue_handler(handler, e)
50
50
  end
51
51
  end
52
52
 
@@ -65,15 +65,13 @@ module Grape
65
65
  message = error[:message] || options[:default_message]
66
66
  headers = { Grape::Http::Headers::CONTENT_TYPE => content_type }
67
67
  headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
68
- backtrace = error[:backtrace] || error[:original_exception] && error[:original_exception].backtrace || []
68
+ backtrace = error[:backtrace] || error[:original_exception]&.backtrace || []
69
69
  original_exception = error.is_a?(Exception) ? error : error[:original_exception] || nil
70
70
  rack_response(format_message(message, backtrace, original_exception), status, headers)
71
71
  end
72
72
 
73
73
  def rack_response(message, status = options[:default_status], headers = { Grape::Http::Headers::CONTENT_TYPE => content_type })
74
- if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
75
- message = ERB::Util.html_escape(message)
76
- end
74
+ message = ERB::Util.html_escape(message) if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
77
75
  Rack::Response.new([message], status, headers)
78
76
  end
79
77
 
@@ -22,6 +22,7 @@ module Grape
22
22
 
23
23
  def after
24
24
  return unless @app_response
25
+
25
26
  status, headers, bodies = *@app_response
26
27
 
27
28
  if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
@@ -36,9 +37,9 @@ module Grape
36
37
  def build_formatted_response(status, headers, bodies)
37
38
  headers = ensure_content_type(headers)
38
39
 
39
- if bodies.is_a?(Grape::ServeFile::FileResponse)
40
- Grape::ServeFile::SendfileResponse.new([], status, headers) do |resp|
41
- resp.body = bodies.file
40
+ if bodies.is_a?(Grape::ServeStream::StreamResponse)
41
+ Grape::ServeStream::SendfileResponse.new([], status, headers) do |resp|
42
+ resp.body = bodies.stream
42
43
  end
43
44
  else
44
45
  # Allow content-type to be explicitly overwritten
@@ -79,7 +80,7 @@ module Grape
79
80
  (request.post? || request.put? || request.patch? || request.delete?) &&
80
81
  (!request.form_data? || !request.media_type) &&
81
82
  !request.parseable_data? &&
82
- (request.content_length.to_i > 0 || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == CHUNKED)
83
+ (request.content_length.to_i.positive? || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == CHUNKED)
83
84
 
84
85
  return unless (input = env[Grape::Env::RACK_INPUT])
85
86
 
@@ -96,9 +97,7 @@ module Grape
96
97
  def read_rack_input(body)
97
98
  fmt = request.media_type ? mime_types[request.media_type] : options[:default_format]
98
99
 
99
- unless content_type_for(fmt)
100
- throw :error, status: 415, message: "The provided content-type '#{request.media_type}' is not supported."
101
- end
100
+ throw :error, status: 415, message: "The provided content-type '#{request.media_type}' is not supported." unless content_type_for(fmt)
102
101
  parser = Grape::Parser.parser_for fmt, **options
103
102
  if parser
104
103
  begin
@@ -145,6 +144,7 @@ module Grape
145
144
  fmt = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[Grape::Http::Headers::FORMAT]
146
145
  # avoid symbol memory leak on an unknown format
147
146
  return fmt.to_sym if content_type_for(fmt)
147
+
148
148
  fmt
149
149
  end
150
150
 
@@ -30,6 +30,10 @@ module Grape
30
30
  def inspect
31
31
  klass.to_s
32
32
  end
33
+
34
+ def use_in(builder)
35
+ builder.use(@klass, *@args, &@block)
36
+ end
33
37
  end
34
38
 
35
39
  include Enumerable
@@ -41,8 +45,8 @@ module Grape
41
45
  @others = []
42
46
  end
43
47
 
44
- def each
45
- @middlewares.each { |x| yield x }
48
+ def each(&block)
49
+ @middlewares.each(&block)
46
50
  end
47
51
 
48
52
  def size
@@ -62,6 +66,7 @@ module Grape
62
66
  middleware = self.class::Middleware.new(*args, &block)
63
67
  middlewares.insert(index, middleware)
64
68
  end
69
+ ruby2_keywords :insert if respond_to?(:ruby2_keywords, true)
65
70
 
66
71
  alias insert_before insert
67
72
 
@@ -69,11 +74,13 @@ module Grape
69
74
  index = assert_index(index, :after)
70
75
  insert(index + 1, *args, &block)
71
76
  end
77
+ ruby2_keywords :insert_after if respond_to?(:ruby2_keywords, true)
72
78
 
73
79
  def use(*args, &block)
74
80
  middleware = self.class::Middleware.new(*args, &block)
75
81
  middlewares.push(middleware)
76
82
  end
83
+ ruby2_keywords :use if respond_to?(:ruby2_keywords, true)
77
84
 
78
85
  def merge_with(middleware_specs)
79
86
  middleware_specs.each do |operation, *args|
@@ -90,7 +97,7 @@ module Grape
90
97
  def build(builder = Rack::Builder.new)
91
98
  others.shift(others.size).each(&method(:merge_with))
92
99
  middlewares.each do |m|
93
- m.block ? builder.use(m.klass, *m.args, &m.block) : builder.use(m.klass, *m.args)
100
+ m.use_in(builder)
94
101
  end
95
102
  builder
96
103
  end
@@ -22,11 +22,9 @@ module Grape
22
22
  def before
23
23
  potential_version = (env[Grape::Http::Headers::HTTP_ACCEPT_VERSION] || '').strip
24
24
 
25
- if strict?
25
+ if strict? && potential_version.empty?
26
26
  # If no Accept-Version header:
27
- if potential_version.empty?
28
- throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
29
- end
27
+ throw :error, status: 406, headers: error_headers, message: 'Accept-Version header must be set.'
30
28
  end
31
29
 
32
30
  return if potential_version.empty?
@@ -51,7 +49,7 @@ module Grape
51
49
  # of routes (see Grape::Router) for more information). To prevent
52
50
  # this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
53
51
  def cascade?
54
- if options[:version_options] && options[:version_options].key?(:cascade)
52
+ if options[:version_options]&.key?(:cascade)
55
53
  options[:version_options][:cascade]
56
54
  else
57
55
  true
@@ -26,10 +26,10 @@ module Grape
26
26
  # route.
27
27
  class Header < Base
28
28
  VENDOR_VERSION_HEADER_REGEX =
29
- /\Avnd\.([a-z0-9.\-_!#\$&\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/.freeze
29
+ /\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/.freeze
30
30
 
31
- HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_!#\$&\^]+/.freeze
32
- HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_!#\$&\^]+?)(?:-([a-z0-9*.]+))+/.freeze
31
+ HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_!#{Regexp.last_match(0)}\^]+/.freeze
32
+ HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_!#{Regexp.last_match(0)}\^]+?)(?:-([a-z0-9*.]+))+/.freeze
33
33
 
34
34
  def before
35
35
  strict_header_checks if strict?
@@ -52,12 +52,14 @@ module Grape
52
52
 
53
53
  def strict_accept_header_presence_check
54
54
  return unless header.qvalues.empty?
55
+
55
56
  fail_with_invalid_accept_header!('Accept header must be set.')
56
57
  end
57
58
 
58
59
  def strict_version_vendor_accept_header_presence_check
59
60
  return unless versions.present?
60
61
  return if an_accept_header_with_version_and_vendor_is_present?
62
+
61
63
  fail_with_invalid_accept_header!('API vendor or version not found.')
62
64
  end
63
65
 
@@ -160,7 +162,7 @@ module Grape
160
162
  # information). To prevent # this behavior, and not add the `X-Cascade`
161
163
  # header, one can set the `:cascade` option to `false`.
162
164
  def cascade?
163
- if version_options && version_options.key?(:cascade)
165
+ if version_options&.key?(:cascade)
164
166
  version_options[:cascade]
165
167
  else
166
168
  true
@@ -32,6 +32,7 @@ module Grape
32
32
  def before
33
33
  potential_version = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[paramkey]
34
34
  return if potential_version.nil?
35
+
35
36
  throw :error, status: 404, message: '404 API Version Not Found', headers: { Grape::Http::Headers::X_CASCADE => 'pass' } if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
36
37
  env[Grape::Env::API_VERSION] = potential_version
37
38
  env[Grape::Env::RACK_REQUEST_QUERY_HASH].delete(paramkey) if env.key? Grape::Env::RACK_REQUEST_QUERY_HASH
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'English'
3
4
  module Rack
4
5
  module Accept
5
6
  module Header
6
- ALLOWED_CHARACTERS = %r{^([a-z*]+)\/([a-z0-9*\&\^\-_#\$!.+]+)(?:;([a-z0-9=;]+))?$}.freeze
7
+ ALLOWED_CHARACTERS = %r{^([a-z*]+)/([a-z0-9*&\^\-_#{$ERROR_INFO}.+]+)(?:;([a-z0-9=;]+))?$}.freeze
7
8
  class << self
8
9
  # Corrected version of https://github.com/mjackson/rack-accept/blob/master/lib/rack/accept/header.rb#L40-L44
9
10
  def parse_media_type(media_type)
@@ -37,6 +37,7 @@ module Grape
37
37
  pieces = path.split('/')
38
38
  potential_version = pieces[1]
39
39
  return unless potential_version&.match?(options[:pattern])
40
+
40
41
  throw :error, status: 404, message: '404 API Version Not Found' if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
41
42
  env[Grape::Env::API_VERSION] = potential_version
42
43
  end
@@ -45,6 +46,7 @@ module Grape
45
46
 
46
47
  def mounted_path?(path)
47
48
  return false unless mount_path && path.start_with?(mount_path)
49
+
48
50
  rest = path.slice(mount_path.length..-1)
49
51
  rest.start_with?('/') || rest.empty?
50
52
  end
@@ -8,7 +8,7 @@ module Grape
8
8
  ::Grape::Json.load(object)
9
9
  rescue ::Grape::Json::ParseError
10
10
  # handle JSON parsing errors via the rescue handlers or provide error message
11
- raise Grape::Exceptions::InvalidMessageBody, 'application/json'
11
+ raise Grape::Exceptions::InvalidMessageBody.new('application/json')
12
12
  end
13
13
  end
14
14
  end
@@ -8,7 +8,7 @@ module Grape
8
8
  ::Grape::Xml.parse(object)
9
9
  rescue ::Grape::Xml::ParseError
10
10
  # handle XML parsing errors via the rescue handlers or provide error message
11
- raise Grape::Exceptions::InvalidMessageBody, 'application/xml'
11
+ raise Grape::Exceptions::InvalidMessageBody.new('application/xml')
12
12
  end
13
13
  end
14
14
  end
data/lib/grape/path.rb CHANGED
@@ -91,6 +91,7 @@ module Grape
91
91
 
92
92
  def split_setting(key)
93
93
  return if settings[key].nil?
94
+
94
95
  settings[key].to_s.split('/')
95
96
  end
96
97
  end