grape 0.3.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +15 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +70 -0
- data/.travis.yml +7 -6
- data/CHANGELOG.md +134 -4
- data/CONTRIBUTING.md +118 -0
- data/Gemfile +5 -2
- data/README.md +551 -116
- data/RELEASING.md +105 -0
- data/Rakefile +29 -8
- data/UPGRADING.md +124 -0
- data/grape.gemspec +3 -3
- data/lib/grape/api.rb +207 -88
- data/lib/grape/cookies.rb +4 -8
- data/lib/grape/endpoint.rb +198 -144
- data/lib/grape/error_formatter/base.rb +5 -7
- data/lib/grape/error_formatter/json.rb +3 -5
- data/lib/grape/error_formatter/txt.rb +1 -3
- data/lib/grape/error_formatter/xml.rb +4 -6
- data/lib/grape/exceptions/base.rb +9 -9
- data/lib/grape/exceptions/incompatible_option_values.rb +10 -0
- data/lib/grape/exceptions/invalid_formatter.rb +1 -4
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -5
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -6
- data/lib/grape/exceptions/missing_mime_type.rb +1 -5
- data/lib/grape/exceptions/missing_option.rb +1 -4
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -4
- data/lib/grape/exceptions/unknown_options.rb +1 -5
- data/lib/grape/exceptions/unknown_validator.rb +1 -3
- data/lib/grape/exceptions/validation.rb +13 -3
- data/lib/grape/exceptions/validation_errors.rb +43 -0
- data/lib/grape/formatter/base.rb +5 -7
- data/lib/grape/formatter/json.rb +0 -3
- data/lib/grape/formatter/serializable_hash.rb +15 -15
- data/lib/grape/formatter/txt.rb +0 -2
- data/lib/grape/formatter/xml.rb +0 -2
- data/lib/grape/http/request.rb +26 -0
- data/lib/grape/locale/en.yml +8 -5
- data/lib/grape/middleware/auth/base.rb +30 -0
- data/lib/grape/middleware/auth/basic.rb +3 -20
- data/lib/grape/middleware/auth/digest.rb +2 -19
- data/lib/grape/middleware/auth/oauth2.rb +31 -24
- data/lib/grape/middleware/base.rb +7 -7
- data/lib/grape/middleware/error.rb +36 -22
- data/lib/grape/middleware/filter.rb +3 -3
- data/lib/grape/middleware/formatter.rb +99 -61
- data/lib/grape/middleware/globals.rb +13 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
- data/lib/grape/middleware/versioner/header.rb +22 -16
- data/lib/grape/middleware/versioner/param.rb +9 -11
- data/lib/grape/middleware/versioner/path.rb +10 -13
- data/lib/grape/middleware/versioner.rb +3 -1
- data/lib/grape/namespace.rb +23 -0
- data/lib/grape/parser/base.rb +3 -5
- data/lib/grape/parser/json.rb +0 -2
- data/lib/grape/parser/xml.rb +0 -2
- data/lib/grape/path.rb +70 -0
- data/lib/grape/route.rb +10 -6
- data/lib/grape/util/content_types.rb +2 -1
- data/lib/grape/util/deep_merge.rb +5 -5
- data/lib/grape/util/hash_stack.rb +13 -2
- data/lib/grape/validations/coerce.rb +11 -10
- data/lib/grape/validations/default.rb +25 -0
- data/lib/grape/validations/presence.rb +7 -3
- data/lib/grape/validations/regexp.rb +2 -5
- data/lib/grape/validations/values.rb +17 -0
- data/lib/grape/validations.rb +161 -54
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +19 -4
- data/spec/grape/api_spec.rb +897 -268
- data/spec/grape/endpoint_spec.rb +283 -66
- data/spec/grape/entity_spec.rb +132 -29
- data/spec/grape/exceptions/missing_mime_type_spec.rb +3 -9
- data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
- data/spec/grape/middleware/auth/basic_spec.rb +8 -8
- data/spec/grape/middleware/auth/digest_spec.rb +5 -5
- data/spec/grape/middleware/auth/oauth2_spec.rb +81 -36
- data/spec/grape/middleware/base_spec.rb +8 -13
- data/spec/grape/middleware/error_spec.rb +13 -17
- data/spec/grape/middleware/exception_spec.rb +47 -27
- data/spec/grape/middleware/formatter_spec.rb +103 -41
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
- data/spec/grape/middleware/versioner/header_spec.rb +76 -51
- data/spec/grape/middleware/versioner/param_spec.rb +18 -18
- data/spec/grape/middleware/versioner/path_spec.rb +6 -6
- data/spec/grape/middleware/versioner_spec.rb +5 -2
- data/spec/grape/path_spec.rb +229 -0
- data/spec/grape/util/hash_stack_spec.rb +31 -32
- data/spec/grape/validations/coerce_spec.rb +116 -51
- data/spec/grape/validations/default_spec.rb +123 -0
- data/spec/grape/validations/presence_spec.rb +42 -44
- data/spec/grape/validations/regexp_spec.rb +9 -9
- data/spec/grape/validations/values_spec.rb +138 -0
- data/spec/grape/validations/zh-CN.yml +4 -3
- data/spec/grape/validations_spec.rb +681 -48
- data/spec/shared/versioning_examples.rb +22 -6
- data/spec/spec_helper.rb +3 -2
- data/spec/support/basic_auth_encode_helpers.rb +0 -1
- data/spec/support/content_type_helpers.rb +11 -0
- data/spec/support/versioned_helpers.rb +13 -5
- metadata +34 -84
data/spec/grape/endpoint_spec.rb
CHANGED
@@ -2,22 +2,25 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Grape::Endpoint do
|
4
4
|
subject { Class.new(Grape::API) }
|
5
|
-
|
5
|
+
|
6
|
+
def app
|
7
|
+
subject
|
8
|
+
end
|
6
9
|
|
7
10
|
describe '#initialize' do
|
8
11
|
it 'takes a settings stack, options, and a block' do
|
9
|
-
p =
|
12
|
+
p = proc {}
|
10
13
|
expect {
|
11
14
|
Grape::Endpoint.new(Grape::Util::HashStack.new, {
|
12
|
-
:
|
13
|
-
:
|
15
|
+
path: '/',
|
16
|
+
method: :get
|
14
17
|
}, &p)
|
15
18
|
}.not_to raise_error
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
19
22
|
it 'sets itself in the env upon call' do
|
20
|
-
subject.get('/'){ "Hello world." }
|
23
|
+
subject.get('/') { "Hello world." }
|
21
24
|
get '/'
|
22
25
|
last_request.env['api.endpoint'].should be_kind_of(Grape::Endpoint)
|
23
26
|
end
|
@@ -61,9 +64,15 @@ describe Grape::Endpoint do
|
|
61
64
|
}
|
62
65
|
end
|
63
66
|
it 'includes additional request headers' do
|
64
|
-
get '/headers', nil,
|
67
|
+
get '/headers', nil, "HTTP_X_GRAPE_CLIENT" => "1"
|
65
68
|
JSON.parse(last_response.body)["X-Grape-Client"].should == "1"
|
66
69
|
end
|
70
|
+
it 'includes headers passed as symbols' do
|
71
|
+
env = Rack::MockRequest.env_for("/headers")
|
72
|
+
env["HTTP_SYMBOL_HEADER".to_sym] = "Goliath passes symbols"
|
73
|
+
body = subject.call(env)[2].body.first
|
74
|
+
JSON.parse(body)["Symbol-Header"].should == "Goliath passes symbols"
|
75
|
+
end
|
67
76
|
end
|
68
77
|
|
69
78
|
describe '#cookies' do
|
@@ -71,10 +80,10 @@ describe Grape::Endpoint do
|
|
71
80
|
subject.get('/get/cookies') do
|
72
81
|
cookies['my-awesome-cookie1'] = 'is cool'
|
73
82
|
cookies['my-awesome-cookie2'] = {
|
74
|
-
:
|
75
|
-
:
|
76
|
-
:
|
77
|
-
:
|
83
|
+
value: 'is cool too',
|
84
|
+
domain: 'my.example.com',
|
85
|
+
path: '/',
|
86
|
+
secure: true,
|
78
87
|
}
|
79
88
|
cookies[:cookie3] = 'symbol'
|
80
89
|
cookies['cookie4'] = 'secret code here'
|
@@ -87,7 +96,7 @@ describe Grape::Endpoint do
|
|
87
96
|
"cookie4=secret+code+here",
|
88
97
|
"my-awesome-cookie1=is+cool",
|
89
98
|
"my-awesome-cookie2=is+cool+too; domain=my.example.com; path=/; secure"
|
90
|
-
|
99
|
+
]
|
91
100
|
end
|
92
101
|
|
93
102
|
it 'sets browser cookies and does not set response cookies' do
|
@@ -121,18 +130,18 @@ describe Grape::Endpoint do
|
|
121
130
|
end
|
122
131
|
sum
|
123
132
|
end
|
124
|
-
get
|
133
|
+
get '/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2'
|
125
134
|
last_response.body.should == '3'
|
126
135
|
cookies = Hash[last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
|
127
136
|
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
128
|
-
[
|
137
|
+
[cookie.name, cookie]
|
129
138
|
end]
|
130
139
|
cookies.size.should == 2
|
131
|
-
[
|
140
|
+
["and_this", "delete_this_cookie"].each do |cookie_name|
|
132
141
|
cookie = cookies[cookie_name]
|
133
142
|
cookie.should_not be_nil
|
134
143
|
cookie.value.should == "deleted"
|
135
|
-
cookie.expired?.should
|
144
|
+
cookie.expired?.should be true
|
136
145
|
end
|
137
146
|
end
|
138
147
|
|
@@ -141,7 +150,7 @@ describe Grape::Endpoint do
|
|
141
150
|
sum = 0
|
142
151
|
cookies.each do |name, val|
|
143
152
|
sum += val.to_i
|
144
|
-
cookies.delete name,
|
153
|
+
cookies.delete name, path: '/test'
|
145
154
|
end
|
146
155
|
sum
|
147
156
|
end
|
@@ -149,15 +158,15 @@ describe Grape::Endpoint do
|
|
149
158
|
last_response.body.should == '3'
|
150
159
|
cookies = Hash[last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
|
151
160
|
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
152
|
-
[
|
161
|
+
[cookie.name, cookie]
|
153
162
|
end]
|
154
163
|
cookies.size.should == 2
|
155
|
-
[
|
164
|
+
["and_this", "delete_this_cookie"].each do |cookie_name|
|
156
165
|
cookie = cookies[cookie_name]
|
157
166
|
cookie.should_not be_nil
|
158
167
|
cookie.value.should == "deleted"
|
159
168
|
cookie.path.should == "/test"
|
160
|
-
cookie.expired?.should
|
169
|
+
cookie.expired?.should be true
|
161
170
|
end
|
162
171
|
end
|
163
172
|
end
|
@@ -167,14 +176,16 @@ describe Grape::Endpoint do
|
|
167
176
|
subject.params do
|
168
177
|
requires :first
|
169
178
|
optional :second
|
179
|
+
optional :third, default: 'third-default'
|
180
|
+
optional :nested, type: Hash do
|
181
|
+
optional :fourth
|
182
|
+
end
|
170
183
|
end
|
171
|
-
|
172
|
-
|
173
184
|
end
|
174
185
|
|
175
186
|
it 'has as many keys as there are declared params' do
|
176
187
|
subject.get '/declared' do
|
177
|
-
declared(params).keys.size.should ==
|
188
|
+
declared(params).keys.size.should == 4
|
178
189
|
""
|
179
190
|
end
|
180
191
|
|
@@ -182,6 +193,46 @@ describe Grape::Endpoint do
|
|
182
193
|
last_response.status.should == 200
|
183
194
|
end
|
184
195
|
|
196
|
+
it 'has a optional param with default value all the time' do
|
197
|
+
subject.get '/declared' do
|
198
|
+
params[:third].should == 'third-default'
|
199
|
+
""
|
200
|
+
end
|
201
|
+
|
202
|
+
get '/declared?first=one'
|
203
|
+
last_response.status.should == 200
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'builds nested params' do
|
207
|
+
subject.get '/declared' do
|
208
|
+
declared(params)[:nested].keys.size.should == 1
|
209
|
+
""
|
210
|
+
end
|
211
|
+
|
212
|
+
get '/declared?first=present&nested[fourth]=1'
|
213
|
+
last_response.status.should == 200
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'builds nested params when given array' do
|
217
|
+
subject.get '/dummy' do
|
218
|
+
end
|
219
|
+
subject.params do
|
220
|
+
requires :first
|
221
|
+
optional :second
|
222
|
+
optional :third, default: 'third-default'
|
223
|
+
optional :nested, type: Array do
|
224
|
+
optional :fourth
|
225
|
+
end
|
226
|
+
end
|
227
|
+
subject.get '/declared' do
|
228
|
+
declared(params)[:nested].size.should == 2
|
229
|
+
""
|
230
|
+
end
|
231
|
+
|
232
|
+
get '/declared?first=present&nested[][fourth]=1&nested[][fourth]=2'
|
233
|
+
last_response.status.should == 200
|
234
|
+
end
|
235
|
+
|
185
236
|
it 'filters out any additional params that are given' do
|
186
237
|
subject.get '/declared' do
|
187
238
|
declared(params).key?(:other).should == false
|
@@ -194,7 +245,7 @@ describe Grape::Endpoint do
|
|
194
245
|
|
195
246
|
it 'stringifies if that option is passed' do
|
196
247
|
subject.get '/declared' do
|
197
|
-
declared(params, :
|
248
|
+
declared(params, stringify: true)["first"].should == "one"
|
198
249
|
""
|
199
250
|
end
|
200
251
|
|
@@ -204,7 +255,7 @@ describe Grape::Endpoint do
|
|
204
255
|
|
205
256
|
it 'does not include missing attributes if that option is passed' do
|
206
257
|
subject.get '/declared' do
|
207
|
-
declared(params, :
|
258
|
+
error! 400, "expected nil" if declared(params, include_missing: false)[:second]
|
208
259
|
""
|
209
260
|
end
|
210
261
|
|
@@ -213,6 +264,55 @@ describe Grape::Endpoint do
|
|
213
264
|
end
|
214
265
|
end
|
215
266
|
|
267
|
+
describe '#declared; call from child namespace' do
|
268
|
+
before do
|
269
|
+
subject.format :json
|
270
|
+
subject.namespace :something do
|
271
|
+
params do
|
272
|
+
requires :id, type: Integer
|
273
|
+
end
|
274
|
+
resource ':id' do
|
275
|
+
params do
|
276
|
+
requires :foo
|
277
|
+
optional :bar
|
278
|
+
end
|
279
|
+
get do
|
280
|
+
{
|
281
|
+
params: params,
|
282
|
+
declared_params: declared(params)
|
283
|
+
}
|
284
|
+
end
|
285
|
+
params do
|
286
|
+
requires :happy
|
287
|
+
optional :days
|
288
|
+
end
|
289
|
+
get '/test' do
|
290
|
+
{
|
291
|
+
params: params,
|
292
|
+
declared_params: declared(params, include_parent_namespaces: false)
|
293
|
+
}
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'should include params defined in the parent namespace' do
|
300
|
+
get '/something/123', foo: 'test', extra: 'hello'
|
301
|
+
expect(last_response.status).to eq 200
|
302
|
+
json = JSON.parse(last_response.body, symbolize_names: true)
|
303
|
+
expect(json[:params][:id]).to eq 123
|
304
|
+
expect(json[:declared_params].keys).to match_array [:foo, :bar, :id]
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'does not include params defined in the parent namespace with include_parent_namespaces: false' do
|
308
|
+
get '/something/123/test', happy: 'test', extra: 'hello'
|
309
|
+
expect(last_response.status).to eq 200
|
310
|
+
json = JSON.parse(last_response.body, symbolize_names: true)
|
311
|
+
expect(json[:params][:id]).to eq 123
|
312
|
+
expect(json[:declared_params].keys).to match_array [:happy, :days]
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
216
316
|
describe '#params' do
|
217
317
|
it 'is available to the caller' do
|
218
318
|
subject.get('/hey') do
|
@@ -242,37 +342,64 @@ describe Grape::Endpoint do
|
|
242
342
|
|
243
343
|
context 'with special requirements' do
|
244
344
|
it 'parses email param with provided requirements for params' do
|
245
|
-
subject.get('/:person_email', :
|
246
|
-
|
345
|
+
subject.get('/:person_email', requirements: { person_email: /.*/ }) do
|
346
|
+
params[:person_email]
|
247
347
|
end
|
248
348
|
|
249
|
-
get '/
|
250
|
-
last_response.body.should == '
|
349
|
+
get '/someone@example.com'
|
350
|
+
last_response.body.should == 'someone@example.com'
|
251
351
|
|
252
|
-
get '
|
253
|
-
last_response.body.should == '
|
352
|
+
get 'someone@example.com.pl'
|
353
|
+
last_response.body.should == 'someone@example.com.pl'
|
254
354
|
end
|
255
355
|
|
256
356
|
it 'parses many params with provided regexps' do
|
257
|
-
subject.get('/:person_email/test/:number',
|
258
|
-
:
|
259
|
-
:person_email => /rodzyn@(.*).com/,
|
260
|
-
:number => /[0-9]/ }) do
|
261
|
-
params[:person_email] << params[:number]
|
357
|
+
subject.get('/:person_email/test/:number', requirements: { person_email: /someone@(.*).com/, number: /[0-9]/ }) do
|
358
|
+
params[:person_email] << params[:number]
|
262
359
|
end
|
263
360
|
|
264
|
-
get '/
|
265
|
-
last_response.body.should == '
|
361
|
+
get '/someone@example.com/test/1'
|
362
|
+
last_response.body.should == 'someone@example.com1'
|
266
363
|
|
267
|
-
get '/
|
364
|
+
get '/someone@testing.wrong/test/1'
|
268
365
|
last_response.status.should == 404
|
269
366
|
|
270
|
-
get '
|
367
|
+
get 'someone@test.com/test/wrong_number'
|
271
368
|
last_response.status.should == 404
|
272
369
|
|
273
|
-
get '
|
370
|
+
get 'someone@test.com/wrong_middle/1'
|
274
371
|
last_response.status.should == 404
|
275
372
|
end
|
373
|
+
|
374
|
+
context 'namespace requirements' do
|
375
|
+
before :each do
|
376
|
+
subject.namespace :outer, requirements: { person_email: /abc@(.*).com/ } do
|
377
|
+
get('/:person_email') do
|
378
|
+
params[:person_email]
|
379
|
+
end
|
380
|
+
|
381
|
+
namespace :inner, requirements: { number: /[0-9]/, person_email: /someone@(.*).com/ }do
|
382
|
+
get '/:person_email/test/:number' do
|
383
|
+
params[:person_email] << params[:number]
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
it "parse email param with provided requirements for params" do
|
389
|
+
get '/outer/abc@example.com'
|
390
|
+
last_response.body.should == 'abc@example.com'
|
391
|
+
end
|
392
|
+
|
393
|
+
it "should override outer namespace's requirements" do
|
394
|
+
get '/outer/inner/someone@testing.wrong/test/1'
|
395
|
+
last_response.status.should == 404
|
396
|
+
|
397
|
+
get '/outer/inner/someone@testing.com/test/1'
|
398
|
+
last_response.status.should == 200
|
399
|
+
last_response.body.should == 'someone@testing.com1'
|
400
|
+
end
|
401
|
+
|
402
|
+
end
|
276
403
|
end
|
277
404
|
|
278
405
|
context 'from body parameters' do
|
@@ -286,31 +413,33 @@ describe Grape::Endpoint do
|
|
286
413
|
end
|
287
414
|
|
288
415
|
it 'converts JSON bodies to params' do
|
289
|
-
post '/request_body', MultiJson.dump(:
|
416
|
+
post '/request_body', MultiJson.dump(user: 'Bobby T.'), 'CONTENT_TYPE' => 'application/json'
|
290
417
|
last_response.body.should == 'Bobby T.'
|
291
418
|
end
|
292
419
|
|
293
420
|
it 'does not convert empty JSON bodies to params' do
|
294
|
-
put '/request_body', '',
|
421
|
+
put '/request_body', '', 'CONTENT_TYPE' => 'application/json'
|
295
422
|
last_response.body.should == ''
|
296
423
|
end
|
297
424
|
|
298
425
|
it 'converts XML bodies to params' do
|
299
|
-
post '/request_body', '<user>Bobby T.</user>',
|
426
|
+
post '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
300
427
|
last_response.body.should == 'Bobby T.'
|
301
428
|
end
|
302
429
|
|
303
430
|
it 'converts XML bodies to params' do
|
304
|
-
put '/request_body', '<user>Bobby T.</user>',
|
431
|
+
put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
305
432
|
last_response.body.should == 'Bobby T.'
|
306
433
|
end
|
307
434
|
|
308
435
|
it 'does not include parameters not defined by the body' do
|
309
436
|
subject.post '/omitted_params' do
|
310
|
-
params[:version]
|
311
|
-
params[:user]
|
437
|
+
error! 400, "expected nil" if params[:version]
|
438
|
+
params[:user]
|
312
439
|
end
|
313
|
-
post '/omitted_params', MultiJson.dump(:
|
440
|
+
post '/omitted_params', MultiJson.dump(user: 'Bob'), 'CONTENT_TYPE' => 'application/json'
|
441
|
+
last_response.status.should == 201
|
442
|
+
last_response.body.should == "Bob"
|
314
443
|
end
|
315
444
|
end
|
316
445
|
|
@@ -320,11 +449,70 @@ describe Grape::Endpoint do
|
|
320
449
|
subject.put '/request_body' do
|
321
450
|
params[:user]
|
322
451
|
end
|
323
|
-
put '/request_body', '<user>Bobby T.</user>',
|
452
|
+
put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
324
453
|
last_response.status.should == 406
|
325
454
|
last_response.body.should == '{"error":"The requested content-type \'application/xml\' is not supported."}'
|
326
455
|
end
|
327
456
|
|
457
|
+
context 'content type with params' do
|
458
|
+
before do
|
459
|
+
subject.format :json
|
460
|
+
subject.content_type :json, 'application/json; charset=utf-8'
|
461
|
+
|
462
|
+
subject.post do
|
463
|
+
params[:data]
|
464
|
+
end
|
465
|
+
post '/', MultiJson.dump(data: { some: 'payload' }), 'CONTENT_TYPE' => 'application/json'
|
466
|
+
end
|
467
|
+
|
468
|
+
it "should not response with 406 for same type without params" do
|
469
|
+
last_response.status.should_not be 406
|
470
|
+
end
|
471
|
+
|
472
|
+
it "should response with given content type in headers" do
|
473
|
+
last_response.headers['Content-Type'].should eq 'application/json; charset=utf-8'
|
474
|
+
end
|
475
|
+
|
476
|
+
end
|
477
|
+
|
478
|
+
context 'precedence' do
|
479
|
+
|
480
|
+
before do
|
481
|
+
subject.format :json
|
482
|
+
subject.namespace '/:id' do
|
483
|
+
get do
|
484
|
+
{
|
485
|
+
params: params[:id]
|
486
|
+
}
|
487
|
+
end
|
488
|
+
post do
|
489
|
+
{
|
490
|
+
params: params[:id]
|
491
|
+
}
|
492
|
+
end
|
493
|
+
put do
|
494
|
+
{
|
495
|
+
params: params[:id]
|
496
|
+
}
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
it 'route string params have higher precedence than body params' do
|
502
|
+
post '/123', { id: 456 }.to_json
|
503
|
+
expect(JSON.parse(last_response.body)['params']).to eq '123'
|
504
|
+
put '/123', { id: 456 }.to_json
|
505
|
+
expect(JSON.parse(last_response.body)['params']).to eq '123'
|
506
|
+
end
|
507
|
+
|
508
|
+
it 'route string params have higher precedence than URL params' do
|
509
|
+
get '/123?id=456'
|
510
|
+
expect(JSON.parse(last_response.body)['params']).to eq '123'
|
511
|
+
post '/123?id=456'
|
512
|
+
expect(JSON.parse(last_response.body)['params']).to eq '123'
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
328
516
|
end
|
329
517
|
|
330
518
|
describe '#error!' do
|
@@ -335,7 +523,7 @@ describe Grape::Endpoint do
|
|
335
523
|
end
|
336
524
|
|
337
525
|
get '/hey'
|
338
|
-
last_response.status.should ==
|
526
|
+
last_response.status.should == 500
|
339
527
|
last_response.body.should == "This is not valid."
|
340
528
|
end
|
341
529
|
|
@@ -351,13 +539,23 @@ describe Grape::Endpoint do
|
|
351
539
|
|
352
540
|
it 'accepts an object and render it in format' do
|
353
541
|
subject.get '/hey' do
|
354
|
-
error!({'dude' => 'rad'}, 403)
|
542
|
+
error!({ 'dude' => 'rad' }, 403)
|
355
543
|
end
|
356
544
|
|
357
545
|
get '/hey.json'
|
358
546
|
last_response.status.should == 403
|
359
547
|
last_response.body.should == '{"dude":"rad"}'
|
360
548
|
end
|
549
|
+
|
550
|
+
it 'can specifiy headers' do
|
551
|
+
subject.get '/hey' do
|
552
|
+
error!({ 'dude' => 'rad' }, 403, 'X-Custom' => 'value')
|
553
|
+
end
|
554
|
+
|
555
|
+
get '/hey.json'
|
556
|
+
last_response.status.should == 403
|
557
|
+
last_response.headers['X-Custom'].should == 'value'
|
558
|
+
end
|
361
559
|
end
|
362
560
|
|
363
561
|
describe '#redirect' do
|
@@ -382,7 +580,7 @@ describe Grape::Endpoint do
|
|
382
580
|
|
383
581
|
it 'support permanent redirect' do
|
384
582
|
subject.get('/hey') do
|
385
|
-
redirect "/ha", :
|
583
|
+
redirect "/ha", permanent: true
|
386
584
|
end
|
387
585
|
get '/hey'
|
388
586
|
last_response.status.should eq 301
|
@@ -396,10 +594,10 @@ describe Grape::Endpoint do
|
|
396
594
|
params[:text]
|
397
595
|
end
|
398
596
|
|
399
|
-
post '/new', :
|
597
|
+
post '/new', text: 'abc'
|
400
598
|
last_response.body.should == 'abc'
|
401
599
|
|
402
|
-
post '/new', :
|
600
|
+
post '/new', text: 'def'
|
403
601
|
last_response.body.should == 'def'
|
404
602
|
end
|
405
603
|
|
@@ -433,7 +631,7 @@ describe Grape::Endpoint do
|
|
433
631
|
describe '.generate_api_method' do
|
434
632
|
it 'raises NameError if the method name is already in use' do
|
435
633
|
expect {
|
436
|
-
Grape::Endpoint.generate_api_method("version", &proc{})
|
634
|
+
Grape::Endpoint.generate_api_method("version", &proc {})
|
437
635
|
}.to raise_error(NameError)
|
438
636
|
end
|
439
637
|
it 'raises ArgumentError if a block is not given' do
|
@@ -442,15 +640,15 @@ describe Grape::Endpoint do
|
|
442
640
|
}.to raise_error(ArgumentError)
|
443
641
|
end
|
444
642
|
it 'returns a Proc' do
|
445
|
-
Grape::Endpoint.generate_api_method("GET test for a proc", &proc{}).should be_a Proc
|
643
|
+
Grape::Endpoint.generate_api_method("GET test for a proc", &proc {}).should be_a Proc
|
446
644
|
end
|
447
645
|
end
|
448
646
|
|
449
647
|
context 'filters' do
|
450
648
|
describe 'before filters' do
|
451
649
|
it 'runs the before filter if set' do
|
452
|
-
subject.before{ env['before_test'] = "OK" }
|
453
|
-
subject.get('/before_test'){ env['before_test'] }
|
650
|
+
subject.before { env['before_test'] = "OK" }
|
651
|
+
subject.get('/before_test') { env['before_test'] }
|
454
652
|
|
455
653
|
get '/before_test'
|
456
654
|
last_response.body.should == "OK"
|
@@ -459,15 +657,15 @@ describe Grape::Endpoint do
|
|
459
657
|
|
460
658
|
describe 'after filters' do
|
461
659
|
it 'overrides the response body if it sets it' do
|
462
|
-
subject.after{ body "after" }
|
463
|
-
subject.get('/after_test'){ "during" }
|
660
|
+
subject.after { body "after" }
|
661
|
+
subject.get('/after_test') { "during" }
|
464
662
|
get '/after_test'
|
465
663
|
last_response.body.should == 'after'
|
466
664
|
end
|
467
665
|
|
468
666
|
it 'does not override the response body with its return' do
|
469
|
-
subject.after{ "after" }
|
470
|
-
subject.get('/after_test'){ "body" }
|
667
|
+
subject.after { "after" }
|
668
|
+
subject.get('/after_test') { "body" }
|
471
669
|
get '/after_test'
|
472
670
|
last_response.body.should == "body"
|
473
671
|
end
|
@@ -479,7 +677,7 @@ describe Grape::Endpoint do
|
|
479
677
|
|
480
678
|
verbs.each do |verb|
|
481
679
|
it 'allows for the anchoring option with a #{verb.upcase} method' do
|
482
|
-
subject.send(verb, '/example', :
|
680
|
+
subject.send(verb, '/example', anchor: true) do
|
483
681
|
verb
|
484
682
|
end
|
485
683
|
send(verb, '/example/and/some/more')
|
@@ -495,7 +693,7 @@ describe Grape::Endpoint do
|
|
495
693
|
end
|
496
694
|
|
497
695
|
it 'responds to /example/and/some/more for the non-anchored #{verb.upcase} method' do
|
498
|
-
subject.send(verb, '/example', :
|
696
|
+
subject.send(verb, '/example', anchor: false) do
|
499
697
|
verb
|
500
698
|
end
|
501
699
|
send(verb, '/example/and/some/more')
|
@@ -513,9 +711,9 @@ describe Grape::Endpoint do
|
|
513
711
|
get '/url'
|
514
712
|
last_response.body.should == "http://example.org/url"
|
515
713
|
end
|
516
|
-
[
|
714
|
+
['v1', :v1].each do |version|
|
517
715
|
it 'should include version #{version}' do
|
518
|
-
subject.version version, :
|
716
|
+
subject.version version, using: :path
|
519
717
|
subject.get('/url') do
|
520
718
|
request.url
|
521
719
|
end
|
@@ -524,7 +722,7 @@ describe Grape::Endpoint do
|
|
524
722
|
end
|
525
723
|
end
|
526
724
|
it 'should include prefix' do
|
527
|
-
subject.version 'v1', :
|
725
|
+
subject.version 'v1', using: :path
|
528
726
|
subject.prefix 'api'
|
529
727
|
subject.get('/url') do
|
530
728
|
request.url
|
@@ -534,4 +732,23 @@ describe Grape::Endpoint do
|
|
534
732
|
end
|
535
733
|
end
|
536
734
|
|
735
|
+
context 'version headers' do
|
736
|
+
before do
|
737
|
+
# NOTE: a 404 is returned instead of the 406 if cascade: false is not set.
|
738
|
+
subject.version 'v1', using: :header, vendor: 'ohanapi', cascade: false
|
739
|
+
subject.get '/test' do
|
740
|
+
"Hello!"
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
it 'result in a 406 response if they are invalid' do
|
745
|
+
get '/test', {}, 'HTTP_ACCEPT' => 'application/vnd.ohanapi.v1+json'
|
746
|
+
last_response.status.should == 406
|
747
|
+
end
|
748
|
+
|
749
|
+
it 'result in a 406 response if they cannot be parsed by rack-accept' do
|
750
|
+
get '/test', {}, 'HTTP_ACCEPT' => 'application/vnd.ohanapi.v1+json; version=1'
|
751
|
+
last_response.status.should == 406
|
752
|
+
end
|
753
|
+
end
|
537
754
|
end
|