grape 0.9.0 → 0.10.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -66
- data/.rubocop_todo.yml +78 -17
- data/.travis.yml +7 -3
- data/Appraisals +7 -0
- data/CHANGELOG.md +24 -0
- data/CONTRIBUTING.md +7 -0
- data/Gemfile +1 -7
- data/Guardfile +1 -1
- data/README.md +560 -94
- data/RELEASING.md +1 -1
- data/Rakefile +10 -11
- data/UPGRADING.md +211 -3
- data/gemfiles/rails_3.gemfile +14 -0
- data/gemfiles/rails_4.gemfile +14 -0
- data/grape.gemspec +10 -9
- data/lib/backports/active_support/deep_dup.rb +49 -0
- data/lib/backports/active_support/duplicable.rb +88 -0
- data/lib/grape.rb +29 -2
- data/lib/grape/api.rb +59 -65
- data/lib/grape/dsl/api.rb +19 -0
- data/lib/grape/dsl/callbacks.rb +6 -4
- data/lib/grape/dsl/configuration.rb +49 -5
- data/lib/grape/dsl/helpers.rb +7 -8
- data/lib/grape/dsl/inside_route.rb +22 -10
- data/lib/grape/dsl/middleware.rb +5 -5
- data/lib/grape/dsl/parameters.rb +6 -2
- data/lib/grape/dsl/request_response.rb +23 -20
- data/lib/grape/dsl/routing.rb +52 -49
- data/lib/grape/dsl/settings.rb +110 -0
- data/lib/grape/dsl/validations.rb +14 -6
- data/lib/grape/endpoint.rb +104 -88
- data/lib/grape/exceptions/base.rb +2 -2
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/missing_option.rb +1 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
- data/lib/grape/exceptions/unknown_options.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/validation.rb +1 -1
- data/lib/grape/exceptions/validation_errors.rb +2 -2
- data/lib/grape/formatter/serializable_hash.rb +1 -1
- data/lib/grape/formatter/xml.rb +1 -1
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/dsl.rb +26 -21
- data/lib/grape/middleware/auth/strategies.rb +1 -1
- data/lib/grape/middleware/auth/strategy_info.rb +0 -2
- data/lib/grape/middleware/base.rb +2 -2
- data/lib/grape/middleware/error.rb +1 -1
- data/lib/grape/middleware/formatter.rb +5 -5
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +3 -3
- data/lib/grape/middleware/versioner/param.rb +2 -2
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/path.rb +9 -3
- data/lib/grape/util/content_types.rb +16 -8
- data/lib/grape/util/inheritable_setting.rb +74 -0
- data/lib/grape/util/inheritable_values.rb +51 -0
- data/lib/grape/util/stackable_values.rb +52 -0
- data/lib/grape/util/strict_hash_configuration.rb +106 -0
- data/lib/grape/validations.rb +0 -220
- data/lib/grape/validations/attributes_iterator.rb +21 -0
- data/lib/grape/validations/params_scope.rb +176 -0
- data/lib/grape/validations/validators/all_or_none.rb +20 -0
- data/lib/grape/validations/validators/allow_blank.rb +30 -0
- data/lib/grape/validations/validators/at_least_one_of.rb +20 -0
- data/lib/grape/validations/validators/base.rb +37 -0
- data/lib/grape/validations/{coerce.rb → validators/coerce.rb} +3 -3
- data/lib/grape/validations/{default.rb → validators/default.rb} +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +20 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +26 -0
- data/lib/grape/validations/validators/mutual_exclusion.rb +25 -0
- data/lib/grape/validations/{presence.rb → validators/presence.rb} +2 -2
- data/lib/grape/validations/validators/regexp.rb +12 -0
- data/lib/grape/validations/validators/values.rb +26 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +522 -343
- data/spec/grape/dsl/callbacks_spec.rb +4 -4
- data/spec/grape/dsl/configuration_spec.rb +48 -9
- data/spec/grape/dsl/helpers_spec.rb +6 -13
- data/spec/grape/dsl/inside_route_spec.rb +43 -4
- data/spec/grape/dsl/middleware_spec.rb +1 -10
- data/spec/grape/dsl/parameters_spec.rb +8 -1
- data/spec/grape/dsl/request_response_spec.rb +16 -22
- data/spec/grape/dsl/routing_spec.rb +21 -5
- data/spec/grape/dsl/settings_spec.rb +219 -0
- data/spec/grape/dsl/validations_spec.rb +8 -11
- data/spec/grape/endpoint_spec.rb +115 -86
- data/spec/grape/entity_spec.rb +33 -33
- data/spec/grape/exceptions/invalid_formatter_spec.rb +3 -5
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +4 -6
- data/spec/grape/exceptions/missing_mime_type_spec.rb +5 -6
- data/spec/grape/exceptions/missing_option_spec.rb +3 -5
- data/spec/grape/exceptions/unknown_options_spec.rb +3 -5
- data/spec/grape/exceptions/unknown_validator_spec.rb +3 -5
- data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
- data/spec/grape/loading_spec.rb +44 -0
- data/spec/grape/middleware/auth/base_spec.rb +0 -4
- data/spec/grape/middleware/auth/dsl_spec.rb +2 -4
- data/spec/grape/middleware/auth/strategies_spec.rb +5 -6
- data/spec/grape/middleware/exception_spec.rb +8 -10
- data/spec/grape/middleware/formatter_spec.rb +13 -15
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +10 -10
- data/spec/grape/middleware/versioner/header_spec.rb +25 -25
- data/spec/grape/middleware/versioner/param_spec.rb +15 -17
- data/spec/grape/middleware/versioner/path_spec.rb +1 -2
- data/spec/grape/middleware/versioner_spec.rb +0 -1
- data/spec/grape/path_spec.rb +66 -45
- data/spec/grape/util/inheritable_setting_spec.rb +217 -0
- data/spec/grape/util/inheritable_values_spec.rb +63 -0
- data/spec/grape/util/stackable_values_spec.rb +115 -0
- data/spec/grape/util/strict_hash_configuration_spec.rb +38 -0
- data/spec/grape/validations/attributes_iterator_spec.rb +4 -0
- data/spec/grape/validations/params_scope_spec.rb +57 -0
- data/spec/grape/validations/validators/all_or_none_spec.rb +60 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +170 -0
- data/spec/grape/validations/{at_least_one_of_spec.rb → validators/at_least_one_of_spec.rb} +7 -3
- data/spec/grape/validations/{coerce_spec.rb → validators/coerce_spec.rb} +8 -11
- data/spec/grape/validations/{default_spec.rb → validators/default_spec.rb} +7 -9
- data/spec/grape/validations/{exactly_one_of_spec.rb → validators/exactly_one_of_spec.rb} +15 -11
- data/spec/grape/validations/{mutual_exclusion_spec.rb → validators/mutual_exclusion_spec.rb} +11 -9
- data/spec/grape/validations/{presence_spec.rb → validators/presence_spec.rb} +30 -30
- data/spec/grape/validations/{regexp_spec.rb → validators/regexp_spec.rb} +2 -4
- data/spec/grape/validations/{values_spec.rb → validators/values_spec.rb} +95 -23
- data/spec/grape/validations/{zh-CN.yml → validators/zh-CN.yml} +0 -0
- data/spec/grape/validations_spec.rb +335 -70
- data/spec/shared/versioning_examples.rb +7 -8
- data/spec/spec_helper.rb +2 -0
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- data/spec/support/content_type_helpers.rb +1 -1
- data/spec/support/versioned_helpers.rb +3 -3
- metadata +80 -33
- data/lib/grape/util/deep_merge.rb +0 -23
- data/lib/grape/util/hash_stack.rb +0 -120
- data/lib/grape/validations/at_least_one_of.rb +0 -25
- data/lib/grape/validations/exactly_one_of.rb +0 -26
- data/lib/grape/validations/mutual_exclusion.rb +0 -25
- data/lib/grape/validations/regexp.rb +0 -12
- data/lib/grape/validations/values.rb +0 -23
- data/spec/grape/util/hash_stack_spec.rb +0 -132
@@ -10,7 +10,6 @@ describe Grape::Validations::RegexpValidator do
|
|
10
10
|
requires :name, regexp: /^[a-z]+$/
|
11
11
|
end
|
12
12
|
get do
|
13
|
-
|
14
13
|
end
|
15
14
|
end
|
16
15
|
end
|
@@ -22,7 +21,7 @@ describe Grape::Validations::RegexpValidator do
|
|
22
21
|
|
23
22
|
context 'invalid input' do
|
24
23
|
it 'refuses inapppopriate' do
|
25
|
-
get '/', name:
|
24
|
+
get '/', name: 'invalid name'
|
26
25
|
expect(last_response.status).to eq(400)
|
27
26
|
end
|
28
27
|
|
@@ -33,8 +32,7 @@ describe Grape::Validations::RegexpValidator do
|
|
33
32
|
end
|
34
33
|
|
35
34
|
it 'accepts valid input' do
|
36
|
-
get '/', name:
|
35
|
+
get '/', name: 'bob'
|
37
36
|
expect(last_response.status).to eq(200)
|
38
37
|
end
|
39
|
-
|
40
38
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Grape::Validations::ValuesValidator do
|
4
|
-
|
5
4
|
class ValuesModel
|
6
5
|
DEFAULT_VALUES = ['valid-type1', 'valid-type2', 'valid-type3']
|
7
6
|
class << self
|
@@ -44,15 +43,36 @@ describe Grape::Validations::ValuesValidator do
|
|
44
43
|
end
|
45
44
|
|
46
45
|
params do
|
47
|
-
|
46
|
+
optional :type, values: ValuesModel.values, default: -> { ValuesModel.values.sample }
|
47
|
+
end
|
48
|
+
get '/default_lambda' do
|
49
|
+
{ type: params[:type] }
|
50
|
+
end
|
51
|
+
|
52
|
+
params do
|
53
|
+
optional :type, values: -> { ValuesModel.values }, default: -> { ValuesModel.values.sample }
|
54
|
+
end
|
55
|
+
get '/default_and_values_lambda' do
|
56
|
+
{ type: params[:type] }
|
57
|
+
end
|
58
|
+
|
59
|
+
params do
|
60
|
+
requires :type, type: Integer, desc: 'An integer', values: [10, 11], default: 10
|
48
61
|
end
|
49
62
|
get '/values/coercion' do
|
50
63
|
{ type: params[:type] }
|
51
64
|
end
|
52
65
|
|
66
|
+
params do
|
67
|
+
requires :type, type: Array[Integer], desc: 'An integer', values: [10, 11], default: 10
|
68
|
+
end
|
69
|
+
get '/values/array_coercion' do
|
70
|
+
{ type: params[:type] }
|
71
|
+
end
|
72
|
+
|
53
73
|
params do
|
54
74
|
optional :optional do
|
55
|
-
requires :type, values:
|
75
|
+
requires :type, values: %w(a b)
|
56
76
|
end
|
57
77
|
end
|
58
78
|
get '/optional_with_required_values'
|
@@ -65,22 +85,22 @@ describe Grape::Validations::ValuesValidator do
|
|
65
85
|
end
|
66
86
|
|
67
87
|
it 'allows a valid value for a parameter' do
|
68
|
-
get(
|
88
|
+
get('/', type: 'valid-type1')
|
69
89
|
expect(last_response.status).to eq 200
|
70
|
-
expect(last_response.body).to eq({ type:
|
90
|
+
expect(last_response.body).to eq({ type: 'valid-type1' }.to_json)
|
71
91
|
end
|
72
92
|
|
73
93
|
it 'does not allow an invalid value for a parameter' do
|
74
|
-
get(
|
94
|
+
get('/', type: 'invalid-type')
|
75
95
|
expect(last_response.status).to eq 400
|
76
|
-
expect(last_response.body).to eq({ error:
|
96
|
+
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
|
77
97
|
end
|
78
98
|
|
79
99
|
context 'nil value for a parameter' do
|
80
100
|
it 'does not allow for root params scope' do
|
81
|
-
get(
|
101
|
+
get('/', type: nil)
|
82
102
|
expect(last_response.status).to eq 400
|
83
|
-
expect(last_response.body).to eq({ error:
|
103
|
+
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
|
84
104
|
end
|
85
105
|
|
86
106
|
it 'allows for a required param in child scope' do
|
@@ -90,15 +110,15 @@ describe Grape::Validations::ValuesValidator do
|
|
90
110
|
end
|
91
111
|
|
92
112
|
it 'allows a valid default value' do
|
93
|
-
get(
|
113
|
+
get('/default/valid')
|
94
114
|
expect(last_response.status).to eq 200
|
95
|
-
expect(last_response.body).to eq({ type:
|
115
|
+
expect(last_response.body).to eq({ type: 'valid-type2' }.to_json)
|
96
116
|
end
|
97
117
|
|
98
118
|
it 'allows a proc for values' do
|
99
119
|
get('/lambda', type: 'valid-type1')
|
100
120
|
expect(last_response.status).to eq 200
|
101
|
-
expect(last_response.body).to eq({ type:
|
121
|
+
expect(last_response.body).to eq({ type: 'valid-type1' }.to_json)
|
102
122
|
end
|
103
123
|
|
104
124
|
it 'does not validate updated values without proc' do
|
@@ -106,7 +126,7 @@ describe Grape::Validations::ValuesValidator do
|
|
106
126
|
|
107
127
|
get('/', type: 'valid-type4')
|
108
128
|
expect(last_response.status).to eq 400
|
109
|
-
expect(last_response.body).to eq({ error:
|
129
|
+
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
|
110
130
|
end
|
111
131
|
|
112
132
|
it 'validates against values in a proc' do
|
@@ -114,39 +134,91 @@ describe Grape::Validations::ValuesValidator do
|
|
114
134
|
|
115
135
|
get('/lambda', type: 'valid-type4')
|
116
136
|
expect(last_response.status).to eq 200
|
117
|
-
expect(last_response.body).to eq({ type:
|
137
|
+
expect(last_response.body).to eq({ type: 'valid-type4' }.to_json)
|
118
138
|
end
|
119
139
|
|
120
140
|
it 'does not allow an invalid value for a parameter using lambda' do
|
121
|
-
get(
|
141
|
+
get('/lambda', type: 'invalid-type')
|
122
142
|
expect(last_response.status).to eq 400
|
123
|
-
expect(last_response.body).to eq({ error:
|
143
|
+
expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'validates default value from proc' do
|
147
|
+
get('/default_lambda')
|
148
|
+
expect(last_response.status).to eq 200
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'validates default value from proc against values in a proc' do
|
152
|
+
get('/default_and_values_lambda')
|
153
|
+
expect(last_response.status).to eq 200
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'raises IncompatibleOptionValues on an invalid default value from proc' do
|
157
|
+
subject = Class.new(Grape::API)
|
158
|
+
expect do
|
159
|
+
subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], default: ValuesModel.values.sample + '_invalid' }
|
160
|
+
end.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
124
161
|
end
|
125
162
|
|
126
163
|
it 'raises IncompatibleOptionValues on an invalid default value' do
|
127
164
|
subject = Class.new(Grape::API)
|
128
|
-
expect
|
165
|
+
expect do
|
129
166
|
subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], default: 'invalid-type' }
|
130
|
-
|
167
|
+
end.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
131
168
|
end
|
132
169
|
|
133
170
|
it 'raises IncompatibleOptionValues when type is incompatible with values array' do
|
134
171
|
subject = Class.new(Grape::API)
|
135
|
-
expect
|
172
|
+
expect do
|
136
173
|
subject.params { optional :type, values: ['valid-type1', 'valid-type2', 'valid-type3'], type: Symbol }
|
137
|
-
|
174
|
+
end.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
138
175
|
end
|
139
176
|
|
140
177
|
it 'allows values to be a kind of the coerced type not just an instance of it' do
|
141
|
-
get(
|
178
|
+
get('/values/coercion', type: 10)
|
142
179
|
expect(last_response.status).to eq 200
|
143
180
|
expect(last_response.body).to eq({ type: 10 }.to_json)
|
144
181
|
end
|
145
182
|
|
183
|
+
it 'allows values to be a kind of the coerced type in an array' do
|
184
|
+
get('/values/array_coercion', type: [10])
|
185
|
+
expect(last_response.status).to eq 200
|
186
|
+
expect(last_response.body).to eq({ type: [10] }.to_json)
|
187
|
+
end
|
188
|
+
|
146
189
|
it 'raises IncompatibleOptionValues when values contains a value that is not a kind of the type' do
|
147
190
|
subject = Class.new(Grape::API)
|
148
|
-
expect
|
191
|
+
expect do
|
149
192
|
subject.params { requires :type, values: [10.5, 11], type: Integer }
|
150
|
-
|
193
|
+
end.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'with a lambda values' do
|
197
|
+
subject do
|
198
|
+
Class.new(Grape::API) do
|
199
|
+
params do
|
200
|
+
optional :type, type: String, values: -> { [SecureRandom.uuid] }, default: -> { SecureRandom.uuid }
|
201
|
+
end
|
202
|
+
get '/random_values'
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def app
|
207
|
+
subject
|
208
|
+
end
|
209
|
+
|
210
|
+
before do
|
211
|
+
expect(SecureRandom).to receive(:uuid).and_return('foo').once
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'only evaluates values dynamically with each request' do
|
215
|
+
get '/random_values', type: 'foo'
|
216
|
+
expect(last_response.status).to eq 200
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'chooses default' do
|
220
|
+
get '/random_values'
|
221
|
+
expect(last_response.status).to eq 200
|
222
|
+
end
|
151
223
|
end
|
152
224
|
end
|
File without changes
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Grape::Validations do
|
4
|
-
|
5
4
|
subject { Class.new(Grape::API) }
|
6
5
|
|
7
6
|
def app
|
@@ -44,18 +43,17 @@ describe Grape::Validations do
|
|
44
43
|
subject.params do
|
45
44
|
optional :some_param
|
46
45
|
end
|
47
|
-
expect(subject.
|
46
|
+
expect(subject.route_setting(:declared_params)).to eq([:some_param])
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
51
50
|
context 'required' do
|
52
51
|
before do
|
53
52
|
subject.params do
|
54
|
-
requires :key
|
55
|
-
end
|
56
|
-
subject.get '/required' do
|
57
|
-
'required works'
|
53
|
+
requires :key, type: String
|
58
54
|
end
|
55
|
+
subject.get('/required') { 'required works' }
|
56
|
+
subject.put('/required') { { key: params[:key] }.to_json }
|
59
57
|
end
|
60
58
|
|
61
59
|
it 'errors when param not present' do
|
@@ -74,7 +72,13 @@ describe Grape::Validations do
|
|
74
72
|
subject.params do
|
75
73
|
requires :some_param
|
76
74
|
end
|
77
|
-
expect(subject.
|
75
|
+
expect(subject.route_setting(:declared_params)).to eq([:some_param])
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'works when required field is present but nil' do
|
79
|
+
put '/required', { key: nil }.to_json, 'CONTENT_TYPE' => 'application/json'
|
80
|
+
expect(last_response.status).to eq(200)
|
81
|
+
expect(JSON.parse(last_response.body)).to eq('key' => nil)
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
@@ -97,7 +101,7 @@ describe Grape::Validations do
|
|
97
101
|
|
98
102
|
it 'adds entity documentation to declared params' do
|
99
103
|
define_requires_all
|
100
|
-
expect(subject.
|
104
|
+
expect(subject.route_setting(:declared_params)).to eq([:required_field, :optional_field])
|
101
105
|
end
|
102
106
|
|
103
107
|
it 'errors when required_field is not present' do
|
@@ -132,7 +136,7 @@ describe Grape::Validations do
|
|
132
136
|
|
133
137
|
it 'adds entity documentation to declared params' do
|
134
138
|
define_requires_none
|
135
|
-
expect(subject.
|
139
|
+
expect(subject.route_setting(:declared_params)).to eq([:required_field, :optional_field])
|
136
140
|
end
|
137
141
|
|
138
142
|
it 'errors when required_field is not present' do
|
@@ -162,7 +166,7 @@ describe Grape::Validations do
|
|
162
166
|
|
163
167
|
it 'adds only the entity documentation to declared params, nothing more' do
|
164
168
|
define_requires_all
|
165
|
-
expect(subject.
|
169
|
+
expect(subject.route_setting(:declared_params)).to eq([:required_field, :optional_field])
|
166
170
|
end
|
167
171
|
end
|
168
172
|
|
@@ -190,9 +194,8 @@ describe Grape::Validations do
|
|
190
194
|
requires :key
|
191
195
|
end
|
192
196
|
end
|
193
|
-
subject.get
|
194
|
-
|
195
|
-
end
|
197
|
+
subject.get('/required') { 'required works' }
|
198
|
+
subject.put('/required') { { items: params[:items] }.to_json }
|
196
199
|
end
|
197
200
|
|
198
201
|
it 'errors when param not present' do
|
@@ -201,8 +204,8 @@ describe Grape::Validations do
|
|
201
204
|
expect(last_response.body).to eq('items is missing')
|
202
205
|
end
|
203
206
|
|
204
|
-
it
|
205
|
-
get '/required', items:
|
207
|
+
it 'errors when param is not an Array' do
|
208
|
+
get '/required', items: 'hello'
|
206
209
|
expect(last_response.status).to eq(400)
|
207
210
|
expect(last_response.body).to eq('items is invalid, items[key] is missing')
|
208
211
|
|
@@ -217,14 +220,10 @@ describe Grape::Validations do
|
|
217
220
|
expect(last_response.body).to eq('required works')
|
218
221
|
end
|
219
222
|
|
220
|
-
it "doesn't
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
requires :key
|
225
|
-
end
|
226
|
-
end
|
227
|
-
}.to raise_error ArgumentError
|
223
|
+
it "doesn't throw a missing param when param is present but empty" do
|
224
|
+
put '/required', { items: [] }.to_json, 'CONTENT_TYPE' => 'application/json'
|
225
|
+
expect(last_response.status).to eq(200)
|
226
|
+
expect(JSON.parse(last_response.body)).to eq('items' => [])
|
228
227
|
end
|
229
228
|
|
230
229
|
it 'adds to declared parameters' do
|
@@ -233,7 +232,7 @@ describe Grape::Validations do
|
|
233
232
|
requires :key
|
234
233
|
end
|
235
234
|
end
|
236
|
-
expect(subject.
|
235
|
+
expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
|
237
236
|
end
|
238
237
|
end
|
239
238
|
|
@@ -255,8 +254,8 @@ describe Grape::Validations do
|
|
255
254
|
expect(last_response.body).to eq('items is missing, items[key] is missing')
|
256
255
|
end
|
257
256
|
|
258
|
-
it
|
259
|
-
get '/required', items:
|
257
|
+
it 'errors when param is not a Hash' do
|
258
|
+
get '/required', items: 'hello'
|
260
259
|
expect(last_response.status).to eq(400)
|
261
260
|
expect(last_response.body).to eq('items is invalid, items[key] is missing')
|
262
261
|
|
@@ -271,23 +270,13 @@ describe Grape::Validations do
|
|
271
270
|
expect(last_response.body).to eq('required works')
|
272
271
|
end
|
273
272
|
|
274
|
-
it "doesn't allow any key in the options hash other than type" do
|
275
|
-
expect {
|
276
|
-
subject.params do
|
277
|
-
requires(:items, desc: 'Foo') do
|
278
|
-
requires :key
|
279
|
-
end
|
280
|
-
end
|
281
|
-
}.to raise_error ArgumentError
|
282
|
-
end
|
283
|
-
|
284
273
|
it 'adds to declared parameters' do
|
285
274
|
subject.params do
|
286
275
|
requires :items do
|
287
276
|
requires :key
|
288
277
|
end
|
289
278
|
end
|
290
|
-
expect(subject.
|
279
|
+
expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
|
291
280
|
end
|
292
281
|
end
|
293
282
|
|
@@ -321,7 +310,85 @@ describe Grape::Validations do
|
|
321
310
|
requires :key
|
322
311
|
end
|
323
312
|
end
|
324
|
-
expect(subject.
|
313
|
+
expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
context 'group params with nested params which has a type' do
|
318
|
+
let(:invalid_items){ { items: '' } }
|
319
|
+
|
320
|
+
before do
|
321
|
+
subject.params do
|
322
|
+
optional :items do
|
323
|
+
optional :key1, type: String
|
324
|
+
optional :key2, type: String
|
325
|
+
end
|
326
|
+
end
|
327
|
+
subject.post '/group_with_nested' do
|
328
|
+
'group with nested works'
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'errors when group param is invalid'do
|
333
|
+
post '/group_with_nested', items: invalid_items
|
334
|
+
expect(last_response.status).to eq(400)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context 'custom validator for a Hash' do
|
339
|
+
module DateRangeValidations
|
340
|
+
class DateRangeValidator < Grape::Validations::Base
|
341
|
+
def validate_param!(attr_name, params)
|
342
|
+
unless params[attr_name][:from] <= params[attr_name][:to]
|
343
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "'from' must be lower or equal to 'to'"
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
before do
|
350
|
+
subject.params do
|
351
|
+
optional :date_range, date_range: true, type: Hash do
|
352
|
+
requires :from, type: Integer
|
353
|
+
requires :to, type: Integer
|
354
|
+
end
|
355
|
+
end
|
356
|
+
subject.get('/optional') do
|
357
|
+
'optional works'
|
358
|
+
end
|
359
|
+
subject.params do
|
360
|
+
requires :date_range, date_range: true, type: Hash do
|
361
|
+
requires :from, type: Integer
|
362
|
+
requires :to, type: Integer
|
363
|
+
end
|
364
|
+
end
|
365
|
+
subject.get('/required') do
|
366
|
+
'required works'
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
context 'which is optional' do
|
371
|
+
it "doesn't throw an error if the validation passes" do
|
372
|
+
get '/optional', date_range: { from: 1, to: 2 }
|
373
|
+
expect(last_response.status).to eq(200)
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'errors if the validation fails' do
|
377
|
+
get '/optional', date_range: { from: 2, to: 1 }
|
378
|
+
expect(last_response.status).to eq(400)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
context 'which is required' do
|
383
|
+
it "doesn't throw an error if the validation passes" do
|
384
|
+
get '/required', date_range: { from: 1, to: 2 }
|
385
|
+
expect(last_response.status).to eq(200)
|
386
|
+
end
|
387
|
+
|
388
|
+
it 'errors if the validation fails' do
|
389
|
+
get '/required', date_range: { from: 2, to: 1 }
|
390
|
+
expect(last_response.status).to eq(400)
|
391
|
+
end
|
325
392
|
end
|
326
393
|
end
|
327
394
|
|
@@ -371,9 +438,9 @@ describe Grape::Validations do
|
|
371
438
|
expect(last_response.body).to eq('children[parents] is missing')
|
372
439
|
end
|
373
440
|
|
374
|
-
it
|
441
|
+
it 'errors when param is not an Array' do
|
375
442
|
# NOTE: would be nicer if these just returned 'children is invalid'
|
376
|
-
get '/within_array', children:
|
443
|
+
get '/within_array', children: 'hello'
|
377
444
|
expect(last_response.status).to eq(400)
|
378
445
|
expect(last_response.body).to eq('children is invalid, children[name] is missing, children[parents] is missing, children[parents] is invalid, children[parents][name] is missing')
|
379
446
|
|
@@ -428,7 +495,7 @@ describe Grape::Validations do
|
|
428
495
|
end
|
429
496
|
|
430
497
|
it 'requires defaults to Array type' do
|
431
|
-
get '/req', planets:
|
498
|
+
get '/req', planets: 'Jupiter, Saturn'
|
432
499
|
expect(last_response.status).to eq(400)
|
433
500
|
expect(last_response.body).to eq('planets is invalid, planets[name] is missing')
|
434
501
|
|
@@ -444,26 +511,26 @@ describe Grape::Validations do
|
|
444
511
|
end
|
445
512
|
|
446
513
|
it 'optional defaults to Array type' do
|
447
|
-
get '/opt', name:
|
514
|
+
get '/opt', name: 'Jupiter', moons: 'Europa, Ganymede'
|
448
515
|
expect(last_response.status).to eq(400)
|
449
516
|
expect(last_response.body).to eq('moons is invalid, moons[name] is missing')
|
450
517
|
|
451
|
-
get '/opt', name:
|
518
|
+
get '/opt', name: 'Jupiter', moons: { name: 'Ganymede' }
|
452
519
|
expect(last_response.status).to eq(400)
|
453
520
|
expect(last_response.body).to eq('moons is invalid')
|
454
521
|
|
455
|
-
get '/opt', name:
|
522
|
+
get '/opt', name: 'Jupiter', moons: [{ name: 'Io' }, { name: 'Callisto' }]
|
456
523
|
expect(last_response.status).to eq(200)
|
457
524
|
|
458
|
-
put_with_json '/opt', name:
|
525
|
+
put_with_json '/opt', name: 'Venus'
|
459
526
|
expect(last_response.status).to eq(200)
|
460
527
|
|
461
|
-
put_with_json '/opt', name:
|
528
|
+
put_with_json '/opt', name: 'Mercury', moons: []
|
462
529
|
expect(last_response.status).to eq(200)
|
463
530
|
end
|
464
531
|
|
465
532
|
it 'group defaults to Array type' do
|
466
|
-
get '/grp', stars:
|
533
|
+
get '/grp', stars: 'Sun'
|
467
534
|
expect(last_response.status).to eq(400)
|
468
535
|
expect(last_response.body).to eq('stars is invalid, stars[name] is missing')
|
469
536
|
|
@@ -545,14 +612,14 @@ describe Grape::Validations do
|
|
545
612
|
expect(last_response.body).to eq('optional group works')
|
546
613
|
end
|
547
614
|
|
548
|
-
it
|
615
|
+
it 'errors when group is present, but required param is not' do
|
549
616
|
get '/optional_group', items: [{ not_key: 'foo' }]
|
550
617
|
expect(last_response.status).to eq(400)
|
551
618
|
expect(last_response.body).to eq('items[key] is missing')
|
552
619
|
end
|
553
620
|
|
554
621
|
it "errors when param is present but isn't an Array" do
|
555
|
-
get '/optional_group', items:
|
622
|
+
get '/optional_group', items: 'hello'
|
556
623
|
expect(last_response.status).to eq(400)
|
557
624
|
expect(last_response.body).to eq('items is invalid, items[key] is missing')
|
558
625
|
|
@@ -567,7 +634,7 @@ describe Grape::Validations do
|
|
567
634
|
requires :key
|
568
635
|
end
|
569
636
|
end
|
570
|
-
expect(subject.
|
637
|
+
expect(subject.route_setting(:declared_params)).to eq([items: [:key]])
|
571
638
|
end
|
572
639
|
end
|
573
640
|
|
@@ -631,7 +698,7 @@ describe Grape::Validations do
|
|
631
698
|
requires(:required_subitems) { requires :value }
|
632
699
|
end
|
633
700
|
end
|
634
|
-
expect(subject.
|
701
|
+
expect(subject.route_setting(:declared_params)).to eq([items: [:key, { optional_subitems: [:value] }, { required_subitems: [:value] }]])
|
635
702
|
end
|
636
703
|
end
|
637
704
|
|
@@ -656,10 +723,10 @@ describe Grape::Validations do
|
|
656
723
|
|
657
724
|
context 'custom validation' do
|
658
725
|
module CustomValidations
|
659
|
-
class Customvalidator < Grape::Validations::
|
726
|
+
class Customvalidator < Grape::Validations::Base
|
660
727
|
def validate_param!(attr_name, params)
|
661
728
|
unless params[attr_name] == 'im custom'
|
662
|
-
|
729
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: 'is not custom!'
|
663
730
|
end
|
664
731
|
end
|
665
732
|
end
|
@@ -776,13 +843,13 @@ describe Grape::Validations do
|
|
776
843
|
expect(last_response.body).to eq('custom is not custom!')
|
777
844
|
end
|
778
845
|
|
779
|
-
specify 'the nested
|
846
|
+
specify 'the nested namespace inherits the custom validator' do
|
780
847
|
get '/nested/nested/two', custom: 'im wrong, validate me'
|
781
848
|
expect(last_response.status).to eq(400)
|
782
849
|
expect(last_response.body).to eq('custom is not custom!')
|
783
850
|
end
|
784
851
|
|
785
|
-
specify 'peer
|
852
|
+
specify 'peer namespaces does not have the validator' do
|
786
853
|
get '/peer/one', custom: 'im not validated'
|
787
854
|
expect(last_response.status).to eq(200)
|
788
855
|
expect(last_response.body).to eq('no validation required')
|
@@ -805,10 +872,10 @@ describe Grape::Validations do
|
|
805
872
|
|
806
873
|
context 'when using options on param' do
|
807
874
|
module CustomValidations
|
808
|
-
class CustomvalidatorWithOptions < Grape::Validations::
|
875
|
+
class CustomvalidatorWithOptions < Grape::Validations::Base
|
809
876
|
def validate_param!(attr_name, params)
|
810
877
|
unless params[attr_name] == @option[:text]
|
811
|
-
|
878
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: @option[:error_message]
|
812
879
|
end
|
813
880
|
end
|
814
881
|
end
|
@@ -816,7 +883,7 @@ describe Grape::Validations do
|
|
816
883
|
|
817
884
|
before do
|
818
885
|
subject.params do
|
819
|
-
optional :custom, customvalidator_with_options: { text: 'im custom with options', error_message:
|
886
|
+
optional :custom, customvalidator_with_options: { text: 'im custom with options', error_message: 'is not custom with options!' }
|
820
887
|
end
|
821
888
|
subject.get '/optional_custom' do
|
822
889
|
'optional with custom works!'
|
@@ -877,16 +944,15 @@ describe Grape::Validations do
|
|
877
944
|
subject.params do
|
878
945
|
use :pagination
|
879
946
|
end
|
880
|
-
expect(subject.
|
947
|
+
expect(subject.route_setting(:declared_params)).to eq [:page, :per_page]
|
881
948
|
end
|
882
949
|
|
883
950
|
it 'by #use with multiple params' do
|
884
951
|
subject.params do
|
885
952
|
use :pagination, :period
|
886
953
|
end
|
887
|
-
expect(subject.
|
954
|
+
expect(subject.route_setting(:declared_params)).to eq [:page, :per_page, :start_date, :end_date]
|
888
955
|
end
|
889
|
-
|
890
956
|
end
|
891
957
|
|
892
958
|
context 'with block' do
|
@@ -960,7 +1026,7 @@ describe Grape::Validations do
|
|
960
1026
|
|
961
1027
|
get '/mutually_exclusive', beer: 'string', wine: 'anotherstring'
|
962
1028
|
expect(last_response.status).to eq(400)
|
963
|
-
expect(last_response.body).to eq
|
1029
|
+
expect(last_response.body).to eq 'beer, wine are mutually exclusive'
|
964
1030
|
end
|
965
1031
|
end
|
966
1032
|
|
@@ -970,17 +1036,62 @@ describe Grape::Validations do
|
|
970
1036
|
optional :beer
|
971
1037
|
optional :wine
|
972
1038
|
mutually_exclusive :beer, :wine
|
973
|
-
optional :
|
974
|
-
|
975
|
-
|
1039
|
+
optional :nested, type: Hash do
|
1040
|
+
optional :scotch
|
1041
|
+
optional :aquavit
|
1042
|
+
mutually_exclusive :scotch, :aquavit
|
1043
|
+
end
|
1044
|
+
optional :nested2, type: Array do
|
1045
|
+
optional :scotch2
|
1046
|
+
optional :aquavit2
|
1047
|
+
mutually_exclusive :scotch2, :aquavit2
|
1048
|
+
end
|
976
1049
|
end
|
977
1050
|
subject.get '/mutually_exclusive' do
|
978
1051
|
'mutually_exclusive works!'
|
979
1052
|
end
|
980
1053
|
|
981
|
-
get '/mutually_exclusive', beer: 'true', wine: 'true', scotch: 'true', aquavit: 'true'
|
1054
|
+
get '/mutually_exclusive', beer: 'true', wine: 'true', nested: { scotch: 'true', aquavit: 'true' }, nested2: [{ scotch2: 'true' }, { scotch2: 'true', aquavit2: 'true' }]
|
1055
|
+
expect(last_response.status).to eq(400)
|
1056
|
+
expect(last_response.body).to eq 'beer, wine are mutually exclusive, scotch, aquavit are mutually exclusive, scotch2, aquavit2 are mutually exclusive'
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
context 'in a group' do
|
1061
|
+
it 'works when only one from the set is present' do
|
1062
|
+
subject.params do
|
1063
|
+
group :drink, type: Hash do
|
1064
|
+
optional :wine
|
1065
|
+
optional :beer
|
1066
|
+
optional :juice
|
1067
|
+
|
1068
|
+
mutually_exclusive :beer, :wine, :juice
|
1069
|
+
end
|
1070
|
+
end
|
1071
|
+
subject.get '/mutually_exclusive_group' do
|
1072
|
+
'mutually_exclusive_group works!'
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
get '/mutually_exclusive_group', drink: { beer: 'true' }
|
1076
|
+
expect(last_response.status).to eq(200)
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
it 'errors when more than one from the set is present' do
|
1080
|
+
subject.params do
|
1081
|
+
group :drink, type: Hash do
|
1082
|
+
optional :wine
|
1083
|
+
optional :beer
|
1084
|
+
optional :juice
|
1085
|
+
|
1086
|
+
mutually_exclusive :beer, :wine, :juice
|
1087
|
+
end
|
1088
|
+
end
|
1089
|
+
subject.get '/mutually_exclusive_group' do
|
1090
|
+
'mutually_exclusive_group works!'
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
get '/mutually_exclusive_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
|
982
1094
|
expect(last_response.status).to eq(400)
|
983
|
-
expect(last_response.body).to eq "beer, wine are mutually exclusive, scotch, aquavit are mutually exclusive"
|
984
1095
|
end
|
985
1096
|
end
|
986
1097
|
end
|
@@ -1002,7 +1113,7 @@ describe Grape::Validations do
|
|
1002
1113
|
it 'errors when none are present' do
|
1003
1114
|
get '/exactly_one_of'
|
1004
1115
|
expect(last_response.status).to eq(400)
|
1005
|
-
expect(last_response.body).to eq
|
1116
|
+
expect(last_response.body).to eq 'beer, wine, juice are missing, exactly one parameter must be provided'
|
1006
1117
|
end
|
1007
1118
|
|
1008
1119
|
it 'succeeds when one is present' do
|
@@ -1014,7 +1125,47 @@ describe Grape::Validations do
|
|
1014
1125
|
it 'errors when two or more are present' do
|
1015
1126
|
get '/exactly_one_of', beer: 'string', wine: 'anotherstring'
|
1016
1127
|
expect(last_response.status).to eq(400)
|
1017
|
-
expect(last_response.body).to eq
|
1128
|
+
expect(last_response.body).to eq 'beer, wine are mutually exclusive'
|
1129
|
+
end
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
context 'nested params' do
|
1133
|
+
before :each do
|
1134
|
+
subject.params do
|
1135
|
+
requires :nested, type: Hash do
|
1136
|
+
optional :beer_nested
|
1137
|
+
optional :wine_nested
|
1138
|
+
optional :juice_nested
|
1139
|
+
exactly_one_of :beer_nested, :wine_nested, :juice_nested
|
1140
|
+
end
|
1141
|
+
optional :nested2, type: Array do
|
1142
|
+
optional :beer_nested2
|
1143
|
+
optional :wine_nested2
|
1144
|
+
optional :juice_nested2
|
1145
|
+
exactly_one_of :beer_nested2, :wine_nested2, :juice_nested2
|
1146
|
+
end
|
1147
|
+
end
|
1148
|
+
subject.get '/exactly_one_of_nested' do
|
1149
|
+
'exactly_one_of works!'
|
1150
|
+
end
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
it 'errors when none are present' do
|
1154
|
+
get '/exactly_one_of_nested'
|
1155
|
+
expect(last_response.status).to eq(400)
|
1156
|
+
expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, exactly one parameter must be provided'
|
1157
|
+
end
|
1158
|
+
|
1159
|
+
it 'succeeds when one is present' do
|
1160
|
+
get '/exactly_one_of_nested', nested: { beer_nested: 'string' }
|
1161
|
+
expect(last_response.status).to eq(200)
|
1162
|
+
expect(last_response.body).to eq 'exactly_one_of works!'
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
it 'errors when two or more are present' do
|
1166
|
+
get '/exactly_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'anotherstring' }]
|
1167
|
+
expect(last_response.status).to eq(400)
|
1168
|
+
expect(last_response.body).to eq 'beer_nested2, wine_nested2 are mutually exclusive'
|
1018
1169
|
end
|
1019
1170
|
end
|
1020
1171
|
end
|
@@ -1036,7 +1187,7 @@ describe Grape::Validations do
|
|
1036
1187
|
it 'errors when none are present' do
|
1037
1188
|
get '/at_least_one_of'
|
1038
1189
|
expect(last_response.status).to eq(400)
|
1039
|
-
expect(last_response.body).to eq
|
1190
|
+
expect(last_response.body).to eq 'beer, wine, juice are missing, at least one parameter must be provided'
|
1040
1191
|
end
|
1041
1192
|
|
1042
1193
|
it 'does not error when one is present' do
|
@@ -1051,6 +1202,120 @@ describe Grape::Validations do
|
|
1051
1202
|
expect(last_response.body).to eq 'at_least_one_of works!'
|
1052
1203
|
end
|
1053
1204
|
end
|
1205
|
+
|
1206
|
+
context 'nested params' do
|
1207
|
+
before :each do
|
1208
|
+
subject.params do
|
1209
|
+
requires :nested, type: Hash do
|
1210
|
+
optional :beer_nested
|
1211
|
+
optional :wine_nested
|
1212
|
+
optional :juice_nested
|
1213
|
+
at_least_one_of :beer_nested, :wine_nested, :juice_nested
|
1214
|
+
end
|
1215
|
+
optional :nested2, type: Array do
|
1216
|
+
optional :beer_nested2
|
1217
|
+
optional :wine_nested2
|
1218
|
+
optional :juice_nested2
|
1219
|
+
at_least_one_of :beer_nested2, :wine_nested2, :juice_nested2
|
1220
|
+
end
|
1221
|
+
end
|
1222
|
+
subject.get '/at_least_one_of_nested' do
|
1223
|
+
'at_least_one_of works!'
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
it 'errors when none are present' do
|
1228
|
+
get '/at_least_one_of_nested'
|
1229
|
+
expect(last_response.status).to eq(400)
|
1230
|
+
expect(last_response.body).to eq 'nested is missing, beer_nested, wine_nested, juice_nested are missing, at least one parameter must be provided'
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
it 'does not error when one is present' do
|
1234
|
+
get '/at_least_one_of_nested', nested: { beer_nested: 'string' }, nested2: [{ beer_nested2: 'string' }]
|
1235
|
+
expect(last_response.status).to eq(200)
|
1236
|
+
expect(last_response.body).to eq 'at_least_one_of works!'
|
1237
|
+
end
|
1238
|
+
|
1239
|
+
it 'does not error when two are present' do
|
1240
|
+
get '/at_least_one_of_nested', nested: { beer_nested: 'string', wine_nested: 'string' }, nested2: [{ beer_nested2: 'string', wine_nested2: 'string' }]
|
1241
|
+
expect(last_response.status).to eq(200)
|
1242
|
+
expect(last_response.body).to eq 'at_least_one_of works!'
|
1243
|
+
end
|
1244
|
+
end
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
context 'in a group' do
|
1248
|
+
it 'works when only one from the set is present' do
|
1249
|
+
subject.params do
|
1250
|
+
group :drink, type: Hash do
|
1251
|
+
optional :wine
|
1252
|
+
optional :beer
|
1253
|
+
optional :juice
|
1254
|
+
|
1255
|
+
exactly_one_of :beer, :wine, :juice
|
1256
|
+
end
|
1257
|
+
end
|
1258
|
+
subject.get '/exactly_one_of_group' do
|
1259
|
+
'exactly_one_of_group works!'
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
get '/exactly_one_of_group', drink: { beer: 'true' }
|
1263
|
+
expect(last_response.status).to eq(200)
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
it 'errors when no parameter from the set is present' do
|
1267
|
+
subject.params do
|
1268
|
+
group :drink, type: Hash do
|
1269
|
+
optional :wine
|
1270
|
+
optional :beer
|
1271
|
+
optional :juice
|
1272
|
+
|
1273
|
+
exactly_one_of :beer, :wine, :juice
|
1274
|
+
end
|
1275
|
+
end
|
1276
|
+
subject.get '/exactly_one_of_group' do
|
1277
|
+
'exactly_one_of_group works!'
|
1278
|
+
end
|
1279
|
+
|
1280
|
+
get '/exactly_one_of_group', drink: {}
|
1281
|
+
expect(last_response.status).to eq(400)
|
1282
|
+
end
|
1283
|
+
|
1284
|
+
it 'errors when more than one from the set is present' do
|
1285
|
+
subject.params do
|
1286
|
+
group :drink, type: Hash do
|
1287
|
+
optional :wine
|
1288
|
+
optional :beer
|
1289
|
+
optional :juice
|
1290
|
+
|
1291
|
+
exactly_one_of :beer, :wine, :juice
|
1292
|
+
end
|
1293
|
+
end
|
1294
|
+
subject.get '/exactly_one_of_group' do
|
1295
|
+
'exactly_one_of_group works!'
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
get '/exactly_one_of_group', drink: { beer: 'true', juice: 'true', wine: 'true' }
|
1299
|
+
expect(last_response.status).to eq(400)
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
it 'does not falsely think the param is there if it is provided outside the block' do
|
1303
|
+
subject.params do
|
1304
|
+
group :drink, type: Hash do
|
1305
|
+
optional :wine
|
1306
|
+
optional :beer
|
1307
|
+
optional :juice
|
1308
|
+
|
1309
|
+
exactly_one_of :beer, :wine, :juice
|
1310
|
+
end
|
1311
|
+
end
|
1312
|
+
subject.get '/exactly_one_of_group' do
|
1313
|
+
'exactly_one_of_group works!'
|
1314
|
+
end
|
1315
|
+
|
1316
|
+
get '/exactly_one_of_group', drink: { foo: 'bar' }, beer: 'true'
|
1317
|
+
expect(last_response.status).to eq(400)
|
1318
|
+
end
|
1054
1319
|
end
|
1055
1320
|
end
|
1056
1321
|
end
|