grape 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +9 -9
  2. data/.gitignore +7 -0
  3. data/.rubocop.yml +5 -0
  4. data/.travis.yml +3 -3
  5. data/CHANGELOG.md +42 -3
  6. data/CONTRIBUTING.md +118 -0
  7. data/Gemfile +4 -4
  8. data/README.md +312 -52
  9. data/Rakefile +6 -1
  10. data/UPGRADING.md +124 -0
  11. data/lib/grape.rb +2 -0
  12. data/lib/grape/api.rb +95 -44
  13. data/lib/grape/cookies.rb +0 -2
  14. data/lib/grape/endpoint.rb +63 -39
  15. data/lib/grape/error_formatter/base.rb +0 -3
  16. data/lib/grape/error_formatter/json.rb +0 -2
  17. data/lib/grape/error_formatter/txt.rb +0 -2
  18. data/lib/grape/error_formatter/xml.rb +0 -2
  19. data/lib/grape/exceptions/base.rb +0 -2
  20. data/lib/grape/exceptions/incompatible_option_values.rb +0 -3
  21. data/lib/grape/exceptions/invalid_formatter.rb +0 -3
  22. data/lib/grape/exceptions/invalid_versioner_option.rb +0 -4
  23. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +0 -2
  24. data/lib/grape/exceptions/missing_mime_type.rb +0 -4
  25. data/lib/grape/exceptions/missing_option.rb +0 -3
  26. data/lib/grape/exceptions/missing_vendor_option.rb +0 -3
  27. data/lib/grape/exceptions/unknown_options.rb +0 -4
  28. data/lib/grape/exceptions/unknown_validator.rb +0 -2
  29. data/lib/grape/exceptions/validation_errors.rb +6 -5
  30. data/lib/grape/formatter/base.rb +0 -3
  31. data/lib/grape/formatter/json.rb +0 -2
  32. data/lib/grape/formatter/serializable_hash.rb +15 -16
  33. data/lib/grape/formatter/txt.rb +0 -2
  34. data/lib/grape/formatter/xml.rb +0 -2
  35. data/lib/grape/http/request.rb +2 -4
  36. data/lib/grape/locale/en.yml +1 -1
  37. data/lib/grape/middleware/auth/oauth2.rb +15 -6
  38. data/lib/grape/middleware/base.rb +7 -7
  39. data/lib/grape/middleware/error.rb +11 -6
  40. data/lib/grape/middleware/formatter.rb +80 -78
  41. data/lib/grape/middleware/globals.rb +13 -0
  42. data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
  43. data/lib/grape/middleware/versioner/header.rb +5 -3
  44. data/lib/grape/middleware/versioner/param.rb +2 -4
  45. data/lib/grape/middleware/versioner/path.rb +3 -4
  46. data/lib/grape/namespace.rb +0 -1
  47. data/lib/grape/parser/base.rb +0 -3
  48. data/lib/grape/parser/json.rb +0 -2
  49. data/lib/grape/parser/xml.rb +0 -2
  50. data/lib/grape/path.rb +1 -3
  51. data/lib/grape/route.rb +0 -3
  52. data/lib/grape/util/hash_stack.rb +1 -1
  53. data/lib/grape/validations.rb +72 -22
  54. data/lib/grape/validations/coerce.rb +5 -4
  55. data/lib/grape/validations/default.rb +5 -3
  56. data/lib/grape/validations/presence.rb +1 -1
  57. data/lib/grape/validations/regexp.rb +0 -2
  58. data/lib/grape/validations/values.rb +2 -1
  59. data/lib/grape/version.rb +1 -1
  60. data/spec/grape/api_spec.rb +385 -96
  61. data/spec/grape/endpoint_spec.rb +162 -15
  62. data/spec/grape/entity_spec.rb +25 -0
  63. data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
  64. data/spec/grape/middleware/auth/oauth2_spec.rb +60 -15
  65. data/spec/grape/middleware/base_spec.rb +3 -8
  66. data/spec/grape/middleware/error_spec.rb +2 -2
  67. data/spec/grape/middleware/exception_spec.rb +4 -4
  68. data/spec/grape/middleware/formatter_spec.rb +7 -4
  69. data/spec/grape/middleware/versioner/param_spec.rb +8 -7
  70. data/spec/grape/path_spec.rb +24 -14
  71. data/spec/grape/util/hash_stack_spec.rb +8 -8
  72. data/spec/grape/validations/coerce_spec.rb +75 -33
  73. data/spec/grape/validations/default_spec.rb +57 -0
  74. data/spec/grape/validations/presence_spec.rb +13 -11
  75. data/spec/grape/validations/values_spec.rb +76 -2
  76. data/spec/grape/validations_spec.rb +443 -20
  77. data/spec/spec_helper.rb +2 -2
  78. data/spec/support/content_type_helpers.rb +11 -0
  79. metadata +9 -38
data/Rakefile CHANGED
@@ -15,7 +15,12 @@ RSpec::Core::RakeTask.new(:rcov) do |spec|
15
15
  end
16
16
 
17
17
  task :spec
18
- task :default => :spec
18
+
19
+ require 'rainbow/ext/string' unless String.respond_to?(:color)
20
+ require 'rubocop/rake_task'
21
+ Rubocop::RakeTask.new(:rubocop)
22
+
23
+ task default: [:rubocop, :spec]
19
24
 
20
25
  begin
21
26
  require 'yard'
@@ -0,0 +1,124 @@
1
+ Upgrading Grape
2
+ ===============
3
+
4
+ ### Upgrading to 0.7.0
5
+
6
+ #### Changes in Exception Handling
7
+
8
+ Assume you have the following exception classes defined.
9
+
10
+ ```ruby
11
+ class ParentError < StandardError; end
12
+ class ChildError < ParentError; end
13
+ ```
14
+
15
+ In Grape <= 0.6.1, the `rescue_from` keyword only handled the exact exception being raised. The following code would rescue `ParentError`, but not `ChildError`.
16
+
17
+ ```ruby
18
+ rescue_from ParentError do |e|
19
+ # only rescue ParentError
20
+ end
21
+ ```
22
+
23
+ This made it impossible to rescue an exception hieararchy, which is a more sensible default. In Grape 0.7.0 or newer, both `ParentError` and `ChildError` are rescued.
24
+
25
+ ```ruby
26
+ rescue_from ParentError do |e|
27
+ # rescue both ParentError and ChildError
28
+ end
29
+ ```
30
+
31
+ To only rescue the base exception class, set `rescue_subclasses: false`.
32
+
33
+ ```ruby
34
+ rescue_from ParentError, rescue_subclasses: false do |e|
35
+ # only rescue ParentError
36
+ end
37
+ ```
38
+
39
+ See [#544](https://github.com/intridea/grape/pull/544) for more information.
40
+
41
+
42
+ #### Changes in the Default HTTP Status Code
43
+
44
+ In Grape <= 0.6.1, the default status code returned from `error!` was 403.
45
+
46
+ ```ruby
47
+ error! "You may not reticulate this spline!" # yields HTTP error 403
48
+ ```
49
+
50
+ This was a bad default value, since 403 means "Forbidden". Change any call to `error!` that does not specify a status code to specify one. The new default value is a more sensible default of 500, which is "Internal Server Error".
51
+
52
+ ```ruby
53
+ error! "You may not reticulate this spline!", 403 # yields HTTP error 403
54
+ ```
55
+
56
+ You may also use `default_error_status` to change the global default.
57
+
58
+ ```ruby
59
+ default_error_status 400
60
+ ```
61
+
62
+ See [#525](https://github.com/intridea/Grape/pull/525) for more information.
63
+
64
+
65
+ #### Changes in Parameter Declaration and Validation
66
+
67
+ In Grape <= 0.6.1, `group`, `optional` and `requires` keywords with a block accepted either an `Array` or a `Hash`.
68
+
69
+ ```ruby
70
+ params do
71
+ requires :id, type: Integer
72
+ group :name do
73
+ requires :first_name
74
+ requires :last_name
75
+ end
76
+ end
77
+ ```
78
+
79
+ This caused the ambiguity and unexpected errors described in [#543](https://github.com/intridea/Grape/issues/543).
80
+
81
+ In Grape 0.6.2, the `group`, `optional` and `requires` keywords take an additional `type` attribute which defaults to `Array`. This means that without a `type` attribute, these nested parameters will no longer accept a single hash, only an array (of hashes).
82
+
83
+ Whereas in 0.6.1 the API above accepted the following json, it no longer does in 0.6.2.
84
+
85
+ ```json
86
+ {
87
+ "id": 1,
88
+ "name": {
89
+ "first_name": "John",
90
+ "last_name" : "Doe"
91
+ }
92
+ }
93
+ ```
94
+
95
+ The `params` block should now read as follows.
96
+
97
+ ```ruby
98
+ params do
99
+ requires :id, type: Integer
100
+ requires :name, type: Hash do
101
+ requires :first_name
102
+ requires :last_name
103
+ end
104
+ end
105
+ ```
106
+
107
+ See [#545](https://github.com/intridea/Grape/pull/545) for more information.
108
+
109
+
110
+ ### Upgrading to 0.6.0
111
+
112
+ In Grape <= 0.5.0, only the first validation error was raised and processing aborted. Validation errors are now collected and a single `Grape::Exceptions::ValidationErrors` exception is raised. You can access the collection of validation errors as `.errors`.
113
+
114
+ ```ruby
115
+ rescue_from Grape::Exceptions::Validations do |e|
116
+ Rack::Response.new({
117
+ status: 422,
118
+ message: e.message,
119
+ errors: e.errors
120
+ }.to_json, 422)
121
+ end
122
+ ```
123
+
124
+ For more information see [#462](https://github.com/intridea/grape/issues/462).
@@ -6,6 +6,7 @@ require 'rack/accept'
6
6
  require 'rack/auth/basic'
7
7
  require 'rack/auth/digest/md5'
8
8
  require 'hashie'
9
+ require 'set'
9
10
  require 'active_support/core_ext/hash/indifferent_access'
10
11
  require 'active_support/ordered_hash'
11
12
  require 'active_support/core_ext/object/conversions'
@@ -16,6 +17,7 @@ require 'multi_json'
16
17
  require 'multi_xml'
17
18
  require 'virtus'
18
19
  require 'i18n'
20
+ require 'thread'
19
21
 
20
22
  I18n.load_path << File.expand_path('../grape/locale/en.yml', __FILE__)
21
23
 
@@ -9,6 +9,8 @@ module Grape
9
9
  attr_reader :endpoints, :instance, :routes, :route_set, :settings, :versions
10
10
  attr_writer :logger
11
11
 
12
+ LOCK = Mutex.new
13
+
12
14
  def logger(logger = nil)
13
15
  if logger
14
16
  @logger = logger
@@ -26,7 +28,7 @@ module Grape
26
28
  end
27
29
 
28
30
  def compile
29
- @instance = new
31
+ @instance ||= new
30
32
  end
31
33
 
32
34
  def change!
@@ -34,7 +36,7 @@ module Grape
34
36
  end
35
37
 
36
38
  def call(env)
37
- compile unless instance
39
+ LOCK.synchronize { compile } unless instance
38
40
  call!(env)
39
41
  end
40
42
 
@@ -199,6 +201,7 @@ module Grape
199
201
  # @param [Block] block Execution block to handle the given exception.
200
202
  # @param [Hash] options Options for the rescue usage.
201
203
  # @option options [Boolean] :backtrace Include a backtrace in the rescue response.
204
+ # @option options [Boolean] :rescue_subclasses Also rescue subclasses of exception classes
202
205
  # @param [Proc] handler Execution proc to handle the given exception as an
203
206
  # alternative to passing a block
204
207
  def rescue_from(*args, &block)
@@ -211,19 +214,12 @@ module Grape
211
214
  options = args.last.is_a?(Hash) ? args.pop : {}
212
215
  handler ||= proc { options[:with] } if options.has_key?(:with)
213
216
 
214
- if handler
215
- args.each do |arg|
216
- imbue(:rescue_handlers, { arg => handler })
217
- end
218
- end
217
+ handler_type = !!options[:rescue_subclasses] ? :rescue_handlers : :base_only_rescue_handlers
218
+ imbue handler_type, Hash[args.map { |arg| [arg, handler] }]
219
219
 
220
220
  imbue(:rescue_options, options)
221
221
 
222
- if args.include?(:all)
223
- set(:rescue_all, true)
224
- else
225
- imbue(:rescued_errors, args)
226
- end
222
+ set(:rescue_all, true) if args.include?(:all)
227
223
  end
228
224
 
229
225
  # Allows you to specify a default representation entity for a
@@ -274,11 +270,16 @@ module Grape
274
270
  if block_given? || new_mod
275
271
  mod = settings.peek[:helpers] || Module.new
276
272
  if new_mod
273
+ inject_api_helpers_to_mod(new_mod) if new_mod.is_a?(Helpers)
277
274
  mod.class_eval do
278
275
  include new_mod
279
276
  end
280
277
  end
281
- mod.class_eval(&block) if block_given?
278
+ if block_given?
279
+ inject_api_helpers_to_mod(mod) do
280
+ mod.class_eval(&block)
281
+ end
282
+ end
282
283
  set(:helpers, mod)
283
284
  else
284
285
  mod = Module.new
@@ -324,11 +325,12 @@ module Grape
324
325
  app_settings.set :mount_path, mount_path
325
326
  app.inherit_settings(app_settings)
326
327
  end
327
- endpoints << Grape::Endpoint.new(settings.clone, {
328
+ endpoints << Grape::Endpoint.new(
329
+ settings.clone,
328
330
  method: :any,
329
331
  path: path,
330
332
  app: app
331
- })
333
+ )
332
334
  end
333
335
  end
334
336
 
@@ -360,6 +362,10 @@ module Grape
360
362
  imbue(:befores, [block])
361
363
  end
362
364
 
365
+ def before_validation(&block)
366
+ imbue(:before_validations, [block])
367
+ end
368
+
363
369
  def after_validation(&block)
364
370
  imbue(:after_validations, [block])
365
371
  end
@@ -510,14 +516,20 @@ module Grape
510
516
  e.options[:app].inherit_settings(other_stack) if e.options[:app].respond_to?(:inherit_settings, true)
511
517
  end
512
518
  end
519
+
520
+ def inject_api_helpers_to_mod(mod, &block)
521
+ mod.extend(Helpers)
522
+ yield if block_given?
523
+ mod.api_changed(self)
524
+ end
513
525
  end
514
526
 
515
527
  def initialize
516
528
  @route_set = Rack::Mount::RouteSet.new
529
+ add_head_not_allowed_methods_and_options_methods
517
530
  self.class.endpoints.each do |endpoint|
518
531
  endpoint.mount_in(@route_set)
519
532
  end
520
- add_head_not_allowed_methods
521
533
  @route_set.freeze
522
534
  end
523
535
 
@@ -549,39 +561,78 @@ module Grape
549
561
  # with a list of HTTP methods that can be called. Also add a route that
550
562
  # will return an HTTP 405 response for any HTTP method that the resource
551
563
  # cannot handle.
552
- def add_head_not_allowed_methods
553
- allowed_methods = Hash.new { |h, k| h[k] = [] }
554
- resources = self.class.endpoints.map do |endpoint|
555
- if endpoint.options[:app] && endpoint.options[:app].respond_to?(:endpoints)
556
- endpoint.options[:app].endpoints.map(&:routes)
557
- else
558
- endpoint.routes
564
+ def add_head_not_allowed_methods_and_options_methods
565
+ methods_per_path = {}
566
+ self.class.endpoints.each do |endpoint|
567
+ routes = endpoint.routes
568
+ routes.each do |route|
569
+ methods_per_path[route.route_path] ||= []
570
+ methods_per_path[route.route_path] << route.route_method
559
571
  end
560
572
  end
561
- resources.flatten.each do |route|
562
- allowed_methods[route.route_compiled] << route.route_method
563
- end
564
- allowed_methods.each do |path_info, methods|
565
- if methods.include?('GET') && !methods.include?("HEAD") && !self.class.settings[:do_not_route_head]
566
- methods = methods | ['HEAD']
567
- end
568
- allow_header = (["OPTIONS"] | methods).join(", ")
569
- unless methods.include?("OPTIONS") || self.class.settings[:do_not_route_options]
570
- @route_set.add_route(proc { [204, { 'Allow' => allow_header }, []] }, {
571
- path_info: path_info,
572
- request_method: "OPTIONS"
573
- })
574
- end
575
- not_allowed_methods = %w(GET PUT POST DELETE PATCH HEAD) - methods
576
- not_allowed_methods << "OPTIONS" if self.class.settings[:do_not_route_options]
577
- not_allowed_methods.each do |bad_method|
578
- @route_set.add_route(proc { [405, { 'Allow' => allow_header, 'Content-Type' => 'text/plain' }, []] }, {
579
- path_info: path_info,
580
- request_method: bad_method
581
- })
573
+
574
+ # The paths we collected are prepared (cf. Path#prepare), so they
575
+ # contain already versioning information when using path versioning.
576
+ # Disable versioning so adding a route won't prepend versioning
577
+ # informations again.
578
+ without_versioning do
579
+ methods_per_path.each do |path, methods|
580
+ allowed_methods = methods.dup
581
+ unless self.class.settings[:do_not_route_head]
582
+ if allowed_methods.include?('GET')
583
+ allowed_methods = allowed_methods | ['HEAD']
584
+ end
585
+ end
586
+
587
+ allow_header = (['OPTIONS'] | allowed_methods).join(', ')
588
+ unless self.class.settings[:do_not_route_options]
589
+ unless allowed_methods.include?('OPTIONS')
590
+ self.class.options(path, {}) do
591
+ header 'Allow', allow_header
592
+ status 204
593
+ ''
594
+ end
595
+ end
596
+ end
597
+
598
+ not_allowed_methods = %w(GET PUT POST DELETE PATCH HEAD) - allowed_methods
599
+ not_allowed_methods << 'OPTIONS' if self.class.settings[:do_not_route_options]
600
+ self.class.route(not_allowed_methods, path) do
601
+ header 'Allow', allow_header
602
+ status 405
603
+ ''
604
+ end
582
605
  end
583
606
  end
584
607
  end
585
608
 
609
+ def without_versioning(&block)
610
+ self.class.settings.push(version: nil, version_options: nil)
611
+ yield
612
+ self.class.settings.pop
613
+ end
614
+
615
+ # This module extends user defined helpers
616
+ # to provide some API-specific functionality
617
+ module Helpers
618
+ attr_accessor :api
619
+ def params(name, &block)
620
+ @named_params ||= {}
621
+ @named_params.merge! name => block
622
+ end
623
+
624
+ def api_changed(new_api)
625
+ @api = new_api
626
+ process_named_params
627
+ end
628
+
629
+ protected
630
+
631
+ def process_named_params
632
+ if @named_params && @named_params.any?
633
+ api.imbue(:named_params, @named_params)
634
+ end
635
+ end
636
+ end
586
637
  end
587
638
  end
@@ -1,6 +1,5 @@
1
1
  module Grape
2
2
  class Cookies
3
-
4
3
  def initialize
5
4
  @cookies = {}
6
5
  @send_cookies = {}
@@ -36,6 +35,5 @@ module Grape
36
35
  options = opts.merge(value: 'deleted', expires: Time.at(0))
37
36
  self.[]=(name, options)
38
37
  end
39
-
40
38
  end
41
39
  end
@@ -5,7 +5,7 @@ module Grape
5
5
  # from inside a `get`, `post`, etc.
6
6
  class Endpoint
7
7
  attr_accessor :block, :source, :options, :settings
8
- attr_reader :env, :request
8
+ attr_reader :env, :request, :headers, :params
9
9
 
10
10
  class << self
11
11
  # @api private
@@ -80,7 +80,7 @@ module Grape
80
80
  route_set.add_route(self, {
81
81
  path_info: route.route_compiled,
82
82
  request_method: method,
83
- }, { route_info: route })
83
+ }, route_info: route)
84
84
  end
85
85
  end
86
86
  end
@@ -146,6 +146,8 @@ module Grape
146
146
  end
147
147
 
148
148
  def call!(env)
149
+ extend helpers
150
+
149
151
  env['api.endpoint'] = self
150
152
  if options[:app]
151
153
  options[:app].call(env)
@@ -156,20 +158,22 @@ module Grape
156
158
  end
157
159
  end
158
160
 
159
- # The parameters passed into the request as
160
- # well as parsed from URL segments.
161
- def params
162
- @params ||= @request.params
163
- end
164
-
165
161
  # A filtering method that will return a hash
166
162
  # consisting only of keys that have been declared by a
167
- # `params` statement.
163
+ # `params` statement against the current/target endpoint or parent
164
+ # namespaces
168
165
  #
169
166
  # @param params [Hash] The initial hash to filter. Usually this will just be `params`
170
- # @param options [Hash] Can pass `:include_missing` and `:stringify` options.
171
- def declared(params, options = {}, declared_params = settings[:declared_params])
167
+ # @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces`
168
+ # options. `:include_parent_namespaces` defaults to true, hence must be set to false if
169
+ # you want only to return params declared against the current/target endpoint
170
+ def declared(params, options = {}, declared_params = nil)
172
171
  options[:include_missing] = true unless options.key?(:include_missing)
172
+ options[:include_parent_namespaces] = true unless options.key?(:include_parent_namespaces)
173
+ if declared_params.nil?
174
+ declared_params = !options[:include_parent_namespaces] ? settings[:declared_params] :
175
+ settings.gather(:declared_params)
176
+ end
173
177
 
174
178
  unless declared_params
175
179
  raise ArgumentError, "Tried to filter for declared parameters but none exist."
@@ -208,9 +212,10 @@ module Grape
208
212
  # end user with the specified message.
209
213
  #
210
214
  # @param message [String] The message to display.
211
- # @param status [Integer] the HTTP Status Code. Defaults to 403.
212
- def error!(message, status = 403)
213
- throw :error, message: message, status: status
215
+ # @param status [Integer] the HTTP Status Code. Defaults to default_error_status, 500 if not set.
216
+ def error!(message, status = nil, headers = nil)
217
+ status = settings[:default_error_status] unless status
218
+ throw :error, message: message, status: status, headers: headers
214
219
  end
215
220
 
216
221
  # Redirect to a new url.
@@ -260,11 +265,6 @@ module Grape
260
265
  end
261
266
  end
262
267
 
263
- # Retrieves all available request headers.
264
- def headers
265
- @headers ||= @request.headers
266
- end
267
-
268
268
  # Set response content-type
269
269
  def content_type(val)
270
270
  header('Content-Type', val)
@@ -324,14 +324,16 @@ module Grape
324
324
  end
325
325
  entity_class = options.delete(:with)
326
326
 
327
- # auto-detect the entity from the first object in the collection
328
- object_instance = object.respond_to?(:first) ? object.first : object
327
+ if entity_class.nil?
328
+ # entity class not explicitely defined, auto-detect from first object in the collection
329
+ object_instance = object.respond_to?(:first) ? object.first : object
329
330
 
330
- object_instance.class.ancestors.each do |potential|
331
- entity_class ||= (settings[:representations] || {})[potential]
332
- end
331
+ object_instance.class.ancestors.each do |potential|
332
+ entity_class ||= (settings[:representations] || {})[potential]
333
+ end
333
334
 
334
- entity_class ||= object_instance.class.const_get(:Entity) if object_instance.class.const_defined?(:Entity)
335
+ entity_class ||= object_instance.class.const_get(:Entity) if object_instance.class.const_defined?(:Entity) && object_instance.class.const_get(:Entity).respond_to?(:represent)
336
+ end
335
337
 
336
338
  root = options.delete(:root)
337
339
 
@@ -360,8 +362,6 @@ module Grape
360
362
  env["rack.routing_args"][:route_info]
361
363
  end
362
364
 
363
- protected
364
-
365
365
  # Return the collection of endpoints within this endpoint.
366
366
  # This is the case when an Grape::API mounts another Grape::API.
367
367
  def endpoints
@@ -372,16 +372,22 @@ module Grape
372
372
  end
373
373
  end
374
374
 
375
+ protected
376
+
375
377
  def run(env)
376
378
  @env = env
377
379
  @header = {}
378
- @request = Grape::Request.new(@env)
379
380
 
380
- extend helpers
381
+ @request = Grape::Request.new(env)
382
+ @params = @request.params
383
+ @headers = @request.headers
384
+
381
385
  cookies.read(@request)
382
386
 
383
387
  run_filters befores
384
388
 
389
+ run_filters before_validations
390
+
385
391
  # Retieve validations from this namespace and all parent namespaces.
386
392
  validation_errors = []
387
393
  settings.gather(:validations).each do |validator|
@@ -411,13 +417,14 @@ module Grape
411
417
  b.use Rack::Head
412
418
  b.use Grape::Middleware::Error,
413
419
  format: settings[:format],
414
- default_status: settings[:default_error_status] || 403,
420
+ content_types: settings[:content_types],
421
+ default_status: settings[:default_error_status] || 500,
415
422
  rescue_all: settings[:rescue_all],
416
- rescued_errors: aggregate_setting(:rescued_errors),
417
423
  default_error_formatter: settings[:default_error_formatter],
418
424
  error_formatters: settings[:error_formatters],
419
425
  rescue_options: settings[:rescue_options],
420
- rescue_handlers: merged_setting(:rescue_handlers)
426
+ rescue_handlers: merged_setting(:rescue_handlers),
427
+ base_only_rescue_handlers: merged_setting(:base_only_rescue_handlers)
421
428
 
422
429
  aggregate_setting(:middleware).each do |m|
423
430
  m = m.dup
@@ -429,15 +436,28 @@ module Grape
429
436
  end
430
437
  end
431
438
 
432
- b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
433
- b.use Rack::Auth::Digest::MD5, settings[:auth][:realm], settings[:auth][:opaque], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_digest
439
+ if settings[:auth]
440
+ auth_proc = settings[:auth][:proc]
441
+ auth_proc_context = self
442
+ auth_middleware = {
443
+ http_basic: { class: Rack::Auth::Basic, args: [settings[:auth][:realm]] },
444
+ http_digest: { class: Rack::Auth::Digest::MD5, args: [settings[:auth][:realm], settings[:auth][:opaque]] }
445
+ }[settings[:auth][:type]]
446
+
447
+ # evaluate auth proc in context of endpoint
448
+ if auth_middleware
449
+ b.use auth_middleware[:class], *auth_middleware[:args] do |*args|
450
+ auth_proc_context.instance_exec(*args, &auth_proc)
451
+ end
452
+ end
453
+ end
434
454
 
435
455
  if settings[:version]
436
- b.use Grape::Middleware::Versioner.using(settings[:version_options][:using]), {
437
- versions: settings[:version] ? settings[:version].flatten : nil,
438
- version_options: settings[:version_options],
439
- prefix: settings[:root_prefix]
440
- }
456
+ b.use Grape::Middleware::Versioner.using(settings[:version_options][:using]),
457
+ versions: settings[:version] ? settings[:version].flatten : nil,
458
+ version_options: settings[:version_options],
459
+ prefix: settings[:root_prefix]
460
+
441
461
  end
442
462
 
443
463
  b.use Grape::Middleware::Formatter,
@@ -480,6 +500,10 @@ module Grape
480
500
  aggregate_setting(:befores)
481
501
  end
482
502
 
503
+ def before_validations
504
+ aggregate_setting(:before_validations)
505
+ end
506
+
483
507
  def after_validations
484
508
  aggregate_setting(:after_validations)
485
509
  end