grape 0.16.2 → 0.17.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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +4 -0
  3. data/CHANGELOG.md +54 -27
  4. data/Dangerfile +80 -0
  5. data/Gemfile +23 -0
  6. data/Gemfile.lock +61 -27
  7. data/README.md +135 -7
  8. data/Rakefile +34 -30
  9. data/UPGRADING.md +21 -0
  10. data/gemfiles/rack_1.5.2.gemfile +21 -0
  11. data/gemfiles/rails_3.gemfile +22 -1
  12. data/gemfiles/rails_4.gemfile +21 -0
  13. data/gemfiles/rails_5.gemfile +34 -0
  14. data/grape.gemspec +0 -14
  15. data/lib/grape.rb +2 -0
  16. data/lib/grape/api.rb +9 -2
  17. data/lib/grape/dsl/headers.rb +1 -1
  18. data/lib/grape/dsl/inside_route.rb +15 -17
  19. data/lib/grape/dsl/middleware.rb +15 -1
  20. data/lib/grape/dsl/parameters.rb +16 -14
  21. data/lib/grape/dsl/request_response.rb +24 -20
  22. data/lib/grape/dsl/routing.rb +11 -10
  23. data/lib/grape/dsl/settings.rb +16 -0
  24. data/lib/grape/endpoint.rb +77 -60
  25. data/lib/grape/exceptions/validation.rb +5 -2
  26. data/lib/grape/exceptions/validation_array_errors.rb +11 -0
  27. data/lib/grape/formatter/xml.rb +1 -1
  28. data/lib/grape/middleware/error.rb +34 -25
  29. data/lib/grape/middleware/formatter.rb +9 -9
  30. data/lib/grape/middleware/stack.rb +110 -0
  31. data/lib/grape/middleware/versioner.rb +1 -1
  32. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
  33. data/lib/grape/middleware/versioner/header.rb +3 -3
  34. data/lib/grape/path.rb +10 -2
  35. data/lib/grape/request.rb +1 -1
  36. data/lib/grape/router.rb +10 -19
  37. data/lib/grape/router/pattern.rb +2 -2
  38. data/lib/grape/router/route.rb +3 -3
  39. data/lib/grape/util/content_types.rb +1 -1
  40. data/lib/grape/util/inheritable_setting.rb +7 -2
  41. data/lib/grape/util/reverse_stackable_values.rb +45 -0
  42. data/lib/grape/util/stackable_values.rb +10 -11
  43. data/lib/grape/validations/attributes_iterator.rb +32 -7
  44. data/lib/grape/validations/params_scope.rb +33 -21
  45. data/lib/grape/validations/types.rb +4 -4
  46. data/lib/grape/validations/types/build_coercer.rb +9 -1
  47. data/lib/grape/validations/validators/all_or_none.rb +2 -2
  48. data/lib/grape/validations/validators/allow_blank.rb +10 -11
  49. data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
  50. data/lib/grape/validations/validators/base.rb +16 -6
  51. data/lib/grape/validations/validators/coerce.rb +3 -6
  52. data/lib/grape/validations/validators/default.rb +26 -1
  53. data/lib/grape/validations/validators/exactly_one_of.rb +1 -1
  54. data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
  55. data/lib/grape/validations/validators/presence.rb +1 -1
  56. data/lib/grape/validations/validators/regexp.rb +1 -1
  57. data/lib/grape/validations/validators/values.rb +1 -1
  58. data/lib/grape/version.rb +1 -1
  59. data/spec/grape/api/custom_validations_spec.rb +3 -3
  60. data/spec/grape/api/parameters_modification_spec.rb +41 -0
  61. data/spec/grape/api_spec.rb +335 -108
  62. data/spec/grape/dsl/logger_spec.rb +1 -1
  63. data/spec/grape/dsl/middleware_spec.rb +25 -5
  64. data/spec/grape/dsl/request_response_spec.rb +20 -6
  65. data/spec/grape/dsl/validations_spec.rb +1 -1
  66. data/spec/grape/endpoint_spec.rb +166 -23
  67. data/spec/grape/entity_spec.rb +0 -2
  68. data/spec/grape/exceptions/body_parse_errors_spec.rb +37 -0
  69. data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
  70. data/spec/grape/exceptions/validation_spec.rb +10 -0
  71. data/spec/grape/integration/global_namespace_function_spec.rb +1 -1
  72. data/spec/grape/integration/rack_spec.rb +1 -1
  73. data/spec/grape/middleware/base_spec.rb +1 -1
  74. data/spec/grape/middleware/exception_spec.rb +2 -2
  75. data/spec/grape/middleware/formatter_spec.rb +4 -4
  76. data/spec/grape/middleware/stack_spec.rb +123 -0
  77. data/spec/grape/middleware/versioner/header_spec.rb +6 -6
  78. data/spec/grape/request_spec.rb +22 -22
  79. data/spec/grape/util/inheritable_setting_spec.rb +23 -0
  80. data/spec/grape/util/reverse_stackable_values_spec.rb +131 -0
  81. data/spec/grape/validations/params_scope_spec.rb +88 -1
  82. data/spec/grape/validations/validators/allow_blank_spec.rb +5 -0
  83. data/spec/grape/validations/validators/coerce_spec.rb +5 -5
  84. data/spec/grape/validations/validators/default_spec.rb +44 -0
  85. data/spec/grape/validations/validators/values_spec.rb +1 -1
  86. data/spec/grape/validations_spec.rb +36 -17
  87. data/spec/spec_helper.rb +1 -8
  88. data/spec/support/versioned_helpers.rb +3 -3
  89. metadata +13 -188
  90. data/gemfiles/rails_3.gemfile.lock +0 -225
  91. data/pkg/grape-0.16.1.gem +0 -0
  92. data/pkg/patch.diff +0 -24
  93. data/tmp/Gemfile.lock +0 -63
@@ -5,7 +5,7 @@ module Grape
5
5
  def validate!(params)
6
6
  super
7
7
  if scope_requires_params && no_exclusive_params_are_present
8
- fail Grape::Exceptions::Validation, params: all_keys, message: message(:at_least_one)
8
+ raise Grape::Exceptions::Validation, params: all_keys, message: message(:at_least_one)
9
9
  end
10
10
  params
11
11
  end
@@ -35,19 +35,29 @@ module Grape
35
35
  # @return [void]
36
36
  def validate!(params)
37
37
  attributes = AttributesIterator.new(self, @scope, params)
38
- attributes.each do |resource_params, attr_name|
39
- if @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
38
+ array_errors = []
39
+ attributes.each do |resource_params, attr_name, inside_array|
40
+ next unless @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
41
+
42
+ begin
40
43
  validate_param!(attr_name, resource_params)
44
+ rescue Grape::Exceptions::Validation => e
45
+ raise e unless inside_array
46
+ # we collect errors inside array because
47
+ # there may be more than one error per field
48
+ array_errors << e
41
49
  end
42
50
  end
51
+
52
+ raise Grape::Exceptions::ValidationArrayErrors, array_errors if array_errors.any?
43
53
  end
44
54
 
45
55
  def self.convert_to_short_name(klass)
46
56
  ret = klass.name.gsub(/::/, '/')
47
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
48
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
49
- .tr('-', '_')
50
- .downcase
57
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
58
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
59
+ .tr('-', '_')
60
+ .downcase
51
61
  File.basename(ret, '_validator')
52
62
  end
53
63
 
@@ -11,13 +11,10 @@ module Grape
11
11
  end
12
12
 
13
13
  def validate_param!(attr_name, params)
14
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless params.is_a? Hash
14
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless params.is_a? Hash
15
15
  new_value = coerce_value(params[attr_name])
16
- if valid_type?(new_value)
17
- params[attr_name] = new_value
18
- else
19
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce)
20
- end
16
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless valid_type?(new_value)
17
+ params[attr_name] = new_value
21
18
  end
22
19
 
23
20
  private
@@ -7,7 +7,14 @@ module Grape
7
7
  end
8
8
 
9
9
  def validate_param!(attr_name, params)
10
- params[attr_name] = @default.is_a?(Proc) ? @default.call : @default unless params.key?(attr_name)
10
+ return if params.key? attr_name
11
+ params[attr_name] = if @default.is_a? Proc
12
+ @default.call
13
+ elsif @default.frozen? || !duplicatable?(@default)
14
+ @default
15
+ else
16
+ duplicate(@default)
17
+ end
11
18
  end
12
19
 
13
20
  def validate!(params)
@@ -20,6 +27,24 @@ module Grape
20
27
  end
21
28
  end
22
29
  end
30
+
31
+ private
32
+
33
+ # return true if we might be able to dup this object
34
+ def duplicatable?(obj)
35
+ !obj.nil? &&
36
+ obj != true &&
37
+ obj != false &&
38
+ !obj.is_a?(Symbol) &&
39
+ !obj.is_a?(Numeric)
40
+ end
41
+
42
+ # make a best effort to dup the object
43
+ def duplicate(obj)
44
+ obj.dup
45
+ rescue TypeError
46
+ obj
47
+ end
23
48
  end
24
49
  end
25
50
  end
@@ -5,7 +5,7 @@ module Grape
5
5
  def validate!(params)
6
6
  super
7
7
  if scope_requires_params && none_of_restricted_params_is_present
8
- fail Grape::Exceptions::Validation, params: all_keys, message: message(:exactly_one)
8
+ raise Grape::Exceptions::Validation, params: all_keys, message: message(:exactly_one)
9
9
  end
10
10
  params
11
11
  end
@@ -7,7 +7,7 @@ module Grape
7
7
  def validate!(params)
8
8
  super
9
9
  if two_or_more_exclusive_params_are_present
10
- fail Grape::Exceptions::Validation, params: processing_keys_in_common, message: message(:mutual_exclusion)
10
+ raise Grape::Exceptions::Validation, params: processing_keys_in_common, message: message(:mutual_exclusion)
11
11
  end
12
12
  params
13
13
  end
@@ -8,7 +8,7 @@ module Grape
8
8
 
9
9
  def validate_param!(attr_name, params)
10
10
  return if params.respond_to?(:key?) && params.key?(attr_name)
11
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:presence)
11
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:presence)
12
12
  end
13
13
  end
14
14
  end
@@ -3,7 +3,7 @@ module Grape
3
3
  class RegexpValidator < Base
4
4
  def validate_param!(attr_name, params)
5
5
  return unless params.key?(attr_name) && !params[attr_name].nil? && !(params[attr_name].to_s =~ (options_key?(:value) ? @option[:value] : @option))
6
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:regexp)
6
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:regexp)
7
7
  end
8
8
  end
9
9
  end
@@ -13,7 +13,7 @@ module Grape
13
13
  values = @values.is_a?(Proc) ? @values.call : @values
14
14
  param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
15
15
  return if param_array.all? { |param| values.include?(param) }
16
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values)
16
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values)
17
17
  end
18
18
 
19
19
  private
@@ -1,4 +1,4 @@
1
1
  module Grape
2
2
  # The current version of Grape.
3
- VERSION = '0.16.2'
3
+ VERSION = '0.17.0'.freeze
4
4
  end
@@ -8,7 +8,7 @@ describe Grape::Validations do
8
8
  def validate_param!(attr_name, params)
9
9
  @option = params[:max].to_i if params.key?(:max)
10
10
  return if params[attr_name].length <= @option
11
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
11
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
12
12
  end
13
13
  end
14
14
  end
@@ -87,7 +87,7 @@ describe Grape::Validations do
87
87
  module CustomValidationsSpec
88
88
  class WithMessageKey < Grape::Validations::PresenceValidator
89
89
  def validate_param!(attr_name, _params)
90
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: :presence
90
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: :presence
91
91
  end
92
92
  end
93
93
  end
@@ -126,7 +126,7 @@ describe Grape::Validations do
126
126
  return unless @option
127
127
  # check if user is admin or not
128
128
  # as an example get a token from request and check if it's admin or not
129
- fail Grape::Exceptions::Validation, params: @attrs, message: 'Can not set Admin only field.' unless request.headers['X-Access-Token'] == 'admin'
129
+ raise Grape::Exceptions::Validation, params: @attrs, message: 'Can not set Admin only field.' unless request.headers['X-Access-Token'] == 'admin'
130
130
  end
131
131
  end
132
132
  end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Endpoint do
4
+ subject { Class.new(Grape::API) }
5
+
6
+ def app
7
+ subject
8
+ end
9
+
10
+ before do
11
+ subject.namespace :test do
12
+ params do
13
+ optional :foo, default: '-abcdef'
14
+ end
15
+ get do
16
+ params[:foo].slice!(0)
17
+ params[:foo]
18
+ end
19
+ end
20
+ end
21
+
22
+ context 'when route modifies param value' do
23
+ it 'param default should not change' do
24
+ get '/test'
25
+ expect(last_response.status).to eq 200
26
+ expect(last_response.body).to eq 'abcdef'
27
+
28
+ get '/test'
29
+ expect(last_response.status).to eq 200
30
+ expect(last_response.body).to eq 'abcdef'
31
+
32
+ get '/test?foo=-123456'
33
+ expect(last_response.status).to eq 200
34
+ expect(last_response.body).to eq '123456'
35
+
36
+ get '/test'
37
+ expect(last_response.status).to eq 200
38
+ expect(last_response.body).to eq 'abcdef'
39
+ end
40
+ end
41
+ end
@@ -258,7 +258,7 @@ describe Grape::API do
258
258
  end
259
259
  end
260
260
  get '/route_param/1234'
261
- expect(last_response.body).to eq("{\"foo\":1234}")
261
+ expect(last_response.body).to eq('{"foo":1234}')
262
262
  end
263
263
  end
264
264
  end
@@ -302,16 +302,27 @@ describe Grape::API do
302
302
 
303
303
  describe 'path versioned APIs' do
304
304
  before do
305
- subject.version 'v1', using: :path
305
+ subject.version version, using: :path
306
306
  subject.enable_root_route!
307
307
  end
308
308
 
309
- it 'without a format' do
310
- versioned_get '/', 'v1', using: :path
309
+ context 'when a single version provided' do
310
+ let(:version) { 'v1' }
311
+
312
+ it 'without a format' do
313
+ versioned_get '/', 'v1', using: :path
314
+ end
315
+
316
+ it 'with a format' do
317
+ get '/v1/.json'
318
+ end
311
319
  end
312
320
 
313
- it 'with a format' do
314
- get '/v1/.json'
321
+ context 'when array of versions provided' do
322
+ let(:version) { %w(v1 v2) }
323
+
324
+ it { versioned_get '/', 'v1', using: :path }
325
+ it { versioned_get '/', 'v2', using: :path }
315
326
  end
316
327
  end
317
328
 
@@ -363,12 +374,17 @@ describe Grape::API do
363
374
  end
364
375
 
365
376
  context 'format' do
377
+ module ApiSpec
378
+ class DummyFormatClass
379
+ end
380
+ end
381
+
366
382
  before(:each) do
367
- allow_any_instance_of(Object).to receive(:to_json).and_return('abc')
368
- allow_any_instance_of(Object).to receive(:to_txt).and_return('def')
383
+ allow_any_instance_of(ApiSpec::DummyFormatClass).to receive(:to_json).and_return('abc')
384
+ allow_any_instance_of(ApiSpec::DummyFormatClass).to receive(:to_txt).and_return('def')
369
385
 
370
386
  subject.get('/abc') do
371
- Object.new
387
+ ApiSpec::DummyFormatClass.new
372
388
  end
373
389
  end
374
390
 
@@ -527,6 +543,36 @@ describe Grape::API do
527
543
  expect(last_response.headers['X-Custom-Header']).to eql 'foo'
528
544
  end
529
545
 
546
+ it 'runs only the before filter on 405 bad method' do
547
+ subject.namespace :example do
548
+ before { header 'X-Custom-Header', 'foo' }
549
+ before_validation { raise 'before_validation filter should not run' }
550
+ after_validation { raise 'after_validation filter should not run' }
551
+ after { raise 'after filter should not run' }
552
+ get
553
+ end
554
+
555
+ post '/example'
556
+ expect(last_response.status).to eql 405
557
+ expect(last_response.headers['X-Custom-Header']).to eql 'foo'
558
+ end
559
+
560
+ it 'runs before filter exactly once on 405 bad method' do
561
+ already_run = false
562
+ subject.namespace :example do
563
+ before do
564
+ raise 'before filter ran twice' if already_run
565
+ already_run = true
566
+ header 'X-Custom-Header', 'foo'
567
+ end
568
+ get
569
+ end
570
+
571
+ post '/example'
572
+ expect(last_response.status).to eql 405
573
+ expect(last_response.headers['X-Custom-Header']).to eql 'foo'
574
+ end
575
+
530
576
  context 'when format is xml' do
531
577
  it 'returns a 405 for an unsupported method' do
532
578
  subject.format :xml
@@ -545,6 +591,21 @@ XML
545
591
  end
546
592
  end
547
593
 
594
+ context 'when accessing env' do
595
+ it 'returns a 405 for an unsupported method' do
596
+ subject.before do
597
+ _custom_header_1 = headers['X-Custom-Header']
598
+ _custom_header_2 = env['HTTP_X_CUSTOM_HEADER']
599
+ end
600
+ subject.get 'example' do
601
+ 'example'
602
+ end
603
+ put '/example'
604
+ expect(last_response.status).to eql 405
605
+ expect(last_response.body).to eql '405 Not Allowed'
606
+ end
607
+ end
608
+
548
609
  specify '405 responses includes an Allow header specifying supported methods' do
549
610
  subject.get 'example' do
550
611
  'example'
@@ -1044,7 +1105,7 @@ XML
1044
1105
  describe '.middleware' do
1045
1106
  it 'includes middleware arguments from settings' do
1046
1107
  subject.use ApiSpec::PhonyMiddleware, 'abc', 123
1047
- expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 'abc', 123]]
1108
+ expect(subject.middleware).to eql [[:use, ApiSpec::PhonyMiddleware, 'abc', 123]]
1048
1109
  end
1049
1110
 
1050
1111
  it 'includes all middleware from stacked settings' do
@@ -1053,9 +1114,9 @@ XML
1053
1114
  subject.use ApiSpec::PhonyMiddleware, 'foo'
1054
1115
 
1055
1116
  expect(subject.middleware).to eql [
1056
- [ApiSpec::PhonyMiddleware, 123],
1057
- [ApiSpec::PhonyMiddleware, 'abc'],
1058
- [ApiSpec::PhonyMiddleware, 'foo']
1117
+ [:use, ApiSpec::PhonyMiddleware, 123],
1118
+ [:use, ApiSpec::PhonyMiddleware, 'abc'],
1119
+ [:use, ApiSpec::PhonyMiddleware, 'foo']
1059
1120
  ]
1060
1121
  end
1061
1122
  end
@@ -1063,7 +1124,7 @@ XML
1063
1124
  describe '.use' do
1064
1125
  it 'adds middleware' do
1065
1126
  subject.use ApiSpec::PhonyMiddleware, 123
1066
- expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 123]]
1127
+ expect(subject.middleware).to eql [[:use, ApiSpec::PhonyMiddleware, 123]]
1067
1128
  end
1068
1129
 
1069
1130
  it 'does not show up outside the namespace' do
@@ -1074,8 +1135,8 @@ XML
1074
1135
  inner_middleware = middleware
1075
1136
  end
1076
1137
 
1077
- expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 123]]
1078
- expect(inner_middleware).to eql [[ApiSpec::PhonyMiddleware, 123], [ApiSpec::PhonyMiddleware, 'abc']]
1138
+ expect(subject.middleware).to eql [[:use, ApiSpec::PhonyMiddleware, 123]]
1139
+ expect(inner_middleware).to eql [[:use, ApiSpec::PhonyMiddleware, 123], [:use, ApiSpec::PhonyMiddleware, 'abc']]
1079
1140
  end
1080
1141
 
1081
1142
  it 'calls the middleware' do
@@ -1091,7 +1152,7 @@ XML
1091
1152
  it 'adds a block if one is given' do
1092
1153
  block = -> {}
1093
1154
  subject.use ApiSpec::PhonyMiddleware, &block
1094
- expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, block]]
1155
+ expect(subject.middleware).to eql [[:use, ApiSpec::PhonyMiddleware, block]]
1095
1156
  end
1096
1157
 
1097
1158
  it 'uses a block if one is given' do
@@ -1132,7 +1193,50 @@ XML
1132
1193
  expect(last_response.body).to eq('Caught in the Net')
1133
1194
  end
1134
1195
  end
1196
+
1197
+ describe '.insert_before' do
1198
+ it 'runs before a given middleware' do
1199
+ m = Class.new(Grape::Middleware::Base) do
1200
+ def call(env)
1201
+ env['phony.args'] ||= []
1202
+ env['phony.args'] << @options[:message]
1203
+ @app.call(env)
1204
+ end
1205
+ end
1206
+
1207
+ subject.use ApiSpec::PhonyMiddleware, 'hello'
1208
+ subject.insert_before ApiSpec::PhonyMiddleware, m, message: 'bye'
1209
+ subject.get '/' do
1210
+ env['phony.args'].join(' ')
1211
+ end
1212
+
1213
+ get '/'
1214
+ expect(last_response.body).to eql 'bye hello'
1215
+ end
1216
+ end
1217
+
1218
+ describe '.insert_after' do
1219
+ it 'runs after a given middleware' do
1220
+ m = Class.new(Grape::Middleware::Base) do
1221
+ def call(env)
1222
+ env['phony.args'] ||= []
1223
+ env['phony.args'] << @options[:message]
1224
+ @app.call(env)
1225
+ end
1226
+ end
1227
+
1228
+ subject.use ApiSpec::PhonyMiddleware, 'hello'
1229
+ subject.insert_after ApiSpec::PhonyMiddleware, m, message: 'bye'
1230
+ subject.get '/' do
1231
+ env['phony.args'].join(' ')
1232
+ end
1233
+
1234
+ get '/'
1235
+ expect(last_response.body).to eql 'hello bye'
1236
+ end
1237
+ end
1135
1238
  end
1239
+
1136
1240
  describe '.http_basic' do
1137
1241
  it 'protects any resources on the same scope' do
1138
1242
  subject.http_basic do |u, _p|
@@ -1243,7 +1347,9 @@ XML
1243
1347
  it 'defaults to a standard logger log format' do
1244
1348
  t = Time.at(100)
1245
1349
  allow(Time).to receive(:now).and_return(t)
1246
- expect(subject.io).to receive(:write).with("I, [#{Logger::Formatter.new.send(:format_datetime, t)}\##{Process.pid}] INFO -- : this will be logged\n")
1350
+ message = "this will be logged\n"
1351
+ message = "I, [#{Logger::Formatter.new.send(:format_datetime, t)}\##{Process.pid}] INFO -- : #{message}" if !defined?(Rails) || Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new('4.0')
1352
+ expect(subject.io).to receive(:write).with(message)
1247
1353
  subject.logger.info 'this will be logged'
1248
1354
  end
1249
1355
  end
@@ -1383,7 +1489,7 @@ XML
1383
1489
  describe '.rescue_from' do
1384
1490
  it 'does not rescue errors when rescue_from is not set' do
1385
1491
  subject.get '/exception' do
1386
- fail 'rain!'
1492
+ raise 'rain!'
1387
1493
  end
1388
1494
  expect { get '/exception' }.to raise_error(RuntimeError, 'rain!')
1389
1495
  end
@@ -1396,16 +1502,46 @@ XML
1396
1502
  end
1397
1503
  subject.rescue_from(ArgumentError) { custom_error! :bob }
1398
1504
  subject.get '/custom_error' do
1399
- fail ArgumentError
1505
+ raise ArgumentError
1400
1506
  end
1401
1507
  get '/custom_error'
1402
1508
  expect(last_response.body).to eq 'hello bob'
1403
1509
  end
1404
1510
 
1511
+ context 'with multiple apis' do
1512
+ let(:a) { Class.new(Grape::API) }
1513
+ let(:b) { Class.new(Grape::API) }
1514
+
1515
+ before do
1516
+ a.helpers do
1517
+ def foo
1518
+ error!('foo', 401)
1519
+ end
1520
+ end
1521
+ a.rescue_from(:all) { foo }
1522
+ a.get { raise 'boo' }
1523
+ b.helpers do
1524
+ def foo
1525
+ error!('bar', 401)
1526
+ end
1527
+ end
1528
+ b.rescue_from(:all) { foo }
1529
+ b.get { raise 'boo' }
1530
+ end
1531
+
1532
+ it 'avoids polluting global namespace' do
1533
+ env = Rack::MockRequest.env_for('/')
1534
+
1535
+ expect(a.call(env)[2].body).to eq(['foo'])
1536
+ expect(b.call(env)[2].body).to eq(['bar'])
1537
+ expect(a.call(env)[2].body).to eq(['foo'])
1538
+ end
1539
+ end
1540
+
1405
1541
  it 'rescues all errors if rescue_from :all is called' do
1406
1542
  subject.rescue_from :all
1407
1543
  subject.get '/exception' do
1408
- fail 'rain!'
1544
+ raise 'rain!'
1409
1545
  end
1410
1546
  get '/exception'
1411
1547
  expect(last_response.status).to eql 500
@@ -1417,7 +1553,7 @@ XML
1417
1553
  subject.default_format :json
1418
1554
  subject.rescue_from :all
1419
1555
  subject.get '/exception' do
1420
- fail 'rain!'
1556
+ raise 'rain!'
1421
1557
  end
1422
1558
  get '/exception'
1423
1559
  expect(last_response.status).to eql 500
@@ -1426,8 +1562,8 @@ XML
1426
1562
 
1427
1563
  it 'rescues only certain errors if rescue_from is called with specific errors' do
1428
1564
  subject.rescue_from ArgumentError
1429
- subject.get('/rescued') { fail ArgumentError }
1430
- subject.get('/unrescued') { fail 'beefcake' }
1565
+ subject.get('/rescued') { raise ArgumentError }
1566
+ subject.get('/unrescued') { raise 'beefcake' }
1431
1567
 
1432
1568
  get '/rescued'
1433
1569
  expect(last_response.status).to eql 500
@@ -1443,7 +1579,7 @@ XML
1443
1579
  end
1444
1580
 
1445
1581
  it 'does not re-raise exceptions of type Grape::Exceptions::Base' do
1446
- subject.get('/custom_exception') { fail ApiSpec::CustomError }
1582
+ subject.get('/custom_exception') { raise ApiSpec::CustomError }
1447
1583
 
1448
1584
  expect { get '/custom_exception' }.not_to raise_error
1449
1585
  end
@@ -1453,7 +1589,7 @@ XML
1453
1589
  rack_response('New Error', e.status)
1454
1590
  end
1455
1591
  subject.get '/custom_error' do
1456
- fail ApiSpec::CustomError, status: 400, message: 'Custom Error'
1592
+ raise ApiSpec::CustomError, status: 400, message: 'Custom Error'
1457
1593
  end
1458
1594
 
1459
1595
  get '/custom_error'
@@ -1464,7 +1600,7 @@ XML
1464
1600
 
1465
1601
  it 'can rescue exceptions raised in the formatter' do
1466
1602
  formatter = double(:formatter)
1467
- allow(formatter).to receive(:call) { fail StandardError }
1603
+ allow(formatter).to receive(:call) { raise StandardError }
1468
1604
  allow(Grape::Formatter).to receive(:formatter_for) { formatter }
1469
1605
 
1470
1606
  subject.rescue_from :all do |_e|
@@ -1484,7 +1620,7 @@ XML
1484
1620
  rack_response("rescued from #{e.message}", 202)
1485
1621
  end
1486
1622
  subject.get '/exception' do
1487
- fail 'rain!'
1623
+ raise 'rain!'
1488
1624
  end
1489
1625
  get '/exception'
1490
1626
  expect(last_response.status).to eql 202
@@ -1503,7 +1639,7 @@ XML
1503
1639
  rack_response("rescued from #{e.class.name}", 500)
1504
1640
  end
1505
1641
  subject.get '/exception' do
1506
- fail ConnectionError
1642
+ raise ConnectionError
1507
1643
  end
1508
1644
  get '/exception'
1509
1645
  expect(last_response.status).to eql 500
@@ -1514,7 +1650,7 @@ XML
1514
1650
  rack_response("rescued from #{e.class.name}", 500)
1515
1651
  end
1516
1652
  subject.get '/exception' do
1517
- fail ConnectionError
1653
+ raise ConnectionError
1518
1654
  end
1519
1655
  get '/exception'
1520
1656
  expect(last_response.status).to eql 500
@@ -1525,7 +1661,7 @@ XML
1525
1661
  rack_response("rescued from #{e.class.name}", 500)
1526
1662
  end
1527
1663
  subject.get '/exception' do
1528
- fail ConnectionError
1664
+ raise ConnectionError
1529
1665
  end
1530
1666
  get '/exception'
1531
1667
  expect(last_response.status).to eql 500
@@ -1539,10 +1675,10 @@ XML
1539
1675
  rack_response("rescued from #{e.class.name}", 500)
1540
1676
  end
1541
1677
  subject.get '/connection' do
1542
- fail ConnectionError
1678
+ raise ConnectionError
1543
1679
  end
1544
1680
  subject.get '/database' do
1545
- fail DatabaseError
1681
+ raise DatabaseError
1546
1682
  end
1547
1683
  get '/connection'
1548
1684
  expect(last_response.status).to eql 500
@@ -1556,7 +1692,7 @@ XML
1556
1692
  rack_response("rescued from #{e.class.name}", 500)
1557
1693
  end
1558
1694
  subject.get '/uncaught' do
1559
- fail CommunicationError
1695
+ raise CommunicationError
1560
1696
  end
1561
1697
  expect { get '/uncaught' }.to raise_error(CommunicationError)
1562
1698
  end
@@ -1568,7 +1704,7 @@ XML
1568
1704
  subject.rescue_from ArgumentError, lambda {
1569
1705
  rack_response('rescued with a lambda', 400)
1570
1706
  }
1571
- subject.get('/rescue_lambda') { fail ArgumentError }
1707
+ subject.get('/rescue_lambda') { raise ArgumentError }
1572
1708
 
1573
1709
  get '/rescue_lambda'
1574
1710
  expect(last_response.status).to eq(400)
@@ -1579,7 +1715,7 @@ XML
1579
1715
  subject.rescue_from ArgumentError, lambda { |e|
1580
1716
  rack_response(e.message, 400)
1581
1717
  }
1582
- subject.get('/rescue_lambda') { fail ArgumentError, 'lambda takes an argument' }
1718
+ subject.get('/rescue_lambda') { raise ArgumentError, 'lambda takes an argument' }
1583
1719
 
1584
1720
  get '/rescue_lambda'
1585
1721
  expect(last_response.status).to eq(400)
@@ -1593,21 +1729,56 @@ XML
1593
1729
  def rescue_arg_error
1594
1730
  error!('500 ArgumentError', 500)
1595
1731
  end
1732
+
1733
+ def rescue_no_method_error
1734
+ error!('500 NoMethodError', 500)
1735
+ end
1596
1736
  end
1597
1737
  subject.rescue_from ArgumentError, with: :rescue_arg_error
1598
- subject.get('/rescue_method') { fail ArgumentError }
1738
+ subject.rescue_from NoMethodError, with: :rescue_no_method_error
1739
+ subject.get('/rescue_arg_error') { raise ArgumentError }
1740
+ subject.get('/rescue_no_method_error') { raise NoMethodError }
1599
1741
 
1600
- get '/rescue_method'
1742
+ get '/rescue_arg_error'
1601
1743
  expect(last_response.status).to eq(500)
1602
1744
  expect(last_response.body).to eq('500 ArgumentError')
1745
+
1746
+ get '/rescue_no_method_error'
1747
+ expect(last_response.status).to eq(500)
1748
+ expect(last_response.body).to eq('500 NoMethodError')
1603
1749
  end
1604
1750
 
1605
1751
  it 'aborts if the specified method name does not exist' do
1606
1752
  subject.rescue_from :all, with: :not_exist_method
1607
- subject.get('/rescue_method') { fail StandardError }
1753
+ subject.get('/rescue_method') { raise StandardError }
1608
1754
 
1609
1755
  expect { get '/rescue_method' }.to raise_error(NoMethodError, 'undefined method `not_exist_method\'')
1610
1756
  end
1757
+
1758
+ it 'correctly chooses exception handler if :all handler is specified' do
1759
+ subject.helpers do
1760
+ def rescue_arg_error
1761
+ error!('500 ArgumentError', 500)
1762
+ end
1763
+
1764
+ def rescue_all_errors
1765
+ error!('500 AnotherError', 500)
1766
+ end
1767
+ end
1768
+
1769
+ subject.rescue_from ArgumentError, with: :rescue_arg_error
1770
+ subject.rescue_from :all, with: :rescue_all_errors
1771
+ subject.get('/argument_error') { raise ArgumentError }
1772
+ subject.get('/another_error') { raise NoMethodError }
1773
+
1774
+ get '/argument_error'
1775
+ expect(last_response.status).to eq(500)
1776
+ expect(last_response.body).to eq('500 ArgumentError')
1777
+
1778
+ get '/another_error'
1779
+ expect(last_response.status).to eq(500)
1780
+ expect(last_response.body).to eq('500 AnotherError')
1781
+ end
1611
1782
  end
1612
1783
 
1613
1784
  describe '.rescue_from klass, rescue_subclasses: boolean' do
@@ -1625,13 +1796,13 @@ XML
1625
1796
  rack_response("rescued from #{e.class.name}", 500)
1626
1797
  end
1627
1798
  subject.get '/caught_child' do
1628
- fail ApiSpec::APIErrors::ChildError
1799
+ raise ApiSpec::APIErrors::ChildError
1629
1800
  end
1630
1801
  subject.get '/caught_parent' do
1631
- fail ApiSpec::APIErrors::ParentError
1802
+ raise ApiSpec::APIErrors::ParentError
1632
1803
  end
1633
1804
  subject.get '/uncaught_parent' do
1634
- fail StandardError
1805
+ raise StandardError
1635
1806
  end
1636
1807
 
1637
1808
  get '/caught_child'
@@ -1646,7 +1817,7 @@ XML
1646
1817
  rack_response("rescued from #{e.class.name}", 500)
1647
1818
  end
1648
1819
  subject.get '/caught_child' do
1649
- fail ApiSpec::APIErrors::ChildError
1820
+ raise ApiSpec::APIErrors::ChildError
1650
1821
  end
1651
1822
 
1652
1823
  get '/caught_child'
@@ -1658,7 +1829,7 @@ XML
1658
1829
  rack_response("rescued from #{e.class.name}", 500)
1659
1830
  end
1660
1831
  subject.get '/uncaught' do
1661
- fail ApiSpec::APIErrors::ChildError
1832
+ raise ApiSpec::APIErrors::ChildError
1662
1833
  end
1663
1834
  expect { get '/uncaught' }.to raise_error(ApiSpec::APIErrors::ChildError)
1664
1835
  end
@@ -1669,7 +1840,7 @@ XML
1669
1840
  subject.rescue_from :all
1670
1841
  subject.format :txt
1671
1842
  subject.get '/exception' do
1672
- fail 'rain!'
1843
+ raise 'rain!'
1673
1844
  end
1674
1845
  get '/exception'
1675
1846
  expect(last_response.body).to eql 'rain!'
@@ -1679,7 +1850,7 @@ XML
1679
1850
  subject.rescue_from :all, backtrace: true
1680
1851
  subject.format :txt
1681
1852
  subject.get '/exception' do
1682
- fail 'rain!'
1853
+ raise 'rain!'
1683
1854
  end
1684
1855
  get '/exception'
1685
1856
  expect(last_response.body.start_with?("rain!\r\n")).to be true
@@ -1690,7 +1861,7 @@ XML
1690
1861
  subject.content_type :foo, 'text/foo'
1691
1862
  subject.rescue_from :all
1692
1863
  subject.get '/exception' do
1693
- fail 'rain!'
1864
+ raise 'rain!'
1694
1865
  end
1695
1866
  get '/exception.foo'
1696
1867
  expect(last_response.body).to start_with 'rain!'
@@ -1702,7 +1873,7 @@ XML
1702
1873
  subject.content_type :json, 'application/json'
1703
1874
  subject.content_type :foo, 'text/foo'
1704
1875
  subject.get '/exception' do
1705
- fail 'rain!'
1876
+ raise 'rain!'
1706
1877
  end
1707
1878
  get '/exception.json'
1708
1879
  expect(last_response.body).to eq('{"error":"rain!"}')
@@ -1724,7 +1895,7 @@ XML
1724
1895
  subject.rescue_from :all, backtrace: true
1725
1896
  subject.error_formatter :txt, ApiSpec::CustomErrorFormatter
1726
1897
  subject.get '/exception' do
1727
- fail 'rain!'
1898
+ raise 'rain!'
1728
1899
  end
1729
1900
  get '/exception'
1730
1901
  expect(last_response.body).to eq('message: rain! @backtrace')
@@ -1746,7 +1917,7 @@ XML
1746
1917
  it 'returns a custom error format' do
1747
1918
  subject.rescue_from :all, backtrace: true
1748
1919
  subject.error_formatter :txt, with: ApiSpec::CustomErrorFormatter
1749
- subject.get('/exception') { fail 'rain!' }
1920
+ subject.get('/exception') { raise 'rain!' }
1750
1921
 
1751
1922
  get '/exception'
1752
1923
  expect(last_response.body).to eq('message: rain! @backtrace')
@@ -1758,7 +1929,7 @@ XML
1758
1929
  subject.rescue_from :all
1759
1930
  subject.format :json
1760
1931
  subject.get '/exception' do
1761
- fail 'rain!'
1932
+ raise 'rain!'
1762
1933
  end
1763
1934
  get '/exception'
1764
1935
  expect(last_response.body).to eql '{"error":"rain!"}'
@@ -1767,7 +1938,7 @@ XML
1767
1938
  subject.rescue_from :all, backtrace: true
1768
1939
  subject.format :json
1769
1940
  subject.get '/exception' do
1770
- fail 'rain!'
1941
+ raise 'rain!'
1771
1942
  end
1772
1943
  get '/exception'
1773
1944
  json = MultiJson.load(last_response.body)
@@ -1995,7 +2166,7 @@ XML
1995
2166
  subject.rescue_from :all
1996
2167
  subject.default_error_status 200
1997
2168
  subject.get '/exception' do
1998
- fail 'rain!'
2169
+ raise 'rain!'
1999
2170
  end
2000
2171
  get '/exception'
2001
2172
  expect(last_response.status).to eql 200
@@ -2003,7 +2174,7 @@ XML
2003
2174
  it 'has a default error status' do
2004
2175
  subject.rescue_from :all
2005
2176
  subject.get '/exception' do
2006
- fail 'rain!'
2177
+ raise 'rain!'
2007
2178
  end
2008
2179
  get '/exception'
2009
2180
  expect(last_response.status).to eql 500
@@ -2135,9 +2306,9 @@ XML
2135
2306
  expect(last_response.body).to eq('["a","b,c"]')
2136
2307
  end
2137
2308
  it 'sets params' do
2138
- expect(subject.routes.map { |route|
2309
+ expect(subject.routes.map do |route|
2139
2310
  { params: route.params }
2140
- }).to eq [
2311
+ end).to eq [
2141
2312
  {
2142
2313
  params: {
2143
2314
  'string' => '',
@@ -2165,9 +2336,9 @@ XML
2165
2336
  end
2166
2337
  end
2167
2338
  it 'sets params' do
2168
- expect(subject.routes.map { |route|
2339
+ expect(subject.routes.map do |route|
2169
2340
  { params: route.params }
2170
- }).to eq [
2341
+ end).to eq [
2171
2342
  {
2172
2343
  params: {
2173
2344
  'one' => { required: true, desc: 'a token' },
@@ -2196,9 +2367,9 @@ XML
2196
2367
  end
2197
2368
  end
2198
2369
  it 'sets params' do
2199
- expect(subject.routes.map { |route|
2370
+ expect(subject.routes.map do |route|
2200
2371
  { params: route.params }
2201
- }).to eq [
2372
+ end).to eq [
2202
2373
  {
2203
2374
  params: {
2204
2375
  'one' => { required: true, desc: 'a token' },
@@ -2250,7 +2421,7 @@ XML
2250
2421
  end
2251
2422
  it 'describes a method' do
2252
2423
  subject.desc 'first method'
2253
- subject.get :first do; end
2424
+ subject.get :first
2254
2425
  expect(subject.routes.length).to eq(1)
2255
2426
  route = subject.routes.first
2256
2427
  expect(route.description).to eq('first method')
@@ -2260,31 +2431,31 @@ XML
2260
2431
  end
2261
2432
  it 'has params which does not include format and version as named captures' do
2262
2433
  subject.version :v1, using: :path
2263
- subject.get :first do; end
2434
+ subject.get :first
2264
2435
  param_keys = subject.routes.first.params.keys
2265
2436
  expect(param_keys).not_to include('format')
2266
2437
  expect(param_keys).not_to include('version')
2267
2438
  end
2268
2439
  it 'describes methods separately' do
2269
2440
  subject.desc 'first method'
2270
- subject.get :first do; end
2441
+ subject.get :first
2271
2442
  subject.desc 'second method'
2272
- subject.get :second do; end
2443
+ subject.get :second
2273
2444
  expect(subject.routes.count).to eq(2)
2274
- expect(subject.routes.map { |route|
2445
+ expect(subject.routes.map do |route|
2275
2446
  { description: route.description, params: route.params }
2276
- }).to eq [
2447
+ end).to eq [
2277
2448
  { description: 'first method', params: {} },
2278
2449
  { description: 'second method', params: {} }
2279
2450
  ]
2280
2451
  end
2281
2452
  it 'resets desc' do
2282
2453
  subject.desc 'first method'
2283
- subject.get :first do; end
2284
- subject.get :second do; end
2285
- expect(subject.routes.map { |route|
2454
+ subject.get :first
2455
+ subject.get :second
2456
+ expect(subject.routes.map do |route|
2286
2457
  { description: route.description, params: route.params }
2287
- }).to eq [
2458
+ end).to eq [
2288
2459
  { description: 'first method', params: {} },
2289
2460
  { description: nil, params: {} }
2290
2461
  ]
@@ -2292,20 +2463,20 @@ XML
2292
2463
  it 'namespaces and describe arbitrary parameters' do
2293
2464
  subject.namespace 'ns' do
2294
2465
  desc 'ns second', foo: 'bar'
2295
- get 'second' do; end
2466
+ get 'second'
2296
2467
  end
2297
- expect(subject.routes.map { |route|
2468
+ expect(subject.routes.map do |route|
2298
2469
  { description: route.description, foo: route.route_foo, params: route.params }
2299
- }).to eq [
2470
+ end).to eq [
2300
2471
  { description: 'ns second', foo: 'bar', params: {} }
2301
2472
  ]
2302
2473
  end
2303
2474
  it 'includes details' do
2304
2475
  subject.desc 'method', details: 'method details'
2305
- subject.get 'method' do; end
2306
- expect(subject.routes.map { |route|
2476
+ subject.get 'method'
2477
+ expect(subject.routes.map do |route|
2307
2478
  { description: route.description, details: route.details, params: route.params }
2308
- }).to eq [
2479
+ end).to eq [
2309
2480
  { description: 'method', details: 'method details', params: {} }
2310
2481
  ]
2311
2482
  end
@@ -2314,9 +2485,9 @@ XML
2314
2485
  subject.get 'reverse' do
2315
2486
  params[:s].reverse
2316
2487
  end
2317
- expect(subject.routes.map { |route|
2488
+ expect(subject.routes.map do |route|
2318
2489
  { description: route.description, params: route.params }
2319
- }).to eq [
2490
+ end).to eq [
2320
2491
  { description: 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } } }
2321
2492
  ]
2322
2493
  end
@@ -2327,17 +2498,17 @@ XML
2327
2498
  optional :param2
2328
2499
  end
2329
2500
  subject.namespace 'ns1' do
2330
- get do; end
2501
+ get { ; }
2331
2502
  end
2332
2503
  subject.params do
2333
2504
  optional :param2
2334
2505
  end
2335
2506
  subject.namespace 'ns2' do
2336
- get do; end
2507
+ get { ; }
2337
2508
  end
2338
- routes_doc = subject.routes.map { |route|
2509
+ routes_doc = subject.routes.map do |route|
2339
2510
  { description: route.description, params: route.params }
2340
- }
2511
+ end
2341
2512
  expect(routes_doc).to eq [
2342
2513
  { description: 'global description',
2343
2514
  params: {
@@ -2362,12 +2533,12 @@ XML
2362
2533
  params do
2363
2534
  optional :method_param, desc: 'method parameter'
2364
2535
  end
2365
- get 'method' do; end
2536
+ get 'method'
2366
2537
  end
2367
2538
 
2368
- routes_doc = subject.routes.map { |route|
2539
+ routes_doc = subject.routes.map do |route|
2369
2540
  { description: route.description, params: route.params }
2370
- }
2541
+ end
2371
2542
  expect(routes_doc).to eq [
2372
2543
  { description: 'method',
2373
2544
  params: {
@@ -2394,12 +2565,12 @@ XML
2394
2565
  params do
2395
2566
  optional :method_param, desc: 'method param'
2396
2567
  end
2397
- get 'method' do; end
2568
+ get 'method'
2398
2569
  end
2399
2570
  end
2400
- expect(subject.routes.map { |route|
2571
+ expect(subject.routes.map do |route|
2401
2572
  { description: route.description, params: route.params }
2402
- }).to eq [
2573
+ end).to eq [
2403
2574
  { description: 'method',
2404
2575
  params: {
2405
2576
  'ns_param' => { required: true, desc: 'ns param 2' },
@@ -2422,7 +2593,7 @@ XML
2422
2593
  requires :param2, desc: 'group2 param2 desc'
2423
2594
  end
2424
2595
  end
2425
- subject.get 'method' do; end
2596
+ subject.get 'method'
2426
2597
 
2427
2598
  expect(subject.routes.map(&:params)).to eq [{
2428
2599
  'group1' => { required: true, type: 'Array' },
@@ -2441,10 +2612,10 @@ XML
2441
2612
  requires :nested_param, desc: 'nested param'
2442
2613
  end
2443
2614
  end
2444
- subject.get 'method' do; end
2445
- expect(subject.routes.map { |route|
2615
+ subject.get 'method'
2616
+ expect(subject.routes.map do |route|
2446
2617
  { description: route.description, params: route.params }
2447
- }).to eq [
2618
+ end).to eq [
2448
2619
  { description: 'nesting',
2449
2620
  params: {
2450
2621
  'root_param' => { required: true, desc: 'root param' },
@@ -2465,10 +2636,10 @@ XML
2465
2636
  subject.params do
2466
2637
  requires :one_param, desc: 'one param'
2467
2638
  end
2468
- subject.get 'method' do; end
2469
- expect(subject.routes.map { |route|
2639
+ subject.get 'method'
2640
+ expect(subject.routes.map do |route|
2470
2641
  { description: route.description, params: route.params }
2471
- }).to eq [
2642
+ end).to eq [
2472
2643
  { description: nil, params: { 'one_param' => { required: true, desc: 'one param' } } }
2473
2644
  ]
2474
2645
  end
@@ -2477,9 +2648,9 @@ XML
2477
2648
  subject.get 'reverse/:s' do
2478
2649
  params[:s].reverse
2479
2650
  end
2480
- expect(subject.routes.map { |route|
2651
+ expect(subject.routes.map do |route|
2481
2652
  { description: route.description, params: route.params }
2482
- }).to eq [
2653
+ end).to eq [
2483
2654
  { description: 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } } }
2484
2655
  ]
2485
2656
  end
@@ -2560,22 +2731,78 @@ XML
2560
2731
  expect(last_response.body).to eq('yo')
2561
2732
  end
2562
2733
 
2563
- it 'inherits rescues even when some defined by mounted' do
2564
- subject.rescue_from :all do |e|
2565
- rack_response("rescued from #{e.message}", 202)
2734
+ context 'when some rescues are defined by mounted' do
2735
+ it 'inherits parent rescues' do
2736
+ subject.rescue_from :all do |e|
2737
+ rack_response("rescued from #{e.message}", 202)
2738
+ end
2739
+
2740
+ app = Class.new(Grape::API)
2741
+
2742
+ subject.namespace :mounted do
2743
+ app.rescue_from ArgumentError
2744
+ app.get('/fail') { raise 'doh!' }
2745
+ mount app
2746
+ end
2747
+
2748
+ get '/mounted/fail'
2749
+ expect(last_response.status).to eql 202
2750
+ expect(last_response.body).to eq('rescued from doh!')
2566
2751
  end
2752
+ it 'prefers rescues defined by mounted if they rescue similar error class' do
2753
+ subject.rescue_from StandardError do
2754
+ rack_response('outer rescue')
2755
+ end
2567
2756
 
2568
- app = Class.new(Grape::API)
2757
+ app = Class.new(Grape::API)
2569
2758
 
2570
- subject.namespace :mounted do
2571
- app.rescue_from ArgumentError
2572
- app.get('/fail') { fail 'doh!' }
2573
- mount app
2759
+ subject.namespace :mounted do
2760
+ rescue_from StandardError do
2761
+ rack_response('inner rescue')
2762
+ end
2763
+ app.get('/fail') { raise 'doh!' }
2764
+ mount app
2765
+ end
2766
+
2767
+ get '/mounted/fail'
2768
+ expect(last_response.body).to eq('inner rescue')
2574
2769
  end
2770
+ it 'prefers rescues defined by mounted even if outer is more specific' do
2771
+ subject.rescue_from ArgumentError do
2772
+ rack_response('outer rescue')
2773
+ end
2774
+
2775
+ app = Class.new(Grape::API)
2776
+
2777
+ subject.namespace :mounted do
2778
+ rescue_from StandardError do
2779
+ rack_response('inner rescue')
2780
+ end
2781
+ app.get('/fail') { raise ArgumentError.new }
2782
+ mount app
2783
+ end
2575
2784
 
2576
- get '/mounted/fail'
2577
- expect(last_response.status).to eql 202
2578
- expect(last_response.body).to eq('rescued from doh!')
2785
+ get '/mounted/fail'
2786
+ expect(last_response.body).to eq('inner rescue')
2787
+ end
2788
+ it 'prefers more specific rescues defined by mounted' do
2789
+ subject.rescue_from StandardError do
2790
+ rack_response('outer rescue')
2791
+ end
2792
+
2793
+ app = Class.new(Grape::API)
2794
+
2795
+ subject.namespace :mounted do
2796
+ rescue_from ArgumentError do
2797
+ rack_response('inner rescue')
2798
+ end
2799
+ app.get('/fail') { raise ArgumentError.new }
2800
+ mount app
2801
+ end
2802
+
2803
+ get '/mounted/fail'
2804
+ expect(last_response.body).to eq('inner rescue')
2805
+ end
2579
2806
  end
2580
2807
 
2581
2808
  it 'collects the routes of the mounted api' do