grape 0.4.1 → 0.5.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.
- data/CHANGELOG.md +29 -5
- data/Gemfile +1 -0
- data/README.md +169 -80
- data/Rakefile +19 -3
- data/lib/grape.rb +6 -3
- data/lib/grape/api.rb +24 -3
- data/lib/grape/endpoint.rb +34 -20
- data/lib/grape/http/request.rb +28 -0
- data/lib/grape/middleware/base.rb +1 -1
- data/lib/grape/middleware/filter.rb +1 -1
- data/lib/grape/middleware/formatter.rb +33 -22
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
- data/lib/grape/middleware/versioner/header.rb +3 -1
- data/lib/grape/util/hash_stack.rb +11 -0
- data/lib/grape/validations.rb +47 -11
- data/lib/grape/validations/default.rb +24 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +171 -11
- data/spec/grape/endpoint_spec.rb +41 -1
- data/spec/grape/entity_spec.rb +38 -0
- data/spec/grape/middleware/formatter_spec.rb +48 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
- data/spec/grape/middleware/versioner_spec.rb +4 -1
- data/spec/grape/validations/default_spec.rb +67 -0
- data/spec/grape/validations_spec.rb +9 -0
- data/spec/shared/versioning_examples.rb +13 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/versioned_helpers.rb +6 -0
- metadata +11 -4
@@ -36,6 +36,29 @@ describe Grape::Middleware::Formatter do
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
context 'error handling' do
|
40
|
+
let(:formatter) { stub(:formatter) }
|
41
|
+
before do
|
42
|
+
Grape::Formatter::Base.stub(:formatter_for) { formatter }
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'rescues formatter-specific exceptions' do
|
46
|
+
formatter.stub(:call) { raise Grape::Exceptions::InvalidFormatter.new(String, 'xml') }
|
47
|
+
|
48
|
+
expect {
|
49
|
+
catch(:error){subject.call({'PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json'})}
|
50
|
+
}.to_not raise_error
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'does not rescue other exceptions' do
|
54
|
+
formatter.stub(:call) { raise StandardError }
|
55
|
+
|
56
|
+
expect {
|
57
|
+
catch(:error){subject.call({'PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json'})}
|
58
|
+
}.to raise_error
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
39
62
|
context 'detection' do
|
40
63
|
|
41
64
|
it 'uses the extension if one is provided' do
|
@@ -166,6 +189,31 @@ describe Grape::Middleware::Formatter do
|
|
166
189
|
end
|
167
190
|
end
|
168
191
|
end
|
192
|
+
it "parses the chunked body from #{method} and copies values into rack.request.from_hash" do
|
193
|
+
io = StringIO.new('{"is_boolean":true,"string":"thing"}')
|
194
|
+
subject.call({
|
195
|
+
'PATH_INFO' => '/infol',
|
196
|
+
'REQUEST_METHOD' => method,
|
197
|
+
'CONTENT_TYPE' => 'application/json',
|
198
|
+
'rack.input' => io,
|
199
|
+
'HTTP_TRANSFER_ENCODING' => 'chunked'
|
200
|
+
})
|
201
|
+
subject.env['rack.request.form_hash']['is_boolean'].should be_true
|
202
|
+
subject.env['rack.request.form_hash']['string'].should == 'thing'
|
203
|
+
end
|
204
|
+
it "rewinds IO" do
|
205
|
+
io = StringIO.new('{"is_boolean":true,"string":"thing"}')
|
206
|
+
io.read
|
207
|
+
subject.call({
|
208
|
+
'PATH_INFO' => '/infol',
|
209
|
+
'REQUEST_METHOD' => method,
|
210
|
+
'CONTENT_TYPE' => 'application/json',
|
211
|
+
'rack.input' => io,
|
212
|
+
'HTTP_TRANSFER_ENCODING' => 'chunked'
|
213
|
+
})
|
214
|
+
subject.env['rack.request.form_hash']['is_boolean'].should be_true
|
215
|
+
subject.env['rack.request.form_hash']['string'].should == 'thing'
|
216
|
+
end
|
169
217
|
it 'parses the body from an xml #{method} and copies values into rack.request.from_hash' do
|
170
218
|
io = StringIO.new('<thing><name>Test</name></thing>')
|
171
219
|
subject.call({
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Middleware::Versioner::AcceptVersionHeader do
|
4
|
+
let(:app) { lambda{|env| [200, env, env]} }
|
5
|
+
subject { Grape::Middleware::Versioner::AcceptVersionHeader.new(app, @options || {}) }
|
6
|
+
|
7
|
+
before do
|
8
|
+
@options = {
|
9
|
+
:version_options => {
|
10
|
+
:using => :accept_version_header
|
11
|
+
},
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'api.version' do
|
16
|
+
before do
|
17
|
+
@options[:versions] = ['v1']
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'is set' do
|
21
|
+
status, _, env = subject.call('HTTP_ACCEPT_VERSION' => 'v1')
|
22
|
+
env['api.version'].should eql 'v1'
|
23
|
+
status.should == 200
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'is set if format provided' do
|
27
|
+
status, _, env = subject.call('HTTP_ACCEPT_VERSION' => 'v1')
|
28
|
+
env['api.version'].should eql 'v1'
|
29
|
+
status.should == 200
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'fails with 406 Not Acceptable if version is not supported' do
|
33
|
+
expect {
|
34
|
+
env = subject.call('HTTP_ACCEPT_VERSION' => 'v2').last
|
35
|
+
}.to throw_symbol(
|
36
|
+
:error,
|
37
|
+
:status => 406,
|
38
|
+
:headers => {'X-Cascade' => 'pass'},
|
39
|
+
:message => 'The requested version is not supported.'
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'succeeds if :strict is not set' do
|
45
|
+
subject.call('HTTP_ACCEPT_VERSION' => '').first.should == 200
|
46
|
+
subject.call({}).first.should == 200
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'succeeds if :strict is set to false' do
|
50
|
+
@options[:version_options][:strict] = false
|
51
|
+
subject.call('HTTP_ACCEPT_VERSION' => '').first.should == 200
|
52
|
+
subject.call({}).first.should == 200
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when :strict is set' do
|
56
|
+
before do
|
57
|
+
@options[:versions] = ['v1']
|
58
|
+
@options[:version_options][:strict] = true
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'fails with 406 Not Acceptable if header is not set' do
|
62
|
+
expect {
|
63
|
+
env = subject.call({}).last
|
64
|
+
}.to throw_symbol(
|
65
|
+
:error,
|
66
|
+
:status => 406,
|
67
|
+
:headers => {'X-Cascade' => 'pass'},
|
68
|
+
:message => 'Accept-Version header must be set.'
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'fails with 406 Not Acceptable if header is empty' do
|
73
|
+
expect {
|
74
|
+
env = subject.call('HTTP_ACCEPT_VERSION' => '').last
|
75
|
+
}.to throw_symbol(
|
76
|
+
:error,
|
77
|
+
:status => 406,
|
78
|
+
:headers => {'X-Cascade' => 'pass'},
|
79
|
+
:message => 'Accept-Version header must be set.'
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'succeeds if proper header is set' do
|
84
|
+
subject.call('HTTP_ACCEPT_VERSION' => 'v1').first.should == 200
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when :strict and :cascade=>false are set' do
|
89
|
+
before do
|
90
|
+
@options[:versions] = ['v1']
|
91
|
+
@options[:version_options][:strict] = true
|
92
|
+
@options[:version_options][:cascade] = false
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'fails with 406 Not Acceptable if header is not set' do
|
96
|
+
expect {
|
97
|
+
env = subject.call({}).last
|
98
|
+
}.to throw_symbol(
|
99
|
+
:error,
|
100
|
+
:status => 406,
|
101
|
+
:headers => {},
|
102
|
+
:message => 'Accept-Version header must be set.'
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'fails with 406 Not Acceptable if header is empty' do
|
107
|
+
expect {
|
108
|
+
env = subject.call('HTTP_ACCEPT_VERSION' => '').last
|
109
|
+
}.to throw_symbol(
|
110
|
+
:error,
|
111
|
+
:status => 406,
|
112
|
+
:headers => {},
|
113
|
+
:message => 'Accept-Version header must be set.'
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'succeeds if proper header is set' do
|
118
|
+
subject.call('HTTP_ACCEPT_VERSION' => 'v1').first.should == 200
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -16,4 +16,7 @@ describe Grape::Middleware::Versioner do
|
|
16
16
|
klass.using(:param).should == Grape::Middleware::Versioner::Param
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
it 'recognizes :accept_version_header' do
|
20
|
+
klass.using(:accept_version_header).should == Grape::Middleware::Versioner::AcceptVersionHeader
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Validations::DefaultValidator do
|
4
|
+
|
5
|
+
module ValidationsSpec
|
6
|
+
module DefaultValidatorSpec
|
7
|
+
class API < Grape::API
|
8
|
+
default_format :json
|
9
|
+
|
10
|
+
params do
|
11
|
+
optional :id
|
12
|
+
optional :type, :default => 'default-type'
|
13
|
+
end
|
14
|
+
get '/' do
|
15
|
+
{ :id => params[:id], :type => params[:type] }
|
16
|
+
end
|
17
|
+
|
18
|
+
params do
|
19
|
+
optional :type1, :default => 'default-type1'
|
20
|
+
optional :type2, :default => 'default-type2'
|
21
|
+
end
|
22
|
+
get '/user' do
|
23
|
+
{ :type1 => params[:type1], :type2 => params[:type2] }
|
24
|
+
end
|
25
|
+
|
26
|
+
params do
|
27
|
+
requires :id
|
28
|
+
optional :type1, :default => 'default-type1'
|
29
|
+
optional :type2, :default => 'default-type2'
|
30
|
+
end
|
31
|
+
|
32
|
+
get '/message' do
|
33
|
+
{ :id => params[:id], :type1 => params[:type1], :type2 => params[:type2] }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def app
|
40
|
+
ValidationsSpec::DefaultValidatorSpec::API
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'set default value for optional param' do
|
44
|
+
get("/")
|
45
|
+
last_response.status.should == 200
|
46
|
+
last_response.body.should == { :id => nil, :type => 'default-type' }.to_json
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'set default values for optional params' do
|
50
|
+
get("/user")
|
51
|
+
last_response.status.should == 200
|
52
|
+
last_response.body.should == { :type1 => 'default-type1', :type2 => 'default-type2' }.to_json
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'set default values for missing params in the request' do
|
56
|
+
get("/user?type2=value2")
|
57
|
+
last_response.status.should == 200
|
58
|
+
last_response.body.should == { :type1 => 'default-type1', :type2 => 'value2' }.to_json
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'set default values for optional params and allow to use required fields in the same time' do
|
62
|
+
get("/message?id=1")
|
63
|
+
last_response.status.should == 200
|
64
|
+
last_response.body.should == { :id => '1', :type1 => 'default-type1', :type2 => 'default-type2' }.to_json
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -79,6 +79,15 @@ describe Grape::Validations do
|
|
79
79
|
last_response.status.should == 200
|
80
80
|
last_response.body.should == 'required works'
|
81
81
|
end
|
82
|
+
|
83
|
+
it 'adds to declared parameters' do
|
84
|
+
subject.params {
|
85
|
+
group :items do
|
86
|
+
requires :key
|
87
|
+
end
|
88
|
+
}
|
89
|
+
subject.settings[:declared_params].should == [:items => [:key]]
|
90
|
+
end
|
82
91
|
end
|
83
92
|
|
84
93
|
context 'custom validation' do
|
@@ -11,7 +11,7 @@ shared_examples_for 'versioning' do
|
|
11
11
|
|
12
12
|
it 'adds the prefix before the API version' do
|
13
13
|
subject.format :txt
|
14
|
-
subject.prefix 'api'
|
14
|
+
subject.prefix 'api'
|
15
15
|
subject.version 'v1', macro_options
|
16
16
|
subject.get :hello do
|
17
17
|
"Version: #{request.env['api.version']}"
|
@@ -106,4 +106,16 @@ shared_examples_for 'versioning' do
|
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
109
|
+
|
110
|
+
it 'does not overwrite version parameter with API version' do
|
111
|
+
subject.format :txt
|
112
|
+
subject.version 'v1', macro_options
|
113
|
+
subject.params { requires :version }
|
114
|
+
subject.get :api_version_with_version_param do
|
115
|
+
params[:version]
|
116
|
+
end
|
117
|
+
versioned_get '/api_version_with_version_param?version=1', 'v1', macro_options
|
118
|
+
last_response.body.should eql '1'
|
119
|
+
end
|
120
|
+
|
109
121
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -10,6 +10,8 @@ def versioned_path(options = {})
|
|
10
10
|
File.join('/', options[:prefix] || '', options[:path])
|
11
11
|
when :header
|
12
12
|
File.join('/', options[:prefix] || '', options[:path])
|
13
|
+
when :accept_version_header
|
14
|
+
File.join('/', options[:prefix] || '', options[:path])
|
13
15
|
else
|
14
16
|
raise ArgumentError.new("unknown versioning strategy: #{options[:using]}")
|
15
17
|
end
|
@@ -25,6 +27,10 @@ def versioned_headers(options)
|
|
25
27
|
{
|
26
28
|
'HTTP_ACCEPT' => "application/vnd.#{options[:vendor]}-#{options[:version]}+#{options[:format]}"
|
27
29
|
}
|
30
|
+
when :accept_version_header
|
31
|
+
{
|
32
|
+
'HTTP_ACCEPT_VERSION' => "#{options[:version]}"
|
33
|
+
}
|
28
34
|
else
|
29
35
|
raise ArgumentError.new("unknown versioning strategy: #{options[:using]}")
|
30
36
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-06-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -308,6 +308,7 @@ files:
|
|
308
308
|
- lib/grape/formatter/serializable_hash.rb
|
309
309
|
- lib/grape/formatter/txt.rb
|
310
310
|
- lib/grape/formatter/xml.rb
|
311
|
+
- lib/grape/http/request.rb
|
311
312
|
- lib/grape/locale/en.yml
|
312
313
|
- lib/grape/middleware/auth/basic.rb
|
313
314
|
- lib/grape/middleware/auth/digest.rb
|
@@ -317,6 +318,7 @@ files:
|
|
317
318
|
- lib/grape/middleware/filter.rb
|
318
319
|
- lib/grape/middleware/formatter.rb
|
319
320
|
- lib/grape/middleware/versioner.rb
|
321
|
+
- lib/grape/middleware/versioner/accept_version_header.rb
|
320
322
|
- lib/grape/middleware/versioner/header.rb
|
321
323
|
- lib/grape/middleware/versioner/param.rb
|
322
324
|
- lib/grape/middleware/versioner/path.rb
|
@@ -330,6 +332,7 @@ files:
|
|
330
332
|
- lib/grape/util/hash_stack.rb
|
331
333
|
- lib/grape/validations.rb
|
332
334
|
- lib/grape/validations/coerce.rb
|
335
|
+
- lib/grape/validations/default.rb
|
333
336
|
- lib/grape/validations/presence.rb
|
334
337
|
- lib/grape/validations/regexp.rb
|
335
338
|
- lib/grape/version.rb
|
@@ -349,12 +352,14 @@ files:
|
|
349
352
|
- spec/grape/middleware/error_spec.rb
|
350
353
|
- spec/grape/middleware/exception_spec.rb
|
351
354
|
- spec/grape/middleware/formatter_spec.rb
|
355
|
+
- spec/grape/middleware/versioner/accept_version_header_spec.rb
|
352
356
|
- spec/grape/middleware/versioner/header_spec.rb
|
353
357
|
- spec/grape/middleware/versioner/param_spec.rb
|
354
358
|
- spec/grape/middleware/versioner/path_spec.rb
|
355
359
|
- spec/grape/middleware/versioner_spec.rb
|
356
360
|
- spec/grape/util/hash_stack_spec.rb
|
357
361
|
- spec/grape/validations/coerce_spec.rb
|
362
|
+
- spec/grape/validations/default_spec.rb
|
358
363
|
- spec/grape/validations/presence_spec.rb
|
359
364
|
- spec/grape/validations/regexp_spec.rb
|
360
365
|
- spec/grape/validations/zh-CN.yml
|
@@ -378,7 +383,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
378
383
|
version: '0'
|
379
384
|
segments:
|
380
385
|
- 0
|
381
|
-
hash: -
|
386
|
+
hash: -683082835591277946
|
382
387
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
383
388
|
none: false
|
384
389
|
requirements:
|
@@ -387,7 +392,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
387
392
|
version: '0'
|
388
393
|
segments:
|
389
394
|
- 0
|
390
|
-
hash: -
|
395
|
+
hash: -683082835591277946
|
391
396
|
requirements: []
|
392
397
|
rubyforge_project: grape
|
393
398
|
rubygems_version: 1.8.25
|
@@ -411,12 +416,14 @@ test_files:
|
|
411
416
|
- spec/grape/middleware/error_spec.rb
|
412
417
|
- spec/grape/middleware/exception_spec.rb
|
413
418
|
- spec/grape/middleware/formatter_spec.rb
|
419
|
+
- spec/grape/middleware/versioner/accept_version_header_spec.rb
|
414
420
|
- spec/grape/middleware/versioner/header_spec.rb
|
415
421
|
- spec/grape/middleware/versioner/param_spec.rb
|
416
422
|
- spec/grape/middleware/versioner/path_spec.rb
|
417
423
|
- spec/grape/middleware/versioner_spec.rb
|
418
424
|
- spec/grape/util/hash_stack_spec.rb
|
419
425
|
- spec/grape/validations/coerce_spec.rb
|
426
|
+
- spec/grape/validations/default_spec.rb
|
420
427
|
- spec/grape/validations/presence_spec.rb
|
421
428
|
- spec/grape/validations/regexp_spec.rb
|
422
429
|
- spec/grape/validations/zh-CN.yml
|