grape 0.14.0 → 0.15.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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -4
  3. data/Gemfile.lock +13 -13
  4. data/README.md +290 -12
  5. data/UPGRADING.md +68 -1
  6. data/gemfiles/rails_3.gemfile +1 -1
  7. data/lib/grape.rb +8 -2
  8. data/lib/grape/api.rb +40 -34
  9. data/lib/grape/dsl/configuration.rb +2 -115
  10. data/lib/grape/dsl/desc.rb +101 -0
  11. data/lib/grape/dsl/headers.rb +16 -0
  12. data/lib/grape/dsl/helpers.rb +5 -9
  13. data/lib/grape/dsl/inside_route.rb +3 -11
  14. data/lib/grape/dsl/logger.rb +20 -0
  15. data/lib/grape/dsl/parameters.rb +12 -10
  16. data/lib/grape/dsl/request_response.rb +17 -4
  17. data/lib/grape/dsl/routing.rb +24 -7
  18. data/lib/grape/dsl/settings.rb +8 -2
  19. data/lib/grape/endpoint.rb +30 -26
  20. data/lib/grape/error_formatter.rb +31 -0
  21. data/lib/grape/error_formatter/base.rb +0 -28
  22. data/lib/grape/error_formatter/json.rb +13 -2
  23. data/lib/grape/error_formatter/txt.rb +3 -1
  24. data/lib/grape/error_formatter/xml.rb +3 -1
  25. data/lib/grape/exceptions/base.rb +11 -4
  26. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  27. data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
  28. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  29. data/lib/grape/exceptions/invalid_message_body.rb +1 -1
  30. data/lib/grape/exceptions/invalid_version_header.rb +1 -1
  31. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  32. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
  33. data/lib/grape/exceptions/method_not_allowed.rb +10 -0
  34. data/lib/grape/exceptions/missing_group_type.rb +1 -1
  35. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  36. data/lib/grape/exceptions/missing_option.rb +1 -1
  37. data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
  38. data/lib/grape/exceptions/unknown_options.rb +1 -1
  39. data/lib/grape/exceptions/unknown_parameter.rb +1 -1
  40. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  41. data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
  42. data/lib/grape/exceptions/validation.rb +2 -1
  43. data/lib/grape/formatter.rb +31 -0
  44. data/lib/grape/middleware/base.rb +28 -2
  45. data/lib/grape/middleware/error.rb +24 -1
  46. data/lib/grape/middleware/formatter.rb +4 -3
  47. data/lib/grape/middleware/versioner/param.rb +13 -2
  48. data/lib/grape/parser.rb +29 -0
  49. data/lib/grape/util/sendfile_response.rb +19 -0
  50. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  51. data/lib/grape/validations/params_scope.rb +39 -9
  52. data/lib/grape/validations/types.rb +16 -0
  53. data/lib/grape/validations/validators/all_or_none.rb +1 -1
  54. data/lib/grape/validations/validators/allow_blank.rb +2 -2
  55. data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
  56. data/lib/grape/validations/validators/base.rb +26 -0
  57. data/lib/grape/validations/validators/coerce.rb +16 -14
  58. data/lib/grape/validations/validators/default.rb +1 -1
  59. data/lib/grape/validations/validators/exactly_one_of.rb +10 -1
  60. data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
  61. data/lib/grape/validations/validators/presence.rb +1 -1
  62. data/lib/grape/validations/validators/regexp.rb +2 -2
  63. data/lib/grape/validations/validators/values.rb +2 -2
  64. data/lib/grape/version.rb +1 -1
  65. data/spec/grape/api/custom_validations_spec.rb +156 -21
  66. data/spec/grape/api/namespace_parameters_in_route_spec.rb +38 -0
  67. data/spec/grape/api/optional_parameters_in_route_spec.rb +43 -0
  68. data/spec/grape/api/required_parameters_in_route_spec.rb +37 -0
  69. data/spec/grape/api_spec.rb +118 -60
  70. data/spec/grape/dsl/configuration_spec.rb +0 -75
  71. data/spec/grape/dsl/desc_spec.rb +77 -0
  72. data/spec/grape/dsl/headers_spec.rb +32 -0
  73. data/spec/grape/dsl/inside_route_spec.rb +0 -18
  74. data/spec/grape/dsl/logger_spec.rb +26 -0
  75. data/spec/grape/dsl/parameters_spec.rb +13 -7
  76. data/spec/grape/dsl/request_response_spec.rb +17 -3
  77. data/spec/grape/dsl/routing_spec.rb +8 -1
  78. data/spec/grape/dsl/settings_spec.rb +42 -0
  79. data/spec/grape/endpoint_spec.rb +60 -9
  80. data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
  81. data/spec/grape/exceptions/validation_spec.rb +7 -0
  82. data/spec/grape/integration/rack_sendfile_spec.rb +44 -0
  83. data/spec/grape/middleware/base_spec.rb +100 -0
  84. data/spec/grape/middleware/exception_spec.rb +1 -2
  85. data/spec/grape/middleware/formatter_spec.rb +12 -2
  86. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +1 -1
  87. data/spec/grape/middleware/versioner/header_spec.rb +11 -1
  88. data/spec/grape/middleware/versioner/param_spec.rb +105 -1
  89. data/spec/grape/validations/params_scope_spec.rb +77 -0
  90. data/spec/grape/validations/validators/allow_blank_spec.rb +277 -0
  91. data/spec/grape/validations/validators/coerce_spec.rb +91 -0
  92. data/spec/grape/validations/validators/default_spec.rb +6 -0
  93. data/spec/grape/validations/validators/presence_spec.rb +27 -0
  94. data/spec/grape/validations/validators/regexp_spec.rb +36 -0
  95. data/spec/grape/validations/validators/values_spec.rb +44 -0
  96. data/spec/grape/validations_spec.rb +149 -4
  97. data/spec/spec_helper.rb +1 -0
  98. metadata +26 -5
  99. data/lib/grape/formatter/base.rb +0 -31
  100. data/lib/grape/parser/base.rb +0 -29
  101. data/pkg/grape-0.13.0.gem +0 -0
@@ -10,19 +10,19 @@ describe Grape::Endpoint do
10
10
  describe '.before_each' do
11
11
  after { Grape::Endpoint.before_each(nil) }
12
12
 
13
- it 'should be settable via block' do
13
+ it 'is settable via block' do
14
14
  block = ->(_endpoint) { 'noop' }
15
15
  Grape::Endpoint.before_each(&block)
16
16
  expect(Grape::Endpoint.before_each.first).to eq(block)
17
17
  end
18
18
 
19
- it 'should be settable via reference' do
19
+ it 'is settable via reference' do
20
20
  block = ->(_endpoint) { 'noop' }
21
21
  Grape::Endpoint.before_each block
22
22
  expect(Grape::Endpoint.before_each.first).to eq(block)
23
23
  end
24
24
 
25
- it 'should be able to override a helper' do
25
+ it 'is able to override a helper' do
26
26
  subject.get('/') { current_user }
27
27
  expect { get '/' }.to raise_error(NameError)
28
28
 
@@ -37,7 +37,7 @@ describe Grape::Endpoint do
37
37
  expect { get '/' }.to raise_error(NameError)
38
38
  end
39
39
 
40
- it 'should be able to stack helper' do
40
+ it 'is able to stack helper' do
41
41
  subject.get('/') do
42
42
  authenticate_user!
43
43
  current_user
@@ -887,13 +887,64 @@ describe Grape::Endpoint do
887
887
  expect(last_response.body).to eq('body')
888
888
  end
889
889
  end
890
+
891
+ it 'allows adding to response with present' do
892
+ subject.format :json
893
+ subject.before { present :before, 'before' }
894
+ subject.before_validation { present :before_validation, 'before_validation' }
895
+ subject.after_validation { present :after_validation, 'after_validation' }
896
+ subject.after { present :after, 'after' }
897
+ subject.get :all_filters do
898
+ present :endpoint, 'endpoint'
899
+ end
900
+
901
+ get '/all_filters'
902
+ json = JSON.parse(last_response.body)
903
+ expect(json.keys).to match_array %w(before before_validation after_validation endpoint after)
904
+ end
905
+
906
+ context 'when terminating the response with error!' do
907
+ it 'breaks normal call chain' do
908
+ called = []
909
+ subject.before { called << 'before' }
910
+ subject.before_validation { called << 'before_validation' }
911
+ subject.after_validation { error! :oops, 500 }
912
+ subject.after { called << 'after' }
913
+ subject.get :error_filters do
914
+ called << 'endpoint'
915
+ ''
916
+ end
917
+
918
+ get '/error_filters'
919
+ expect(last_response.status).to eql 500
920
+ expect(called).to match_array %w(before before_validation)
921
+ end
922
+
923
+ it 'allows prior and parent filters of same type to run' do
924
+ called = []
925
+ subject.before { called << 'parent' }
926
+ subject.namespace :parent do
927
+ before { called << 'prior' }
928
+ before { error! :oops, 500 }
929
+ before { called << 'subsequent' }
930
+ get :hello do
931
+ called << :endpoint
932
+ 'Hello!'
933
+ end
934
+ end
935
+
936
+ get '/parent/hello'
937
+ expect(last_response.status).to eql 500
938
+ expect(called).to match_array %w(parent prior)
939
+ end
940
+ end
890
941
  end
891
942
 
892
943
  context 'anchoring' do
893
944
  verbs = %w(post get head delete put options patch)
894
945
 
895
946
  verbs.each do |verb|
896
- it 'allows for the anchoring option with a #{verb.upcase} method' do
947
+ it "allows for the anchoring option with a #{verb.upcase} method" do
897
948
  subject.send(verb, '/example', anchor: true) do
898
949
  verb
899
950
  end
@@ -901,7 +952,7 @@ describe Grape::Endpoint do
901
952
  expect(last_response.status).to eql 404
902
953
  end
903
954
 
904
- it 'anchors paths by default for the #{verb.upcase} method' do
955
+ it "anchors paths by default for the #{verb.upcase} method" do
905
956
  subject.send(verb, '/example') do
906
957
  verb
907
958
  end
@@ -909,7 +960,7 @@ describe Grape::Endpoint do
909
960
  expect(last_response.status).to eql 404
910
961
  end
911
962
 
912
- it 'responds to /example/and/some/more for the non-anchored #{verb.upcase} method' do
963
+ it "responds to /example/and/some/more for the non-anchored #{verb.upcase} method" do
913
964
  subject.send(verb, '/example', anchor: false) do
914
965
  verb
915
966
  end
@@ -921,7 +972,7 @@ describe Grape::Endpoint do
921
972
  end
922
973
 
923
974
  context 'request' do
924
- it 'should be set to the url requested' do
975
+ it 'is set to the url requested' do
925
976
  subject.get('/url') do
926
977
  request.url
927
978
  end
@@ -929,7 +980,7 @@ describe Grape::Endpoint do
929
980
  expect(last_response.body).to eq('http://example.org/url')
930
981
  end
931
982
  ['v1', :v1].each do |version|
932
- it 'should include version #{version}' do
983
+ it "should include version #{version}" do
933
984
  subject.version version, using: :path
934
985
  subject.get('/url') do
935
986
  request.url
@@ -35,8 +35,8 @@ describe Grape::Exceptions::ValidationErrors do
35
35
 
36
36
  describe '#full_messages' do
37
37
  context 'with errors' do
38
- let(:validation_error_1) { Grape::Exceptions::Validation.new(params: ['id'], message_key: 'presence') }
39
- let(:validation_error_2) { Grape::Exceptions::Validation.new(params: ['name'], message_key: 'presence') }
38
+ let(:validation_error_1) { Grape::Exceptions::Validation.new(params: ['id'], message: :presence) }
39
+ let(:validation_error_2) { Grape::Exceptions::Validation.new(params: ['name'], message: :presence) }
40
40
  subject { described_class.new(errors: [validation_error_1, validation_error_2]).full_messages }
41
41
 
42
42
  it 'returns an array with each errors full message' do
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Exceptions::Validation do
4
+ it 'fails when params are missing' do
5
+ expect { Grape::Exceptions::Validation.new(message: 'presence') }.to raise_error(RuntimeError, 'Params are missing:')
6
+ end
7
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::Sendfile do
4
+ subject do
5
+ send_file = file_streamer
6
+ app = Class.new(Grape::API) do
7
+ use Rack::Sendfile
8
+ format :json
9
+ get do
10
+ file send_file
11
+ end
12
+ end
13
+
14
+ options = {
15
+ method: 'GET',
16
+ 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect',
17
+ 'HTTP_X_ACCEL_MAPPING' => '/accel/mapping/=/replaced/'
18
+ }
19
+ env = Rack::MockRequest.env_for('/', options)
20
+ app.call(env)
21
+ end
22
+
23
+ context do
24
+ let(:file_streamer) do
25
+ double(:file_streamer, to_path: '/accel/mapping/some/path')
26
+ end
27
+
28
+ it 'contains Sendfile headers' do
29
+ headers = subject[1]
30
+ expect(headers).to include('X-Accel-Redirect')
31
+ end
32
+ end
33
+
34
+ context do
35
+ let(:file_streamer) do
36
+ double(:file_streamer)
37
+ end
38
+
39
+ it 'not contains Sendfile headers' do
40
+ headers = subject[1]
41
+ expect(headers).to_not include('X-Accel-Redirect')
42
+ end
43
+ end
44
+ end
@@ -29,6 +29,44 @@ describe Grape::Middleware::Base do
29
29
  after { subject.call!({}) }
30
30
  end
31
31
 
32
+ context 'callbacks on error' do
33
+ let(:blank_app) { ->(_) { fail StandardError } }
34
+
35
+ it 'calls #after' do
36
+ expect(subject).to receive(:after)
37
+ expect { subject.call({}) }.to raise_error(StandardError)
38
+ end
39
+ end
40
+
41
+ context 'after callback' do
42
+ before do
43
+ allow(subject).to receive(:after).and_return([200, {}, 'Hello from after callback'])
44
+ end
45
+
46
+ it 'overwrites application response' do
47
+ expect(subject.call!({}).last).to eq('Hello from after callback')
48
+ end
49
+ end
50
+
51
+ context 'after callback with errors' do
52
+ it 'does not overwrite the application response' do
53
+ expect(subject.call({})).to eq([200, {}, 'Hi there.'])
54
+ end
55
+
56
+ context 'with patched warnings' do
57
+ before do
58
+ @warnings = warnings = []
59
+ allow_any_instance_of(Grape::Middleware::Base).to receive(:warn) { |m| warnings << m }
60
+ allow(subject).to receive(:after).and_raise(StandardError)
61
+ end
62
+
63
+ it 'does show a warning' do
64
+ expect { subject.call({}) }.to raise_error(StandardError)
65
+ expect(@warnings).not_to be_empty
66
+ end
67
+ end
68
+ end
69
+
32
70
  it 'is able to access the response' do
33
71
  subject.call({})
34
72
  expect(subject.response).to be_kind_of(Rack::Response)
@@ -99,4 +137,66 @@ describe Grape::Middleware::Base do
99
137
  end
100
138
  end
101
139
  end
140
+
141
+ context 'header' do
142
+ module HeaderSpec
143
+ class ExampleWare < Grape::Middleware::Base
144
+ def before
145
+ header 'X-Test-Before', 'Hi'
146
+ end
147
+
148
+ def after
149
+ header 'X-Test-After', 'Bye'
150
+ nil
151
+ end
152
+ end
153
+ end
154
+
155
+ def app
156
+ Rack::Builder.app do
157
+ use HeaderSpec::ExampleWare
158
+ run ->(_) { [200, {}, ['Yeah']] }
159
+ end
160
+ end
161
+
162
+ it 'is able to set a header' do
163
+ get '/'
164
+ expect(last_response.headers['X-Test-Before']).to eq('Hi')
165
+ expect(last_response.headers['X-Test-After']).to eq('Bye')
166
+ end
167
+ end
168
+
169
+ context 'header overwrite' do
170
+ module HeaderOverwritingSpec
171
+ class ExampleWare < Grape::Middleware::Base
172
+ def before
173
+ header 'X-Test-Overwriting', 'Hi'
174
+ end
175
+
176
+ def after
177
+ header 'X-Test-Overwriting', 'Bye'
178
+ nil
179
+ end
180
+ end
181
+
182
+ class API < Grape::API
183
+ get('/') do
184
+ header 'X-Test-Overwriting', 'Yeah'
185
+ 'Hello'
186
+ end
187
+ end
188
+ end
189
+
190
+ def app
191
+ Rack::Builder.app do
192
+ use HeaderOverwritingSpec::ExampleWare
193
+ run HeaderOverwritingSpec::API.new
194
+ end
195
+ end
196
+
197
+ it 'overwrites header by after headers' do
198
+ get '/'
199
+ expect(last_response.headers['X-Test-Overwriting']).to eq('Bye')
200
+ end
201
+ end
102
202
  end
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'active_support/core_ext/hash'
3
2
 
4
3
  describe Grape::Middleware::Error do
5
4
  # raises a text exception
@@ -59,7 +58,7 @@ describe Grape::Middleware::Error do
59
58
  use Grape::Middleware::Error
60
59
  run ExceptionSpec::ExceptionApp
61
60
  end
62
- expect { get '/' }.to raise_error
61
+ expect { get '/' }.to raise_error(RuntimeError, 'rain!')
63
62
  end
64
63
 
65
64
  context 'with rescue_all set to true' do
@@ -50,7 +50,7 @@ describe Grape::Middleware::Formatter do
50
50
  context 'error handling' do
51
51
  let(:formatter) { double(:formatter) }
52
52
  before do
53
- allow(Grape::Formatter::Base).to receive(:formatter_for) { formatter }
53
+ allow(Grape::Formatter).to receive(:formatter_for) { formatter }
54
54
  end
55
55
 
56
56
  it 'rescues formatter-specific exceptions' do
@@ -66,7 +66,7 @@ describe Grape::Middleware::Formatter do
66
66
 
67
67
  expect do
68
68
  catch(:error) { subject.call('PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json') }
69
- end.to raise_error
69
+ end.to raise_error(StandardError)
70
70
  end
71
71
  end
72
72
 
@@ -282,4 +282,14 @@ describe Grape::Middleware::Formatter do
282
282
  end
283
283
  end
284
284
  end
285
+
286
+ context 'send file' do
287
+ let(:app) { ->(_env) { [200, {}, @body] } }
288
+
289
+ it 'returns Grape::Uril::SendFileReponse' do
290
+ @body = Grape::Util::FileResponse.new('file')
291
+ env = { 'PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json' }
292
+ expect(subject.call(env)).to be_a(Grape::Util::SendfileResponse)
293
+ end
294
+ end
285
295
  end
@@ -85,7 +85,7 @@ describe Grape::Middleware::Versioner::AcceptVersionHeader do
85
85
  end
86
86
  end
87
87
 
88
- context 'when :strict and :cascade=>false are set' do
88
+ context 'when :strict and cascade: false' do
89
89
  before do
90
90
  @options[:versions] = ['v1']
91
91
  @options[:version_options][:strict] = true
@@ -189,7 +189,7 @@ describe Grape::Middleware::Versioner::Header do
189
189
  end
190
190
  end
191
191
 
192
- context 'when :strict and :cascade=>false are set' do
192
+ context 'when :strict and cascade: false' do
193
193
  before do
194
194
  @options[:versions] = ['v1']
195
195
  @options[:version_options][:strict] = true
@@ -224,6 +224,16 @@ describe Grape::Middleware::Versioner::Header do
224
224
  end
225
225
  end
226
226
 
227
+ it 'fails with 406 Not Acceptable if header contains a single invalid accept' do
228
+ expect { subject.call('HTTP_ACCEPT' => 'application/json;application/vnd.vendor-v1+json').first }
229
+ .to raise_exception do |exception|
230
+ expect(exception).to be_a(Grape::Exceptions::InvalidAcceptHeader)
231
+ expect(exception.headers).to eql({})
232
+ expect(exception.status).to eql 406
233
+ expect(exception.message).to include('API vendor or version not found.')
234
+ end
235
+ end
236
+
227
237
  it 'succeeds if proper header is set' do
228
238
  expect(subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json').first).to eq(200)
229
239
  end
@@ -22,7 +22,7 @@ describe Grape::Middleware::Versioner::Param do
22
22
  end
23
23
 
24
24
  context 'with specified parameter name' do
25
- before { @options = { parameter: 'v' } }
25
+ before { @options = { version_options: { parameter: 'v' } } }
26
26
  it 'sets the API version based on the custom parameter name' do
27
27
  env = Rack::MockRequest.env_for('/awesome', params: { 'v' => 'v1' })
28
28
  expect(subject.call(env)[1]['api.version']).to eq('v1')
@@ -53,4 +53,108 @@ describe Grape::Middleware::Versioner::Param do
53
53
  env = Rack::MockRequest.env_for('/awesome', params: {})
54
54
  expect(subject.call(env).first).to eq(200)
55
55
  end
56
+
57
+ context 'when there are multiple versions without a custom param' do
58
+ subject { Class.new(Grape::API) }
59
+
60
+ let(:v1_app) do
61
+ Class.new(Grape::API) do
62
+ version 'v1', using: :param
63
+ content_type :v1_test, 'application/vnd.test.a-cool_resource-v1+json'
64
+ formatter :v1_test, ->(object, _) { object }
65
+ format :v1_test
66
+
67
+ resources :users do
68
+ get :hello do
69
+ 'one'
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ let(:v2_app) do
76
+ Class.new(Grape::API) do
77
+ version 'v2', using: :param
78
+ content_type :v2_test, 'application/vnd.test.a-cool_resource-v2+json'
79
+ formatter :v2_test, ->(object, _) { object }
80
+ format :v2_test
81
+
82
+ resources :users do
83
+ get :hello do
84
+ 'two'
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def app
91
+ subject.mount v2_app
92
+ subject.mount v1_app
93
+ subject
94
+ end
95
+
96
+ it 'responds correctly to a v1 request' do
97
+ versioned_get '/users/hello', 'v1', using: :param, parameter: :apiver
98
+ expect(last_response.body).to eq('one')
99
+ expect(last_response.body).not_to include('API vendor or version not found')
100
+ end
101
+
102
+ it 'responds correctly to a v2 request' do
103
+ versioned_get '/users/hello', 'v2', using: :param, parameter: :apiver
104
+ expect(last_response.body).to eq('two')
105
+ expect(last_response.body).not_to include('API vendor or version not found')
106
+ end
107
+ end
108
+
109
+ context 'when there are multiple versions with a custom param' do
110
+ subject { Class.new(Grape::API) }
111
+
112
+ let(:v1_app) do
113
+ Class.new(Grape::API) do
114
+ version 'v1', using: :param, parameter: 'v'
115
+ content_type :v1_test, 'application/vnd.test.a-cool_resource-v1+json'
116
+ formatter :v1_test, ->(object, _) { object }
117
+ format :v1_test
118
+
119
+ resources :users do
120
+ get :hello do
121
+ 'one'
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ let(:v2_app) do
128
+ Class.new(Grape::API) do
129
+ version 'v2', using: :param, parameter: 'v'
130
+ content_type :v2_test, 'application/vnd.test.a-cool_resource-v2+json'
131
+ formatter :v2_test, ->(object, _) { object }
132
+ format :v2_test
133
+
134
+ resources :users do
135
+ get :hello do
136
+ 'two'
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ def app
143
+ subject.mount v2_app
144
+ subject.mount v1_app
145
+ subject
146
+ end
147
+
148
+ it 'responds correctly to a v1 request' do
149
+ versioned_get '/users/hello', 'v1', using: :param, parameter: 'v'
150
+ expect(last_response.body).to eq('one')
151
+ expect(last_response.body).not_to include('API vendor or version not found')
152
+ end
153
+
154
+ it 'responds correctly to a v2 request' do
155
+ versioned_get '/users/hello', 'v2', using: :param, parameter: 'v'
156
+ expect(last_response.body).to eq('two')
157
+ expect(last_response.body).not_to include('API vendor or version not found')
158
+ end
159
+ end
56
160
  end