grape 0.2.3 → 0.2.4

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 (42) hide show
  1. data/.yardopts +2 -0
  2. data/CHANGELOG.markdown +19 -0
  3. data/Gemfile +2 -0
  4. data/README.markdown +166 -131
  5. data/Rakefile +1 -3
  6. data/lib/grape.rb +0 -3
  7. data/lib/grape/api.rb +16 -4
  8. data/lib/grape/cookies.rb +32 -30
  9. data/lib/grape/endpoint.rb +25 -11
  10. data/lib/grape/entity.rb +5 -0
  11. data/lib/grape/error_formatter/base.rb +2 -1
  12. data/lib/grape/middleware/base.rb +9 -2
  13. data/lib/grape/middleware/error.rb +11 -11
  14. data/lib/grape/middleware/formatter.rb +8 -5
  15. data/lib/grape/middleware/versioner/header.rb +1 -3
  16. data/lib/grape/middleware/versioner/path.rb +15 -2
  17. data/lib/grape/util/deep_merge.rb +4 -4
  18. data/lib/grape/validations.rb +2 -3
  19. data/lib/grape/version.rb +1 -1
  20. data/spec/grape/api_spec.rb +316 -175
  21. data/spec/grape/endpoint_spec.rb +159 -57
  22. data/spec/grape/entity_spec.rb +80 -80
  23. data/spec/grape/middleware/auth/basic_spec.rb +3 -3
  24. data/spec/grape/middleware/auth/digest_spec.rb +4 -4
  25. data/spec/grape/middleware/auth/oauth2_spec.rb +4 -4
  26. data/spec/grape/middleware/base_spec.rb +9 -9
  27. data/spec/grape/middleware/error_spec.rb +4 -4
  28. data/spec/grape/middleware/exception_spec.rb +13 -13
  29. data/spec/grape/middleware/formatter_spec.rb +25 -25
  30. data/spec/grape/middleware/versioner/header_spec.rb +23 -23
  31. data/spec/grape/middleware/versioner/param_spec.rb +8 -8
  32. data/spec/grape/middleware/versioner/path_spec.rb +8 -8
  33. data/spec/grape/middleware/versioner_spec.rb +6 -3
  34. data/spec/grape/util/hash_stack_spec.rb +20 -20
  35. data/spec/grape/validations/presence_spec.rb +1 -1
  36. data/spec/grape/validations/regexp_spec.rb +2 -2
  37. data/spec/grape/validations_spec.rb +4 -4
  38. data/spec/shared/versioning_examples.rb +48 -20
  39. metadata +5 -7
  40. data/.document +0 -5
  41. data/lib/grape/middleware/prefixer.rb +0 -21
  42. data/spec/grape/middleware/prefixer_spec.rb +0 -30
@@ -5,22 +5,25 @@ describe Grape::Endpoint do
5
5
  def app; subject end
6
6
 
7
7
  describe '#initialize' do
8
- it 'should take a settings stack, options, and a block' do
9
- expect{ Grape::Endpoint.new(Grape::Util::HashStack.new, {
10
- :path => '/',
11
- :method => :get
12
- }, &Proc.new{}) }.not_to raise_error
8
+ it 'takes a settings stack, options, and a block' do
9
+ p = Proc.new {}
10
+ expect {
11
+ Grape::Endpoint.new(Grape::Util::HashStack.new, {
12
+ :path => '/',
13
+ :method => :get
14
+ }, &p)
15
+ }.not_to raise_error
13
16
  end
14
17
  end
15
18
 
16
- it 'should set itself in the env upon call' do
19
+ it 'sets itself in the env upon call' do
17
20
  subject.get('/'){ "Hello world." }
18
21
  get '/'
19
22
  last_request.env['api.endpoint'].should be_kind_of(Grape::Endpoint)
20
23
  end
21
24
 
22
25
  describe '#status' do
23
- it 'should be callable from within a block' do
26
+ it 'is callable from within a block' do
24
27
  subject.get('/home') do
25
28
  status 206
26
29
  "Hello"
@@ -33,7 +36,7 @@ describe Grape::Endpoint do
33
36
  end
34
37
 
35
38
  describe '#header' do
36
- it 'should be callable from within a block' do
39
+ it 'is callable from within a block' do
37
40
  subject.get('/hey') do
38
41
  header 'X-Awesome', 'true'
39
42
  "Awesome"
@@ -45,7 +48,7 @@ describe Grape::Endpoint do
45
48
  end
46
49
 
47
50
  describe '#cookies' do
48
- it 'should be callable from within a block' do
51
+ it 'is callable from within a block' do
49
52
  subject.get('/get/cookies') do
50
53
  cookies['my-awesome-cookie1'] = 'is cool'
51
54
  cookies['my-awesome-cookie2'] = {
@@ -68,7 +71,7 @@ describe Grape::Endpoint do
68
71
  ]
69
72
  end
70
73
 
71
- it "should set browser cookies and should not set response cookies" do
74
+ it 'sets browser cookies and does not set response cookies' do
72
75
  subject.get('/username') do
73
76
  cookies[:username]
74
77
  end
@@ -79,7 +82,7 @@ describe Grape::Endpoint do
79
82
  last_response.headers['Set-Cookie'].should_not =~ /sandbox=true/
80
83
  end
81
84
 
82
- it "should set and update browser cookies" do
85
+ it 'sets and update browser cookies' do
83
86
  subject.get('/username') do
84
87
  cookies[:sandbox] = true if cookies[:sandbox] == 'false'
85
88
  cookies[:username] += "_test"
@@ -90,7 +93,7 @@ describe Grape::Endpoint do
90
93
  last_response.headers['Set-Cookie'].should =~ /sandbox=true/
91
94
  end
92
95
 
93
- it "should delete cookie" do
96
+ it 'deletes cookie' do
94
97
  subject.get('/test') do
95
98
  sum = 0
96
99
  cookies.each do |name, val|
@@ -106,6 +109,23 @@ describe Grape::Endpoint do
106
109
  "delete_this_cookie=deleted; expires=Thu, 01-Jan-1970 00:00:00 GMT"
107
110
  ]
108
111
  end
112
+
113
+ it 'deletes cookies with path' do
114
+ subject.get('/test') do
115
+ sum = 0
116
+ cookies.each do |name, val|
117
+ sum += val.to_i
118
+ cookies.delete name, { :path => '/test' }
119
+ end
120
+ sum
121
+ end
122
+ get('/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2')
123
+ last_response.body.should == '3'
124
+ last_response.headers['Set-Cookie'].split("\n").sort.should == [
125
+ "and_this=deleted; path=/test; expires=Thu, 01-Jan-1970 00:00:00 GMT",
126
+ "delete_this_cookie=deleted; path=/test; expires=Thu, 01-Jan-1970 00:00:00 GMT"
127
+ ]
128
+ end
109
129
  end
110
130
 
111
131
  describe '#declared' do
@@ -118,8 +138,8 @@ describe Grape::Endpoint do
118
138
 
119
139
  end
120
140
 
121
- it 'should have as many keys as there are declared params' do
122
- subject.get "/declared" do
141
+ it 'has as many keys as there are declared params' do
142
+ subject.get '/declared' do
123
143
  declared(params).keys.size.should == 2
124
144
  ""
125
145
  end
@@ -128,8 +148,8 @@ describe Grape::Endpoint do
128
148
  last_response.status.should == 200
129
149
  end
130
150
 
131
- it 'should filter out any additional params that are given' do
132
- subject.get "/declared" do
151
+ it 'filters out any additional params that are given' do
152
+ subject.get '/declared' do
133
153
  declared(params).key?(:other).should == false
134
154
  ""
135
155
  end
@@ -138,8 +158,8 @@ describe Grape::Endpoint do
138
158
  last_response.status.should == 200
139
159
  end
140
160
 
141
- it 'should stringify if that option is passed' do
142
- subject.get "/declared" do
161
+ it 'stringifies if that option is passed' do
162
+ subject.get '/declared' do
143
163
  declared(params, :stringify => true)["first"].should == "one"
144
164
  ""
145
165
  end
@@ -148,8 +168,8 @@ describe Grape::Endpoint do
148
168
  last_response.status.should == 200
149
169
  end
150
170
 
151
- it 'should not include missing attributes if that option is passed' do
152
- subject.get "/declared" do
171
+ it 'does not include missing attributes if that option is passed' do
172
+ subject.get '/declared' do
153
173
  declared(params, :include_missing => false)[:second].should == nil
154
174
  ""
155
175
  end
@@ -160,7 +180,7 @@ describe Grape::Endpoint do
160
180
  end
161
181
 
162
182
  describe '#params' do
163
- it 'should be available to the caller' do
183
+ it 'is available to the caller' do
164
184
  subject.get('/hey') do
165
185
  params[:howdy]
166
186
  end
@@ -169,7 +189,7 @@ describe Grape::Endpoint do
169
189
  last_response.body.should == 'hey'
170
190
  end
171
191
 
172
- it 'should parse from path segments' do
192
+ it 'parses from path segments' do
173
193
  subject.get('/hey/:id') do
174
194
  params[:id]
175
195
  end
@@ -178,7 +198,7 @@ describe Grape::Endpoint do
178
198
  last_response.body.should == '12'
179
199
  end
180
200
 
181
- it 'should deeply convert nested params' do
201
+ it 'deeply converts nested params' do
182
202
  subject.get '/location' do
183
203
  params[:location][:city]
184
204
  end
@@ -187,7 +207,7 @@ describe Grape::Endpoint do
187
207
  end
188
208
 
189
209
  context 'with special requirements' do
190
- it 'should parse email param with provided requirements for params' do
210
+ it 'parses email param with provided requirements for params' do
191
211
  subject.get('/:person_email', :requirements => { :person_email => /.*/ }) do
192
212
  params[:person_email]
193
213
  end
@@ -199,7 +219,7 @@ describe Grape::Endpoint do
199
219
  last_response.body.should == 'rodzyn@grape.com.pl'
200
220
  end
201
221
 
202
- it 'should parse many params with provided regexps' do
222
+ it 'parses many params with provided regexps' do
203
223
  subject.get('/:person_email/test/:number',
204
224
  :requirements => {
205
225
  :person_email => /rodzyn@(.*).com/,
@@ -232,22 +252,22 @@ describe Grape::Endpoint do
232
252
  end
233
253
  end
234
254
 
235
- it 'should convert JSON bodies to params' do
236
- post '/request_body', MultiJson.encode(:user => 'Bobby T.'), {'CONTENT_TYPE' => 'application/json'}
255
+ it 'converts JSON bodies to params' do
256
+ post '/request_body', MultiJson.dump(:user => 'Bobby T.'), {'CONTENT_TYPE' => 'application/json'}
237
257
  last_response.body.should == 'Bobby T.'
238
258
  end
239
259
 
240
- it 'should not convert empty JSON bodies to params' do
260
+ it 'does not convert empty JSON bodies to params' do
241
261
  put '/request_body', '', {'CONTENT_TYPE' => 'application/json'}
242
262
  last_response.body.should == ''
243
263
  end
244
264
 
245
- it 'should convert XML bodies to params' do
265
+ it 'converts XML bodies to params' do
246
266
  post '/request_body', '<user>Bobby T.</user>', {'CONTENT_TYPE' => 'application/xml'}
247
267
  last_response.body.should == 'Bobby T.'
248
268
  end
249
269
 
250
- it 'should convert XML bodies to params' do
270
+ it 'converts XML bodies to params' do
251
271
  put '/request_body', '<user>Bobby T.</user>', {'CONTENT_TYPE' => 'application/xml'}
252
272
  last_response.body.should == 'Bobby T.'
253
273
  end
@@ -256,13 +276,20 @@ describe Grape::Endpoint do
256
276
  subject.post '/omitted_params' do
257
277
  body_params[:version].should == nil
258
278
  end
259
- post '/omitted_params', MultiJson.encode(:user => 'Blah'), {'CONTENT_TYPE' => 'application/json'}
279
+ post '/omitted_params', MultiJson.dump(:user => 'Blah'), {'CONTENT_TYPE' => 'application/json'}
280
+ end
281
+
282
+ it 'should return an equivalent hash on subsequenst calls' do
283
+ subject.post '/two_times' do
284
+ body_params.should == body_params
285
+ end
286
+ post '/two_times', MultiJson.dump(:user => 'Bobby T.'), {'CONTENT_TYPE' => 'application/json'}
260
287
  end
261
288
  end
262
289
  end
263
290
 
264
291
  describe '#error!' do
265
- it 'should accept a message' do
292
+ it 'accepts a message' do
266
293
  subject.get('/hey') do
267
294
  error! "This is not valid."
268
295
  "This is valid."
@@ -273,7 +300,7 @@ describe Grape::Endpoint do
273
300
  last_response.body.should == "This is not valid."
274
301
  end
275
302
 
276
- it 'should accept a code' do
303
+ it 'accepts a code' do
277
304
  subject.get('/hey') do
278
305
  error! "Unauthorized.", 401
279
306
  end
@@ -283,7 +310,7 @@ describe Grape::Endpoint do
283
310
  last_response.body.should == "Unauthorized."
284
311
  end
285
312
 
286
- it 'should accept an object and render it in format' do
313
+ it 'accepts an object and render it in format' do
287
314
  subject.get '/hey' do
288
315
  error!({'dude' => 'rad'}, 403)
289
316
  end
@@ -294,8 +321,8 @@ describe Grape::Endpoint do
294
321
  end
295
322
  end
296
323
 
297
- describe "#redirect" do
298
- it "should redirect to a url with status 302" do
324
+ describe '#redirect' do
325
+ it 'redirects to a url with status 302' do
299
326
  subject.get('/hey') do
300
327
  redirect "/ha"
301
328
  end
@@ -305,7 +332,7 @@ describe Grape::Endpoint do
305
332
  last_response.body.should eq ""
306
333
  end
307
334
 
308
- it "should have status code 303 if it is not get request and it is http 1.1" do
335
+ it 'has status code 303 if it is not get request and it is http 1.1' do
309
336
  subject.post('/hey') do
310
337
  redirect "/ha"
311
338
  end
@@ -314,7 +341,7 @@ describe Grape::Endpoint do
314
341
  last_response.headers['Location'].should eq "/ha"
315
342
  end
316
343
 
317
- it "support permanent redirect" do
344
+ it 'support permanent redirect' do
318
345
  subject.get('/hey') do
319
346
  redirect "/ha", :permanent => true
320
347
  end
@@ -325,7 +352,7 @@ describe Grape::Endpoint do
325
352
  end
326
353
  end
327
354
 
328
- it 'should not persist params between calls' do
355
+ it 'does not persist params between calls' do
329
356
  subject.post('/new') do
330
357
  params[:text]
331
358
  end
@@ -337,7 +364,7 @@ describe Grape::Endpoint do
337
364
  last_response.body.should == 'def'
338
365
  end
339
366
 
340
- it 'should reset all instance variables (except block) between calls' do
367
+ it 'resets all instance variables (except block) between calls' do
341
368
  subject.helpers do
342
369
  def memoized
343
370
  @memoized ||= params[:howdy]
@@ -354,7 +381,7 @@ describe Grape::Endpoint do
354
381
  last_response.body.should == 'yo'
355
382
  end
356
383
 
357
- it 'should allow explicit return calls' do
384
+ it 'allows explicit return calls' do
358
385
  subject.get('/home') do
359
386
  return "Hello"
360
387
  end
@@ -364,24 +391,24 @@ describe Grape::Endpoint do
364
391
  last_response.body.should == "Hello"
365
392
  end
366
393
 
367
- describe ".generate_api_method" do
368
- it "should raise NameError if the method name is already in use" do
394
+ describe '.generate_api_method' do
395
+ it 'raises NameError if the method name is already in use' do
369
396
  expect {
370
397
  Grape::Endpoint.generate_api_method("version", &proc{})
371
398
  }.to raise_error(NameError)
372
399
  end
373
- it "should raise ArgumentError if a block is not given" do
400
+ it 'raises ArgumentError if a block is not given' do
374
401
  expect {
375
402
  Grape::Endpoint.generate_api_method("GET without a block method")
376
403
  }.to raise_error(ArgumentError)
377
404
  end
378
- it "should return a Proc" do
405
+ it 'returns a Proc' do
379
406
  Grape::Endpoint.generate_api_method("GET test for a proc", &proc{}).should be_a Proc
380
407
  end
381
408
  end
382
409
 
383
410
  describe '#present' do
384
- it 'should just set the object as the body if no options are provided' do
411
+ it 'sets the object as the body if no options are provided' do
385
412
  subject.get '/example' do
386
413
  present({:abc => 'def'})
387
414
  body.should == {:abc => 'def'}
@@ -389,7 +416,7 @@ describe Grape::Endpoint do
389
416
  get '/example'
390
417
  end
391
418
 
392
- it 'should call through to the provided entity class if one is given' do
419
+ it 'calls through to the provided entity class if one is given' do
393
420
  subject.get '/example' do
394
421
  entity_mock = Object.new
395
422
  entity_mock.should_receive(:represent)
@@ -398,7 +425,7 @@ describe Grape::Endpoint do
398
425
  get '/example'
399
426
  end
400
427
 
401
- it 'should pull a representation from the class options if it exists' do
428
+ it 'pulls a representation from the class options if it exists' do
402
429
  entity = Class.new(Grape::Entity)
403
430
  entity.stub!(:represent).and_return("Hiya")
404
431
 
@@ -410,7 +437,7 @@ describe Grape::Endpoint do
410
437
  last_response.body.should == 'Hiya'
411
438
  end
412
439
 
413
- it 'should pull a representation from the class ancestor if it exists' do
440
+ it 'pulls a representation from the class ancestor if it exists' do
414
441
  entity = Class.new(Grape::Entity)
415
442
  entity.stub!(:represent).and_return("Hiya")
416
443
 
@@ -424,7 +451,7 @@ describe Grape::Endpoint do
424
451
  last_response.body.should == 'Hiya'
425
452
  end
426
453
 
427
- it 'should automatically use Klass::Entity if that exists' do
454
+ it 'automatically uses Klass::Entity if that exists' do
428
455
  some_model = Class.new
429
456
  entity = Class.new(Grape::Entity)
430
457
  entity.stub!(:represent).and_return("Auto-detect!")
@@ -438,18 +465,65 @@ describe Grape::Endpoint do
438
465
  last_response.body.should == 'Auto-detect!'
439
466
  end
440
467
 
441
- it 'should add a root key to the output if one is given' do
468
+ it 'adds a root key to the output if one is given' do
442
469
  subject.get '/example' do
443
470
  present({:abc => 'def'}, :root => :root)
444
471
  body.should == {:root => {:abc => 'def'}}
445
472
  end
446
473
  get '/example'
447
474
  end
475
+
476
+ [ :json, :serializable_hash ].each do |format|
477
+
478
+ it 'presents with #{format}' do
479
+ entity = Class.new(Grape::Entity)
480
+ entity.root "examples", "example"
481
+ entity.expose :id
482
+
483
+ subject.format format
484
+ subject.get '/example' do
485
+ c = Class.new do
486
+ attr_reader :id
487
+ def initialize(id)
488
+ @id = id
489
+ end
490
+ end
491
+ present c.new(1), :with => entity
492
+ end
493
+
494
+ get '/example'
495
+ last_response.status.should == 200
496
+ last_response.body.should == '{"example":{"id":1}}'
497
+ end
498
+
499
+ it 'presents with #{format} collection' do
500
+ entity = Class.new(Grape::Entity)
501
+ entity.root "examples", "example"
502
+ entity.expose :id
503
+
504
+ subject.format format
505
+ subject.get '/examples' do
506
+ c = Class.new do
507
+ attr_reader :id
508
+ def initialize(id)
509
+ @id = id
510
+ end
511
+ end
512
+ examples = [ c.new(1), c.new(2) ]
513
+ present examples, :with => entity
514
+ end
515
+
516
+ get '/examples'
517
+ last_response.status.should == 200
518
+ last_response.body.should == '{"examples":[{"id":1},{"id":2}]}'
519
+ end
520
+
521
+ end
448
522
  end
449
523
 
450
524
  context 'filters' do
451
525
  describe 'before filters' do
452
- it 'should run the before filter if set' do
526
+ it 'runs the before filter if set' do
453
527
  subject.before{ env['before_test'] = "OK" }
454
528
  subject.get('/before_test'){ env['before_test'] }
455
529
 
@@ -459,14 +533,14 @@ describe Grape::Endpoint do
459
533
  end
460
534
 
461
535
  describe 'after filters' do
462
- it 'should override the response body if it sets it' do
536
+ it 'overrides the response body if it sets it' do
463
537
  subject.after{ body "after" }
464
538
  subject.get('/after_test'){ "during" }
465
539
  get '/after_test'
466
540
  last_response.body.should == 'after'
467
541
  end
468
542
 
469
- it 'should not override the response body with its return' do
543
+ it 'does not override the response body with its return' do
470
544
  subject.after{ "after" }
471
545
  subject.get('/after_test'){ "body" }
472
546
  get '/after_test'
@@ -479,7 +553,7 @@ describe Grape::Endpoint do
479
553
  verbs = %w(post get head delete put options patch)
480
554
 
481
555
  verbs.each do |verb|
482
- it "should allow for the anchoring option with a #{verb.upcase} method" do
556
+ it 'allows for the anchoring option with a #{verb.upcase} method' do
483
557
  subject.send(verb, '/example', :anchor => true) do
484
558
  verb
485
559
  end
@@ -487,7 +561,7 @@ describe Grape::Endpoint do
487
561
  last_response.status.should eql 404
488
562
  end
489
563
 
490
- it "should anchor paths by default for the #{verb.upcase} method" do
564
+ it 'anchors paths by default for the #{verb.upcase} method' do
491
565
  subject.send(verb, '/example') do
492
566
  verb
493
567
  end
@@ -495,7 +569,7 @@ describe Grape::Endpoint do
495
569
  last_response.status.should eql 404
496
570
  end
497
571
 
498
- it "should respond to /example/and/some/more for the non-anchored #{verb.upcase} method" do
572
+ it 'responds to /example/and/some/more for the non-anchored #{verb.upcase} method' do
499
573
  subject.send(verb, '/example', :anchor => false) do
500
574
  verb
501
575
  end
@@ -505,4 +579,32 @@ describe Grape::Endpoint do
505
579
  end
506
580
  end
507
581
  end
582
+
583
+ context 'request' do
584
+ it 'should be set to the url requested' do
585
+ subject.get('/url') do
586
+ request.url
587
+ end
588
+ get '/url'
589
+ last_response.body.should == "http://example.org/url"
590
+ end
591
+ it 'should include version' do
592
+ subject.version 'v1', :using => :path
593
+ subject.get('/url') do
594
+ request.url
595
+ end
596
+ get '/v1/url'
597
+ last_response.body.should == "http://example.org/v1/url"
598
+ end
599
+ it 'should include prefix' do
600
+ subject.version 'v1', :using => :path
601
+ subject.prefix 'api'
602
+ subject.get('/url') do
603
+ request.url
604
+ end
605
+ get '/api/v1/url'
606
+ last_response.body.should == "http://example.org/api/v1/url"
607
+ end
608
+ end
609
+
508
610
  end