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
@@ -14,10 +14,10 @@ describe Grape::Validations::PresenceValidator do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
params do
|
17
|
-
requires :id, :
|
17
|
+
requires :id, regexp: /^[0-9]+$/
|
18
18
|
end
|
19
19
|
post do
|
20
|
-
{:
|
20
|
+
{ ret: params[:id] }
|
21
21
|
end
|
22
22
|
|
23
23
|
params do
|
@@ -28,8 +28,9 @@ describe Grape::Validations::PresenceValidator do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
params do
|
31
|
-
|
32
|
-
requires :first_name
|
31
|
+
requires :user, type: Hash do
|
32
|
+
requires :first_name
|
33
|
+
requires :last_name
|
33
34
|
end
|
34
35
|
end
|
35
36
|
get '/nested' do
|
@@ -37,11 +38,12 @@ describe Grape::Validations::PresenceValidator do
|
|
37
38
|
end
|
38
39
|
|
39
40
|
params do
|
40
|
-
|
41
|
+
requires :admin, type: Hash do
|
41
42
|
requires :admin_name
|
42
|
-
|
43
|
-
|
44
|
-
requires :first_name
|
43
|
+
requires :super, type: Hash do
|
44
|
+
requires :user, type: Hash do
|
45
|
+
requires :first_name
|
46
|
+
requires :last_name
|
45
47
|
end
|
46
48
|
end
|
47
49
|
end
|
@@ -58,87 +60,83 @@ describe Grape::Validations::PresenceValidator do
|
|
58
60
|
end
|
59
61
|
|
60
62
|
it 'does not validate for any params' do
|
61
|
-
get
|
63
|
+
get "/bacons"
|
62
64
|
last_response.status.should == 200
|
63
|
-
last_response.body.should == "All the bacon"
|
65
|
+
last_response.body.should == "All the bacon".to_json
|
64
66
|
end
|
65
67
|
|
66
68
|
it 'validates id' do
|
67
69
|
post '/'
|
68
70
|
last_response.status.should == 400
|
69
|
-
last_response.body.should == '{"error":"
|
71
|
+
last_response.body.should == '{"error":"id is missing"}'
|
70
72
|
|
71
73
|
io = StringIO.new('{"id" : "a56b"}')
|
72
|
-
post '/', {}, 'rack.input' => io,
|
73
|
-
|
74
|
-
'CONTENT_LENGTH' => io.length
|
75
|
-
last_response.body.should == '{"error":"invalid parameter: id"}'
|
74
|
+
post '/', {}, 'rack.input' => io, 'CONTENT_TYPE' => 'application/json', 'CONTENT_LENGTH' => io.length
|
75
|
+
last_response.body.should == '{"error":"id is invalid"}'
|
76
76
|
last_response.status.should == 400
|
77
77
|
|
78
78
|
io = StringIO.new('{"id" : 56}')
|
79
|
-
post '/', {}, 'rack.input' => io,
|
80
|
-
'CONTENT_TYPE' => 'application/json',
|
81
|
-
'CONTENT_LENGTH' => io.length
|
79
|
+
post '/', {}, 'rack.input' => io, 'CONTENT_TYPE' => 'application/json', 'CONTENT_LENGTH' => io.length
|
82
80
|
last_response.body.should == '{"ret":56}'
|
83
81
|
last_response.status.should == 201
|
84
82
|
end
|
85
83
|
|
86
84
|
it 'validates name, company' do
|
87
|
-
get
|
85
|
+
get '/'
|
88
86
|
last_response.status.should == 400
|
89
|
-
last_response.body.should == '{"error":"
|
87
|
+
last_response.body.should == '{"error":"name is missing"}'
|
90
88
|
|
91
|
-
get
|
89
|
+
get '/', name: "Bob"
|
92
90
|
last_response.status.should == 400
|
93
|
-
last_response.body.should == '{"error":"
|
91
|
+
last_response.body.should == '{"error":"company is missing"}'
|
94
92
|
|
95
|
-
get
|
93
|
+
get '/', name: "Bob", company: "TestCorp"
|
96
94
|
last_response.status.should == 200
|
97
|
-
last_response.body.should == "Hello"
|
95
|
+
last_response.body.should == "Hello".to_json
|
98
96
|
end
|
99
97
|
|
100
98
|
it 'validates nested parameters' do
|
101
|
-
get
|
99
|
+
get '/nested'
|
102
100
|
last_response.status.should == 400
|
103
|
-
last_response.body.should == '{"error":"missing
|
101
|
+
last_response.body.should == '{"error":"user is missing, user[first_name] is missing, user[last_name] is missing"}'
|
104
102
|
|
105
|
-
get
|
103
|
+
get '/nested', user: { first_name: "Billy" }
|
106
104
|
last_response.status.should == 400
|
107
|
-
last_response.body.should == '{"error":"
|
105
|
+
last_response.body.should == '{"error":"user[last_name] is missing"}'
|
108
106
|
|
109
|
-
get
|
107
|
+
get '/nested', user: { first_name: "Billy", last_name: "Bob" }
|
110
108
|
last_response.status.should == 200
|
111
|
-
last_response.body.should == "Nested"
|
109
|
+
last_response.body.should == "Nested".to_json
|
112
110
|
end
|
113
111
|
|
114
112
|
it 'validates triple nested parameters' do
|
115
|
-
get
|
113
|
+
get '/nested_triple'
|
116
114
|
last_response.status.should == 400
|
117
|
-
last_response.body.should
|
115
|
+
last_response.body.should include '{"error":"admin is missing'
|
118
116
|
|
119
|
-
get
|
117
|
+
get '/nested_triple', user: { first_name: "Billy" }
|
120
118
|
last_response.status.should == 400
|
121
|
-
last_response.body.should
|
119
|
+
last_response.body.should include '{"error":"admin is missing'
|
122
120
|
|
123
|
-
get
|
121
|
+
get '/nested_triple', admin: { super: { first_name: "Billy" } }
|
124
122
|
last_response.status.should == 400
|
125
|
-
last_response.body.should == '{"error":"missing
|
123
|
+
last_response.body.should == '{"error":"admin[admin_name] is missing, admin[super][user] is missing, admin[super][user][first_name] is missing, admin[super][user][last_name] is missing"}'
|
126
124
|
|
127
|
-
get
|
125
|
+
get '/nested_triple', super: { user: { first_name: "Billy", last_name: "Bob" } }
|
128
126
|
last_response.status.should == 400
|
129
|
-
last_response.body.should
|
127
|
+
last_response.body.should include '{"error":"admin is missing'
|
130
128
|
|
131
|
-
get
|
129
|
+
get '/nested_triple', admin: { super: { user: { first_name: "Billy" } } }
|
132
130
|
last_response.status.should == 400
|
133
|
-
last_response.body.should == '{"error":"missing
|
131
|
+
last_response.body.should == '{"error":"admin[admin_name] is missing, admin[super][user][last_name] is missing"}'
|
134
132
|
|
135
|
-
get
|
133
|
+
get '/nested_triple', admin: { admin_name: 'admin', super: { user: { first_name: "Billy" } } }
|
136
134
|
last_response.status.should == 400
|
137
|
-
last_response.body.should == '{"error":"
|
135
|
+
last_response.body.should == '{"error":"admin[super][user][last_name] is missing"}'
|
138
136
|
|
139
|
-
get
|
137
|
+
get '/nested_triple', admin: { admin_name: 'admin', super: { user: { first_name: "Billy", last_name: "Bob" } } }
|
140
138
|
last_response.status.should == 200
|
141
|
-
last_response.body.should == "Nested triple"
|
139
|
+
last_response.body.should == "Nested triple".to_json
|
142
140
|
end
|
143
141
|
|
144
142
|
end
|
@@ -5,29 +5,29 @@ describe Grape::Validations::RegexpValidator do
|
|
5
5
|
module RegexpValidatorSpec
|
6
6
|
class API < Grape::API
|
7
7
|
default_format :json
|
8
|
-
|
8
|
+
|
9
9
|
params do
|
10
|
-
requires :name, :
|
10
|
+
requires :name, regexp: /^[a-z]+$/
|
11
11
|
end
|
12
12
|
get do
|
13
|
-
|
13
|
+
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def app
|
20
20
|
ValidationsSpec::RegexpValidatorSpec::API
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
it 'refuses invalid input' do
|
24
|
-
get '/', :
|
24
|
+
get '/', name: "invalid name"
|
25
25
|
last_response.status.should == 400
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
it 'accepts valid input' do
|
29
|
-
get '/', :
|
29
|
+
get '/', name: "bob"
|
30
30
|
last_response.status.should == 200
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Validations::ValuesValidator do
|
4
|
+
|
5
|
+
class ValuesModel
|
6
|
+
DEFAULT_VALUES = ['valid-type1', 'valid-type2', 'valid-type3']
|
7
|
+
class << self
|
8
|
+
def values
|
9
|
+
@values ||= []
|
10
|
+
[DEFAULT_VALUES + @values].flatten.uniq
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_value(value)
|
14
|
+
@values ||= []
|
15
|
+
@values << value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ValidationsSpec
|
21
|
+
module ValuesValidatorSpec
|
22
|
+
class API < Grape::API
|
23
|
+
default_format :json
|
24
|
+
|
25
|
+
params do
|
26
|
+
requires :type, values: ValuesModel.values
|
27
|
+
end
|
28
|
+
get '/' do
|
29
|
+
{ type: params[:type] }
|
30
|
+
end
|
31
|
+
|
32
|
+
params do
|
33
|
+
optional :type, values: ValuesModel.values, default: 'valid-type2'
|
34
|
+
end
|
35
|
+
get '/default/valid' do
|
36
|
+
{ type: params[:type] }
|
37
|
+
end
|
38
|
+
|
39
|
+
params do
|
40
|
+
optional :type, values: -> { ValuesModel.values }, default: 'valid-type2'
|
41
|
+
end
|
42
|
+
get '/lambda' do
|
43
|
+
{ type: params[:type] }
|
44
|
+
end
|
45
|
+
|
46
|
+
params do
|
47
|
+
requires :type, type: Integer, desc: "An integer", values: [10, 11], default: 10
|
48
|
+
end
|
49
|
+
get '/values/coercion' do
|
50
|
+
{ type: params[:type] }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def app
|
57
|
+
ValidationsSpec::ValuesValidatorSpec::API
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'allows a valid value for a parameter' do
|
61
|
+
get("/", type: 'valid-type1')
|
62
|
+
last_response.status.should eq 200
|
63
|
+
last_response.body.should eq({ type: "valid-type1" }.to_json)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'does not allow an invalid value for a parameter' do
|
67
|
+
get("/", type: 'invalid-type')
|
68
|
+
last_response.status.should eq 400
|
69
|
+
last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'does not allow nil value for a parameter' do
|
73
|
+
get("/", type: nil)
|
74
|
+
last_response.status.should eq 400
|
75
|
+
last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'allows a valid default value' do
|
79
|
+
get("/default/valid")
|
80
|
+
last_response.status.should eq 200
|
81
|
+
last_response.body.should eq({ type: "valid-type2" }.to_json)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'allows a proc for values' do
|
85
|
+
get('/lambda', type: 'valid-type1')
|
86
|
+
last_response.status.should eq 200
|
87
|
+
last_response.body.should eq({ type: "valid-type1" }.to_json)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'does not validate updated values without proc' do
|
91
|
+
ValuesModel.add_value('valid-type4')
|
92
|
+
|
93
|
+
get('/', type: 'valid-type4')
|
94
|
+
last_response.status.should eq 400
|
95
|
+
last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'validates against values in a proc' do
|
99
|
+
ValuesModel.add_value('valid-type4')
|
100
|
+
|
101
|
+
get('/lambda', type: 'valid-type4')
|
102
|
+
last_response.status.should eq 200
|
103
|
+
last_response.body.should eq({ type: "valid-type4" }.to_json)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'does not allow an invalid value for a parameter using lambda' do
|
107
|
+
get("/lambda", type: 'invalid-type')
|
108
|
+
last_response.status.should eq 400
|
109
|
+
last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'raises IncompatibleOptionValues on an invalid default value' do
|
113
|
+
subject = Class.new(Grape::API)
|
114
|
+
expect {
|
115
|
+
subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], default: 'invalid-type' }
|
116
|
+
}.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'raises IncompatibleOptionValues when type is incompatible with values array' do
|
120
|
+
subject = Class.new(Grape::API)
|
121
|
+
expect {
|
122
|
+
subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], type: Symbol }
|
123
|
+
}.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'allows values to be a kind of the coerced type not just an instance of it' do
|
127
|
+
get("/values/coercion", type: 10)
|
128
|
+
last_response.status.should eq 200
|
129
|
+
last_response.body.should eq({ type: 10 }.to_json)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'raises IncompatibleOptionValues when values contains a value that is not a kind of the type' do
|
133
|
+
subject = Class.new(Grape::API)
|
134
|
+
expect {
|
135
|
+
subject.params { requires :type, values: [10.5, 11], type: Integer }
|
136
|
+
}.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
137
|
+
end
|
138
|
+
end
|