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.
- checksums.yaml +4 -4
- data/Appraisals +4 -0
- data/CHANGELOG.md +54 -27
- data/Dangerfile +80 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +61 -27
- data/README.md +135 -7
- data/Rakefile +34 -30
- data/UPGRADING.md +21 -0
- data/gemfiles/rack_1.5.2.gemfile +21 -0
- data/gemfiles/rails_3.gemfile +22 -1
- data/gemfiles/rails_4.gemfile +21 -0
- data/gemfiles/rails_5.gemfile +34 -0
- data/grape.gemspec +0 -14
- data/lib/grape.rb +2 -0
- data/lib/grape/api.rb +9 -2
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +15 -17
- data/lib/grape/dsl/middleware.rb +15 -1
- data/lib/grape/dsl/parameters.rb +16 -14
- data/lib/grape/dsl/request_response.rb +24 -20
- data/lib/grape/dsl/routing.rb +11 -10
- data/lib/grape/dsl/settings.rb +16 -0
- data/lib/grape/endpoint.rb +77 -60
- data/lib/grape/exceptions/validation.rb +5 -2
- data/lib/grape/exceptions/validation_array_errors.rb +11 -0
- data/lib/grape/formatter/xml.rb +1 -1
- data/lib/grape/middleware/error.rb +34 -25
- data/lib/grape/middleware/formatter.rb +9 -9
- data/lib/grape/middleware/stack.rb +110 -0
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +3 -3
- data/lib/grape/path.rb +10 -2
- data/lib/grape/request.rb +1 -1
- data/lib/grape/router.rb +10 -19
- data/lib/grape/router/pattern.rb +2 -2
- data/lib/grape/router/route.rb +3 -3
- data/lib/grape/util/content_types.rb +1 -1
- data/lib/grape/util/inheritable_setting.rb +7 -2
- data/lib/grape/util/reverse_stackable_values.rb +45 -0
- data/lib/grape/util/stackable_values.rb +10 -11
- data/lib/grape/validations/attributes_iterator.rb +32 -7
- data/lib/grape/validations/params_scope.rb +33 -21
- data/lib/grape/validations/types.rb +4 -4
- data/lib/grape/validations/types/build_coercer.rb +9 -1
- data/lib/grape/validations/validators/all_or_none.rb +2 -2
- data/lib/grape/validations/validators/allow_blank.rb +10 -11
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
- data/lib/grape/validations/validators/base.rb +16 -6
- data/lib/grape/validations/validators/coerce.rb +3 -6
- data/lib/grape/validations/validators/default.rb +26 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +1 -1
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
- data/lib/grape/validations/validators/presence.rb +1 -1
- data/lib/grape/validations/validators/regexp.rb +1 -1
- data/lib/grape/validations/validators/values.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +3 -3
- data/spec/grape/api/parameters_modification_spec.rb +41 -0
- data/spec/grape/api_spec.rb +335 -108
- data/spec/grape/dsl/logger_spec.rb +1 -1
- data/spec/grape/dsl/middleware_spec.rb +25 -5
- data/spec/grape/dsl/request_response_spec.rb +20 -6
- data/spec/grape/dsl/validations_spec.rb +1 -1
- data/spec/grape/endpoint_spec.rb +166 -23
- data/spec/grape/entity_spec.rb +0 -2
- data/spec/grape/exceptions/body_parse_errors_spec.rb +37 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
- data/spec/grape/exceptions/validation_spec.rb +10 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +1 -1
- data/spec/grape/integration/rack_spec.rb +1 -1
- data/spec/grape/middleware/base_spec.rb +1 -1
- data/spec/grape/middleware/exception_spec.rb +2 -2
- data/spec/grape/middleware/formatter_spec.rb +4 -4
- data/spec/grape/middleware/stack_spec.rb +123 -0
- data/spec/grape/middleware/versioner/header_spec.rb +6 -6
- data/spec/grape/request_spec.rb +22 -22
- data/spec/grape/util/inheritable_setting_spec.rb +23 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +131 -0
- data/spec/grape/validations/params_scope_spec.rb +88 -1
- data/spec/grape/validations/validators/allow_blank_spec.rb +5 -0
- data/spec/grape/validations/validators/coerce_spec.rb +5 -5
- data/spec/grape/validations/validators/default_spec.rb +44 -0
- data/spec/grape/validations/validators/values_spec.rb +1 -1
- data/spec/grape/validations_spec.rb +36 -17
- data/spec/spec_helper.rb +1 -8
- data/spec/support/versioned_helpers.rb +3 -3
- metadata +13 -188
- data/gemfiles/rails_3.gemfile.lock +0 -225
- data/pkg/grape-0.16.1.gem +0 -0
- data/pkg/patch.diff +0 -24
- 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
|
-
|
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
|
-
|
39
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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
|
-
|
17
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
16
|
+
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values)
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
data/lib/grape/version.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/spec/grape/api_spec.rb
CHANGED
@@ -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(
|
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
|
305
|
+
subject.version version, using: :path
|
306
306
|
subject.enable_root_route!
|
307
307
|
end
|
308
308
|
|
309
|
-
|
310
|
-
|
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
|
-
|
314
|
-
|
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(
|
368
|
-
allow_any_instance_of(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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') {
|
1430
|
-
subject.get('/unrescued') {
|
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') {
|
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
|
-
|
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) {
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1678
|
+
raise ConnectionError
|
1543
1679
|
end
|
1544
1680
|
subject.get '/database' do
|
1545
|
-
|
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
|
-
|
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') {
|
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') {
|
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.
|
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 '/
|
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') {
|
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
|
-
|
1799
|
+
raise ApiSpec::APIErrors::ChildError
|
1629
1800
|
end
|
1630
1801
|
subject.get '/caught_parent' do
|
1631
|
-
|
1802
|
+
raise ApiSpec::APIErrors::ParentError
|
1632
1803
|
end
|
1633
1804
|
subject.get '/uncaught_parent' do
|
1634
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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') {
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
2309
|
+
expect(subject.routes.map do |route|
|
2139
2310
|
{ params: route.params }
|
2140
|
-
|
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
|
2339
|
+
expect(subject.routes.map do |route|
|
2169
2340
|
{ params: route.params }
|
2170
|
-
|
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
|
2370
|
+
expect(subject.routes.map do |route|
|
2200
2371
|
{ params: route.params }
|
2201
|
-
|
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
|
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
|
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
|
2441
|
+
subject.get :first
|
2271
2442
|
subject.desc 'second method'
|
2272
|
-
subject.get :second
|
2443
|
+
subject.get :second
|
2273
2444
|
expect(subject.routes.count).to eq(2)
|
2274
|
-
expect(subject.routes.map
|
2445
|
+
expect(subject.routes.map do |route|
|
2275
2446
|
{ description: route.description, params: route.params }
|
2276
|
-
|
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
|
2284
|
-
subject.get :second
|
2285
|
-
expect(subject.routes.map
|
2454
|
+
subject.get :first
|
2455
|
+
subject.get :second
|
2456
|
+
expect(subject.routes.map do |route|
|
2286
2457
|
{ description: route.description, params: route.params }
|
2287
|
-
|
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'
|
2466
|
+
get 'second'
|
2296
2467
|
end
|
2297
|
-
expect(subject.routes.map
|
2468
|
+
expect(subject.routes.map do |route|
|
2298
2469
|
{ description: route.description, foo: route.route_foo, params: route.params }
|
2299
|
-
|
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'
|
2306
|
-
expect(subject.routes.map
|
2476
|
+
subject.get 'method'
|
2477
|
+
expect(subject.routes.map do |route|
|
2307
2478
|
{ description: route.description, details: route.details, params: route.params }
|
2308
|
-
|
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
|
2488
|
+
expect(subject.routes.map do |route|
|
2318
2489
|
{ description: route.description, params: route.params }
|
2319
|
-
|
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
|
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
|
2507
|
+
get { ; }
|
2337
2508
|
end
|
2338
|
-
routes_doc = subject.routes.map
|
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'
|
2536
|
+
get 'method'
|
2366
2537
|
end
|
2367
2538
|
|
2368
|
-
routes_doc = subject.routes.map
|
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'
|
2568
|
+
get 'method'
|
2398
2569
|
end
|
2399
2570
|
end
|
2400
|
-
expect(subject.routes.map
|
2571
|
+
expect(subject.routes.map do |route|
|
2401
2572
|
{ description: route.description, params: route.params }
|
2402
|
-
|
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'
|
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'
|
2445
|
-
expect(subject.routes.map
|
2615
|
+
subject.get 'method'
|
2616
|
+
expect(subject.routes.map do |route|
|
2446
2617
|
{ description: route.description, params: route.params }
|
2447
|
-
|
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'
|
2469
|
-
expect(subject.routes.map
|
2639
|
+
subject.get 'method'
|
2640
|
+
expect(subject.routes.map do |route|
|
2470
2641
|
{ description: route.description, params: route.params }
|
2471
|
-
|
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
|
2651
|
+
expect(subject.routes.map do |route|
|
2481
2652
|
{ description: route.description, params: route.params }
|
2482
|
-
|
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
|
-
|
2564
|
-
|
2565
|
-
|
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
|
-
|
2757
|
+
app = Class.new(Grape::API)
|
2569
2758
|
|
2570
|
-
|
2571
|
-
|
2572
|
-
|
2573
|
-
|
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
|
-
|
2577
|
-
|
2578
|
-
|
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
|