grape 0.7.0 → 0.8.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 (62) hide show
  1. checksums.yaml +5 -13
  2. data/.rubocop.yml +6 -6
  3. data/.travis.yml +11 -2
  4. data/CHANGELOG.md +23 -1
  5. data/Gemfile +11 -10
  6. data/Guardfile +5 -6
  7. data/README.md +194 -13
  8. data/UPGRADING.md +3 -3
  9. data/grape.gemspec +1 -1
  10. data/grape.png +0 -0
  11. data/lib/grape/api.rb +21 -13
  12. data/lib/grape/endpoint.rb +31 -13
  13. data/lib/grape/exceptions/validation.rb +2 -2
  14. data/lib/grape/locale/en.yml +2 -0
  15. data/lib/grape/middleware/auth/oauth2.rb +69 -65
  16. data/lib/grape/middleware/error.rb +4 -2
  17. data/lib/grape/middleware/formatter.rb +2 -2
  18. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
  19. data/lib/grape/middleware/versioner/header.rb +3 -3
  20. data/lib/grape/util/hash_stack.rb +1 -1
  21. data/lib/grape/validations.rb +22 -8
  22. data/lib/grape/validations/default.rb +1 -1
  23. data/lib/grape/validations/exactly_one_of.rb +26 -0
  24. data/lib/grape/validations/mutual_exclusion.rb +25 -0
  25. data/lib/grape/validations/presence.rb +1 -1
  26. data/lib/grape/validations/regexp.rb +2 -1
  27. data/lib/grape/validations/values.rb +7 -1
  28. data/lib/grape/version.rb +1 -1
  29. data/spec/grape/api_spec.rb +390 -333
  30. data/spec/grape/endpoint_spec.rb +129 -99
  31. data/spec/grape/entity_spec.rb +47 -27
  32. data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
  33. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +1 -1
  34. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -2
  35. data/spec/grape/exceptions/missing_option_spec.rb +1 -1
  36. data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
  37. data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
  38. data/spec/grape/middleware/auth/basic_spec.rb +3 -3
  39. data/spec/grape/middleware/auth/digest_spec.rb +4 -4
  40. data/spec/grape/middleware/auth/oauth2_spec.rb +11 -11
  41. data/spec/grape/middleware/base_spec.rb +9 -9
  42. data/spec/grape/middleware/error_spec.rb +4 -4
  43. data/spec/grape/middleware/exception_spec.rb +17 -17
  44. data/spec/grape/middleware/formatter_spec.rb +38 -38
  45. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +11 -11
  46. data/spec/grape/middleware/versioner/header_spec.rb +39 -39
  47. data/spec/grape/middleware/versioner/param_spec.rb +10 -10
  48. data/spec/grape/middleware/versioner/path_spec.rb +7 -7
  49. data/spec/grape/middleware/versioner_spec.rb +4 -4
  50. data/spec/grape/path_spec.rb +6 -6
  51. data/spec/grape/util/hash_stack_spec.rb +22 -22
  52. data/spec/grape/validations/coerce_spec.rb +34 -34
  53. data/spec/grape/validations/default_spec.rb +16 -16
  54. data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
  55. data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
  56. data/spec/grape/validations/presence_spec.rb +34 -34
  57. data/spec/grape/validations/regexp_spec.rb +11 -4
  58. data/spec/grape/validations/values_spec.rb +34 -20
  59. data/spec/grape/validations_spec.rb +300 -147
  60. data/spec/shared/versioning_examples.rb +18 -18
  61. data/spec/spec_helper.rb +2 -1
  62. metadata +81 -38
@@ -28,19 +28,19 @@ describe Grape::Entity do
28
28
 
29
29
  it 'pulls a representation from the class options if it exists' do
30
30
  entity = Class.new(Grape::Entity)
31
- entity.stub(:represent).and_return("Hiya")
31
+ allow(entity).to receive(:represent).and_return("Hiya")
32
32
 
33
33
  subject.represent Object, with: entity
34
34
  subject.get '/example' do
35
35
  present Object.new
36
36
  end
37
37
  get '/example'
38
- last_response.body.should == 'Hiya'
38
+ expect(last_response.body).to eq('Hiya')
39
39
  end
40
40
 
41
41
  it 'pulls a representation from the class options if the presented object is a collection of objects' do
42
42
  entity = Class.new(Grape::Entity)
43
- entity.stub(:represent).and_return("Hiya")
43
+ allow(entity).to receive(:represent).and_return("Hiya")
44
44
 
45
45
  class TestObject
46
46
  end
@@ -61,15 +61,15 @@ describe Grape::Entity do
61
61
  end
62
62
 
63
63
  get '/example'
64
- last_response.body.should == "Hiya"
64
+ expect(last_response.body).to eq("Hiya")
65
65
 
66
66
  get '/example2'
67
- last_response.body.should == "Hiya"
67
+ expect(last_response.body).to eq("Hiya")
68
68
  end
69
69
 
70
70
  it 'pulls a representation from the class ancestor if it exists' do
71
71
  entity = Class.new(Grape::Entity)
72
- entity.stub(:represent).and_return("Hiya")
72
+ allow(entity).to receive(:represent).and_return("Hiya")
73
73
 
74
74
  subclass = Class.new(Object)
75
75
 
@@ -78,13 +78,13 @@ describe Grape::Entity do
78
78
  present subclass.new
79
79
  end
80
80
  get '/example'
81
- last_response.body.should == 'Hiya'
81
+ expect(last_response.body).to eq('Hiya')
82
82
  end
83
83
 
84
84
  it 'automatically uses Klass::Entity if that exists' do
85
85
  some_model = Class.new
86
86
  entity = Class.new(Grape::Entity)
87
- entity.stub(:represent).and_return("Auto-detect!")
87
+ allow(entity).to receive(:represent).and_return("Auto-detect!")
88
88
 
89
89
  some_model.const_set :Entity, entity
90
90
 
@@ -92,13 +92,13 @@ describe Grape::Entity do
92
92
  present some_model.new
93
93
  end
94
94
  get '/example'
95
- last_response.body.should == 'Auto-detect!'
95
+ expect(last_response.body).to eq('Auto-detect!')
96
96
  end
97
97
 
98
98
  it 'automatically uses Klass::Entity based on the first object in the collection being presented' do
99
99
  some_model = Class.new
100
100
  entity = Class.new(Grape::Entity)
101
- entity.stub(:represent).and_return("Auto-detect!")
101
+ allow(entity).to receive(:represent).and_return("Auto-detect!")
102
102
 
103
103
  some_model.const_set :Entity, entity
104
104
 
@@ -106,7 +106,7 @@ describe Grape::Entity do
106
106
  present [some_model.new]
107
107
  end
108
108
  get '/example'
109
- last_response.body.should == 'Auto-detect!'
109
+ expect(last_response.body).to eq('Auto-detect!')
110
110
  end
111
111
 
112
112
  it 'does not run autodetection for Entity when explicitely provided' do
@@ -117,10 +117,30 @@ describe Grape::Entity do
117
117
  present some_array, with: entity
118
118
  end
119
119
 
120
- some_array.should_not_receive(:first)
120
+ expect(some_array).not_to receive(:first)
121
121
  get '/example'
122
122
  end
123
123
 
124
+ it 'does not use #first method on ActiveRecord::Relation to prevent needless sql query' do
125
+ entity = Class.new(Grape::Entity)
126
+ some_relation = Class.new
127
+ some_model = Class.new
128
+
129
+ allow(entity).to receive(:represent).and_return("Auto-detect!")
130
+ allow(some_relation).to receive(:first)
131
+ allow(some_relation).to receive(:klass).and_return(some_model)
132
+
133
+ some_model.const_set :Entity, entity
134
+
135
+ subject.get '/example' do
136
+ present some_relation
137
+ end
138
+
139
+ expect(some_relation).not_to receive(:first)
140
+ get '/example'
141
+ expect(last_response.body).to eq('Auto-detect!')
142
+ end
143
+
124
144
  it 'autodetection does not use Entity if it is not a presenter' do
125
145
  some_model = Class.new
126
146
  entity = Class.new
@@ -131,7 +151,7 @@ describe Grape::Entity do
131
151
  present some_model
132
152
  end
133
153
  get '/example'
134
- entity.should_not_receive(:represent)
154
+ expect(entity).not_to receive(:represent)
135
155
  end
136
156
 
137
157
  it 'adds a root key to the output if one is given' do
@@ -161,8 +181,8 @@ describe Grape::Entity do
161
181
  end
162
182
 
163
183
  get '/example'
164
- last_response.status.should == 200
165
- last_response.body.should == '{"example":{"id":1}}'
184
+ expect(last_response.status).to eq(200)
185
+ expect(last_response.body).to eq('{"example":{"id":1}}')
166
186
  end
167
187
 
168
188
  it 'presents with #{format} collection' do
@@ -183,8 +203,8 @@ describe Grape::Entity do
183
203
  end
184
204
 
185
205
  get '/examples'
186
- last_response.status.should == 200
187
- last_response.body.should == '{"examples":[{"id":1},{"id":2}]}'
206
+ expect(last_response.status).to eq(200)
207
+ expect(last_response.body).to eq('{"examples":[{"id":1},{"id":2}]}')
188
208
  end
189
209
 
190
210
  end
@@ -206,9 +226,9 @@ describe Grape::Entity do
206
226
  present c.new(name: "johnnyiller"), with: entity
207
227
  end
208
228
  get '/example'
209
- last_response.status.should == 200
210
- last_response.headers['Content-type'].should == "application/xml"
211
- last_response.body.should == <<-XML
229
+ expect(last_response.status).to eq(200)
230
+ expect(last_response.headers['Content-type']).to eq("application/xml")
231
+ expect(last_response.body).to eq <<-XML
212
232
  <?xml version="1.0" encoding="UTF-8"?>
213
233
  <hash>
214
234
  <example>
@@ -235,9 +255,9 @@ XML
235
255
  present c.new(name: "johnnyiller"), with: entity
236
256
  end
237
257
  get '/example'
238
- last_response.status.should == 200
239
- last_response.headers['Content-type'].should == "application/json"
240
- last_response.body.should == '{"example":{"name":"johnnyiller"}}'
258
+ expect(last_response.status).to eq(200)
259
+ expect(last_response.headers['Content-type']).to eq("application/json")
260
+ expect(last_response.body).to eq('{"example":{"name":"johnnyiller"}}')
241
261
  end
242
262
 
243
263
  it 'presents with jsonp utilising Rack::JSONP' do
@@ -265,9 +285,9 @@ XML
265
285
  end
266
286
 
267
287
  get '/example?callback=abcDef'
268
- last_response.status.should == 200
269
- last_response.headers['Content-type'].should == "application/javascript"
270
- last_response.body.should == 'abcDef({"example":{"name":"johnnyiller"}})'
288
+ expect(last_response.status).to eq(200)
289
+ expect(last_response.headers['Content-type']).to eq("application/javascript")
290
+ expect(last_response.body).to eq('abcDef({"example":{"name":"johnnyiller"}})')
271
291
  end
272
292
 
273
293
  context "present with multiple entities" do
@@ -296,7 +316,7 @@ XML
296
316
  "user1" => { "name" => "user1" },
297
317
  "user2" => { "name" => "user2" }
298
318
  }
299
- JSON(last_response.body).should == expect_response_json
319
+ expect(JSON(last_response.body)).to eq(expect_response_json)
300
320
  end
301
321
 
302
322
  end
@@ -8,7 +8,7 @@ describe Grape::Exceptions::InvalidFormatter do
8
8
  end
9
9
 
10
10
  it "contains the problem in the message" do
11
- error.message.should include(
11
+ expect(error.message).to include(
12
12
  "cannot convert String to xml"
13
13
  )
14
14
  end
@@ -8,7 +8,7 @@ describe Grape::Exceptions::InvalidVersionerOption do
8
8
  end
9
9
 
10
10
  it "contains the problem in the message" do
11
- error.message.should include(
11
+ expect(error.message).to include(
12
12
  "Unknown :using for versioner: headers"
13
13
  )
14
14
  end
@@ -8,11 +8,11 @@ describe Grape::Exceptions::MissingMimeType do
8
8
  end
9
9
 
10
10
  it "contains the problem in the message" do
11
- error.message.should include "missing mime type for new_json"
11
+ expect(error.message).to include "missing mime type for new_json"
12
12
  end
13
13
 
14
14
  it "contains the resolution in the message" do
15
- error.message.should include "or add your own with content_type :new_json, 'application/new_json' "
15
+ expect(error.message).to include "or add your own with content_type :new_json, 'application/new_json' "
16
16
  end
17
17
  end
18
18
  end
@@ -8,7 +8,7 @@ describe Grape::Exceptions::MissingOption do
8
8
  end
9
9
 
10
10
  it "contains the problem in the message" do
11
- error.message.should include(
11
+ expect(error.message).to include(
12
12
  "You must specify :path options."
13
13
  )
14
14
  end
@@ -8,7 +8,7 @@ describe Grape::Exceptions::UnknownOptions do
8
8
  end
9
9
 
10
10
  it "contains the problem in the message" do
11
- error.message.should include(
11
+ expect(error.message).to include(
12
12
  "unknown options: "
13
13
  )
14
14
  end
@@ -8,7 +8,7 @@ describe Grape::Exceptions::UnknownValidator do
8
8
  end
9
9
 
10
10
  it "contains the problem in the message" do
11
- error.message.should include(
11
+ expect(error.message).to include(
12
12
  "unknown validator: gt_10"
13
13
  )
14
14
  end
@@ -16,16 +16,16 @@ describe Grape::Middleware::Auth::Basic do
16
16
  it 'throws a 401 if no auth is given' do
17
17
  @proc = lambda { false }
18
18
  get '/whatever'
19
- last_response.status.should == 401
19
+ expect(last_response.status).to eq(401)
20
20
  end
21
21
 
22
22
  it 'authenticates if given valid creds' do
23
23
  get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin', 'admin')
24
- last_response.status.should == 200
24
+ expect(last_response.status).to eq(200)
25
25
  end
26
26
 
27
27
  it 'throws a 401 is wrong auth is given' do
28
28
  get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin', 'wrong')
29
- last_response.status.should == 401
29
+ expect(last_response.status).to eq(401)
30
30
  end
31
31
  end
@@ -25,23 +25,23 @@ describe Grape::Middleware::Auth::Digest do
25
25
 
26
26
  it 'is a digest authentication challenge' do
27
27
  get '/test'
28
- last_response.should be_challenge
28
+ expect(last_response).to be_challenge
29
29
  end
30
30
 
31
31
  it 'throws a 401 if no auth is given' do
32
32
  get '/test'
33
- last_response.status.should == 401
33
+ expect(last_response.status).to eq(401)
34
34
  end
35
35
 
36
36
  it 'authenticates if given valid creds' do
37
37
  digest_authorize "foo", "bar"
38
38
  get '/test'
39
- last_response.status.should == 200
39
+ expect(last_response.status).to eq(200)
40
40
  end
41
41
 
42
42
  it 'throws a 401 if given invalid creds' do
43
43
  digest_authorize "bar", "foo"
44
44
  get '/test'
45
- last_response.status.should == 401
45
+ expect(last_response.status).to eq(401)
46
46
  end
47
47
  end
@@ -33,7 +33,7 @@ describe Grape::Middleware::Auth::OAuth2 do
33
33
  before { get '/awesome?access_token=g123' }
34
34
 
35
35
  it 'sets env["api.token"]' do
36
- last_response.body.should == 'g123'
36
+ expect(last_response.body).to eq('g123')
37
37
  end
38
38
  end
39
39
 
@@ -45,11 +45,11 @@ describe Grape::Middleware::Auth::OAuth2 do
45
45
  end
46
46
 
47
47
  it 'throws an error' do
48
- @err[:status].should == 401
48
+ expect(@err[:status]).to eq(401)
49
49
  end
50
50
 
51
51
  it 'sets the WWW-Authenticate header in the response' do
52
- @err[:headers]['WWW-Authenticate'].should == "OAuth realm='OAuth API', error='invalid_grant'"
52
+ expect(@err[:headers]['WWW-Authenticate']).to eq("OAuth realm='OAuth API', error='invalid_grant'")
53
53
  end
54
54
  end
55
55
  end
@@ -62,11 +62,11 @@ describe Grape::Middleware::Auth::OAuth2 do
62
62
  end
63
63
 
64
64
  it 'throws an error' do
65
- @err[:status].should == 401
65
+ expect(@err[:status]).to eq(401)
66
66
  end
67
67
 
68
68
  it 'sets the WWW-Authenticate header in the response to error' do
69
- @err[:headers]['WWW-Authenticate'].should == "OAuth realm='OAuth API', error='invalid_grant'"
69
+ expect(@err[:headers]['WWW-Authenticate']).to eq("OAuth realm='OAuth API', error='invalid_grant'")
70
70
  end
71
71
  end
72
72
 
@@ -77,7 +77,7 @@ describe Grape::Middleware::Auth::OAuth2 do
77
77
  end
78
78
 
79
79
  it 'sets env["api.token"]' do
80
- last_response.body.should == 'g123'
80
+ expect(last_response.body).to eq('g123')
81
81
  end
82
82
  end
83
83
  end
@@ -88,7 +88,7 @@ describe Grape::Middleware::Auth::OAuth2 do
88
88
  end
89
89
 
90
90
  it 'sets env["api.token"]' do
91
- last_response.body.should == 'g123'
91
+ expect(last_response.body).to eq('g123')
92
92
  end
93
93
  end
94
94
 
@@ -100,11 +100,11 @@ describe Grape::Middleware::Auth::OAuth2 do
100
100
  end
101
101
 
102
102
  it 'throws an error' do
103
- @err[:status].should == 403
103
+ expect(@err[:status]).to eq(403)
104
104
  end
105
105
 
106
106
  it 'sets the WWW-Authenticate header in the response to error' do
107
- @err[:headers]['WWW-Authenticate'].should == "OAuth realm='OAuth API', error='insufficient_scope'"
107
+ expect(@err[:headers]['WWW-Authenticate']).to eq("OAuth realm='OAuth API', error='insufficient_scope'")
108
108
  end
109
109
  end
110
110
 
@@ -120,7 +120,7 @@ describe Grape::Middleware::Auth::OAuth2 do
120
120
  before { post '/awesome' }
121
121
 
122
122
  it 'succeeds anyway' do
123
- last_response.status.should == 200
123
+ expect(last_response.status).to eq(200)
124
124
  end
125
125
  end
126
126
 
@@ -128,7 +128,7 @@ describe Grape::Middleware::Auth::OAuth2 do
128
128
  before { get '/awesome?access_token=g123' }
129
129
 
130
130
  it 'sets env["api.token"]' do
131
- last_response.body.should == 'g123'
131
+ expect(last_response.body).to eq('g123')
132
132
  end
133
133
  end
134
134
  end
@@ -6,24 +6,24 @@ describe Grape::Middleware::Base do
6
6
 
7
7
  before do
8
8
  # Keep it one object for testing.
9
- subject.stub(:dup).and_return(subject)
9
+ allow(subject).to receive(:dup).and_return(subject)
10
10
  end
11
11
 
12
12
  it 'has the app as an accessor' do
13
- subject.app.should == blank_app
13
+ expect(subject.app).to eq(blank_app)
14
14
  end
15
15
 
16
16
  it 'calls through to the app' do
17
- subject.call({}).should == [200, {}, 'Hi there.']
17
+ expect(subject.call({})).to eq([200, {}, 'Hi there.'])
18
18
  end
19
19
 
20
20
  context 'callbacks' do
21
21
  it 'calls #before' do
22
- subject.should_receive(:before)
22
+ expect(subject).to receive(:before)
23
23
  end
24
24
 
25
25
  it 'calls #after' do
26
- subject.should_receive(:after)
26
+ expect(subject).to receive(:after)
27
27
  end
28
28
 
29
29
  after { subject.call!({}) }
@@ -31,12 +31,12 @@ describe Grape::Middleware::Base do
31
31
 
32
32
  it 'is able to access the response' do
33
33
  subject.call({})
34
- subject.response.should be_kind_of(Rack::Response)
34
+ expect(subject.response).to be_kind_of(Rack::Response)
35
35
  end
36
36
 
37
37
  context 'options' do
38
38
  it 'persists options passed at initialization' do
39
- Grape::Middleware::Base.new(blank_app, abc: true).options[:abc].should be true
39
+ expect(Grape::Middleware::Base.new(blank_app, abc: true).options[:abc]).to be true
40
40
  end
41
41
 
42
42
  context 'defaults' do
@@ -47,11 +47,11 @@ describe Grape::Middleware::Base do
47
47
  end
48
48
 
49
49
  it 'persists the default options' do
50
- ExampleWare.new(blank_app).options[:monkey].should be true
50
+ expect(ExampleWare.new(blank_app).options[:monkey]).to be true
51
51
  end
52
52
 
53
53
  it 'overrides default options when provided' do
54
- ExampleWare.new(blank_app, monkey: false).options[:monkey].should be false
54
+ expect(ExampleWare.new(blank_app, monkey: false).options[:monkey]).to be false
55
55
  end
56
56
  end
57
57
  end
@@ -22,24 +22,24 @@ describe Grape::Middleware::Error do
22
22
  it 'sets the status code appropriately' do
23
23
  ErrApp.error = { status: 410 }
24
24
  get '/'
25
- last_response.status.should == 410
25
+ expect(last_response.status).to eq(410)
26
26
  end
27
27
 
28
28
  it 'sets the error message appropriately' do
29
29
  ErrApp.error = { message: 'Awesome stuff.' }
30
30
  get '/'
31
- last_response.body.should == 'Awesome stuff.'
31
+ expect(last_response.body).to eq('Awesome stuff.')
32
32
  end
33
33
 
34
34
  it 'defaults to a 500 status' do
35
35
  ErrApp.error = {}
36
36
  get '/'
37
- last_response.status.should == 500
37
+ expect(last_response.status).to eq(500)
38
38
  end
39
39
 
40
40
  it 'has a default message' do
41
41
  ErrApp.error = {}
42
42
  get '/'
43
- last_response.body.should == 'Aww, hamburgers.'
43
+ expect(last_response.body).to eq('Aww, hamburgers.')
44
44
  end
45
45
  end