grape 0.12.0 → 0.13.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_todo.yml +2 -2
- data/CHANGELOG.md +237 -215
- data/CONTRIBUTING.md +4 -4
- data/README.md +133 -10
- data/RELEASING.md +14 -6
- data/Rakefile +1 -1
- data/UPGRADING.md +23 -23
- data/grape.gemspec +1 -3
- data/lib/grape/api.rb +24 -4
- data/lib/grape/dsl/callbacks.rb +20 -0
- data/lib/grape/dsl/configuration.rb +54 -0
- data/lib/grape/dsl/inside_route.rb +33 -1
- data/lib/grape/dsl/parameters.rb +80 -0
- data/lib/grape/dsl/routing.rb +14 -0
- data/lib/grape/dsl/settings.rb +36 -1
- data/lib/grape/dsl/validations.rb +7 -5
- data/lib/grape/endpoint.rb +42 -32
- data/lib/grape/exceptions/unknown_parameter.rb +10 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -3
- data/lib/grape/http/headers.rb +0 -1
- data/lib/grape/http/request.rb +12 -4
- data/lib/grape/locale/en.yml +1 -0
- data/lib/grape/middleware/base.rb +1 -0
- data/lib/grape/middleware/formatter.rb +39 -23
- data/lib/grape/namespace.rb +13 -2
- data/lib/grape/path.rb +1 -0
- data/lib/grape/route.rb +5 -0
- data/lib/grape/util/file_response.rb +21 -0
- data/lib/grape/util/inheritable_setting.rb +23 -2
- data/lib/grape/util/inheritable_values.rb +1 -1
- data/lib/grape/util/parameter_types.rb +58 -0
- data/lib/grape/util/stackable_values.rb +5 -2
- data/lib/grape/validations/params_scope.rb +83 -9
- data/lib/grape/validations/validators/coerce.rb +11 -2
- data/lib/grape/validations.rb +5 -0
- data/lib/grape/version.rb +2 -1
- data/lib/grape.rb +7 -8
- data/spec/grape/api_spec.rb +63 -0
- data/spec/grape/dsl/inside_route_spec.rb +37 -2
- data/spec/grape/dsl/validations_spec.rb +18 -0
- data/spec/grape/endpoint_spec.rb +83 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +28 -0
- data/spec/grape/middleware/base_spec.rb +33 -11
- data/spec/grape/middleware/formatter_spec.rb +0 -5
- data/spec/grape/util/inheritable_values_spec.rb +14 -0
- data/spec/grape/util/parameter_types_spec.rb +54 -0
- data/spec/grape/util/stackable_values_spec.rb +10 -0
- data/spec/grape/validations/params_scope_spec.rb +84 -0
- data/spec/grape/validations/validators/coerce_spec.rb +29 -8
- data/spec/grape/validations/validators/values_spec.rb +12 -0
- metadata +9 -6
- data/lib/backports/active_support/deep_dup.rb +0 -49
- data/lib/backports/active_support/duplicable.rb +0 -88
@@ -201,8 +201,43 @@ module Grape
|
|
201
201
|
subject.file 'file'
|
202
202
|
end
|
203
203
|
|
204
|
-
it 'returns value' do
|
205
|
-
expect(subject.file).to eq 'file'
|
204
|
+
it 'returns value wrapped in FileResponse' do
|
205
|
+
expect(subject.file).to eq Grape::Util::FileResponse.new('file')
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'returns default' do
|
210
|
+
expect(subject.file).to be nil
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe '#stream' do
|
215
|
+
describe 'set' do
|
216
|
+
before do
|
217
|
+
subject.header 'Cache-Control', 'cache'
|
218
|
+
subject.header 'Content-Length', 123
|
219
|
+
subject.header 'Transfer-Encoding', 'base64'
|
220
|
+
subject.stream 'file'
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'returns value wrapped in FileResponse' do
|
224
|
+
expect(subject.stream).to eq Grape::Util::FileResponse.new('file')
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'also sets result of file to value wrapped in FileResponse' do
|
228
|
+
expect(subject.file).to eq Grape::Util::FileResponse.new('file')
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'sets Cache-Control header to no-cache' do
|
232
|
+
expect(subject.header['Cache-Control']).to eq 'no-cache'
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'sets Content-Length header to nil' do
|
236
|
+
expect(subject.header['Content-Length']).to eq nil
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'sets Transfer-Encoding header to nil' do
|
240
|
+
expect(subject.header['Transfer-Encoding']).to eq nil
|
206
241
|
end
|
207
242
|
end
|
208
243
|
|
@@ -15,9 +15,15 @@ module Grape
|
|
15
15
|
before do
|
16
16
|
subject.namespace_stackable :declared_params, ['dummy']
|
17
17
|
subject.namespace_stackable :validations, ['dummy']
|
18
|
+
subject.namespace_stackable :params, ['dummy']
|
19
|
+
subject.route_setting :description, description: 'lol', params: ['dummy']
|
18
20
|
subject.reset_validations!
|
19
21
|
end
|
20
22
|
|
23
|
+
after do
|
24
|
+
subject.unset_route_setting :description
|
25
|
+
end
|
26
|
+
|
21
27
|
it 'resets declared params' do
|
22
28
|
expect(subject.namespace_stackable(:declared_params)).to eq []
|
23
29
|
end
|
@@ -25,6 +31,18 @@ module Grape
|
|
25
31
|
it 'resets validations' do
|
26
32
|
expect(subject.namespace_stackable(:validations)).to eq []
|
27
33
|
end
|
34
|
+
|
35
|
+
it 'resets params' do
|
36
|
+
expect(subject.namespace_stackable(:params)).to eq []
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'resets documentation params' do
|
40
|
+
expect(subject.route_setting(:description)[:params]).to be_nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'does not reset documentation description' do
|
44
|
+
expect(subject.route_setting(:description)[:description]).to eq 'lol'
|
45
|
+
end
|
28
46
|
end
|
29
47
|
|
30
48
|
describe '.params' do
|
data/spec/grape/endpoint_spec.rb
CHANGED
@@ -941,4 +941,87 @@ describe Grape::Endpoint do
|
|
941
941
|
expect(last_response.body).to eq File.read(__FILE__)
|
942
942
|
end
|
943
943
|
end
|
944
|
+
|
945
|
+
context 'validation errors' do
|
946
|
+
before do
|
947
|
+
subject.before do
|
948
|
+
header['Access-Control-Allow-Origin'] = '*'
|
949
|
+
end
|
950
|
+
subject.params do
|
951
|
+
requires :id, type: String
|
952
|
+
end
|
953
|
+
subject.get do
|
954
|
+
'should not get here'
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
it 'returns the errors, and passes headers' do
|
959
|
+
get '/'
|
960
|
+
expect(last_response.status).to eq 400
|
961
|
+
expect(last_response.body).to eq 'id is missing'
|
962
|
+
expect(last_response.headers['Access-Control-Allow-Origin']).to eq('*')
|
963
|
+
end
|
964
|
+
end
|
965
|
+
|
966
|
+
context 'instrumentation' do
|
967
|
+
before do
|
968
|
+
subject.before do
|
969
|
+
# Placeholder
|
970
|
+
end
|
971
|
+
subject.get do
|
972
|
+
'hello'
|
973
|
+
end
|
974
|
+
|
975
|
+
@events = []
|
976
|
+
@subscriber = ActiveSupport::Notifications.subscribe(/grape/) do |*args|
|
977
|
+
@events << ActiveSupport::Notifications::Event.new(*args)
|
978
|
+
end
|
979
|
+
end
|
980
|
+
|
981
|
+
after do
|
982
|
+
ActiveSupport::Notifications.unsubscribe(@subscriber)
|
983
|
+
end
|
984
|
+
|
985
|
+
it 'notifies AS::N' do
|
986
|
+
get '/'
|
987
|
+
|
988
|
+
# In order that the events finalized (time each block ended)
|
989
|
+
expect(@events).to contain_exactly(
|
990
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
991
|
+
filters: a_collection_containing_exactly(an_instance_of(Proc)),
|
992
|
+
type: :before }),
|
993
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
994
|
+
filters: [],
|
995
|
+
type: :before_validation }),
|
996
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
997
|
+
filters: [],
|
998
|
+
type: :after_validation }),
|
999
|
+
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: an_instance_of(Grape::Endpoint) }),
|
1000
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
1001
|
+
filters: [],
|
1002
|
+
type: :after }),
|
1003
|
+
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
1004
|
+
env: an_instance_of(Hash) })
|
1005
|
+
)
|
1006
|
+
|
1007
|
+
# In order that events were initialized
|
1008
|
+
expect(@events.sort_by(&:time)).to contain_exactly(
|
1009
|
+
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
1010
|
+
env: an_instance_of(Hash) }),
|
1011
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
1012
|
+
filters: a_collection_containing_exactly(an_instance_of(Proc)),
|
1013
|
+
type: :before }),
|
1014
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
1015
|
+
filters: [],
|
1016
|
+
type: :before_validation }),
|
1017
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
1018
|
+
filters: [],
|
1019
|
+
type: :after_validation }),
|
1020
|
+
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: an_instance_of(Grape::Endpoint) }),
|
1021
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: an_instance_of(Grape::Endpoint),
|
1022
|
+
filters: [],
|
1023
|
+
type: :after })
|
1024
|
+
)
|
1025
|
+
end
|
1026
|
+
end
|
944
1027
|
end
|
@@ -5,6 +5,22 @@ describe Grape::Exceptions::ValidationErrors do
|
|
5
5
|
let(:validation_message) { 'FooBar is invalid' }
|
6
6
|
let(:validation_error) { OpenStruct.new(params: [validation_message]) }
|
7
7
|
|
8
|
+
context 'initialize' do
|
9
|
+
let(:headers) {
|
10
|
+
{
|
11
|
+
'A-Header-Key' => 'A-Header-Value'
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
subject do
|
16
|
+
described_class.new(errors: [validation_error], headers: headers)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should assign headers through base class' do
|
20
|
+
expect(subject.headers).to eq(headers)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
8
24
|
context 'message' do
|
9
25
|
context 'is not repeated' do
|
10
26
|
let(:error) do
|
@@ -17,6 +33,18 @@ describe Grape::Exceptions::ValidationErrors do
|
|
17
33
|
end
|
18
34
|
end
|
19
35
|
|
36
|
+
describe '#full_messages' do
|
37
|
+
context 'with errors' do
|
38
|
+
let(:validation_error_1) { Grape::Exceptions::Validation.new(params: ['id'], message_key: 'presence') }
|
39
|
+
let(:validation_error_2) { Grape::Exceptions::Validation.new(params: ['name'], message_key: 'presence') }
|
40
|
+
subject { described_class.new(errors: [validation_error_1, validation_error_2]).full_messages }
|
41
|
+
|
42
|
+
it 'returns an array with each errors full message' do
|
43
|
+
expect(subject).to contain_exactly('id is missing', 'name is missing')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
20
48
|
context 'api' do
|
21
49
|
subject { Class.new(Grape::API) }
|
22
50
|
|
@@ -36,21 +36,43 @@ describe Grape::Middleware::Base do
|
|
36
36
|
|
37
37
|
describe '#response' do
|
38
38
|
subject { Grape::Middleware::Base.new(response) }
|
39
|
-
let(:response) { ->(_) { [204, { abc: 1 }, 'test'] } }
|
40
39
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
context Array do
|
41
|
+
let(:response) { ->(_) { [204, { abc: 1 }, 'test'] } }
|
42
|
+
|
43
|
+
it 'status' do
|
44
|
+
subject.call({})
|
45
|
+
expect(subject.response.status).to eq(204)
|
46
|
+
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
it 'body' do
|
49
|
+
subject.call({})
|
50
|
+
expect(subject.response.body).to eq(['test'])
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'header' do
|
54
|
+
subject.call({})
|
55
|
+
expect(subject.response.header).to have_key(:abc)
|
56
|
+
end
|
49
57
|
end
|
50
58
|
|
51
|
-
|
52
|
-
|
53
|
-
|
59
|
+
context Rack::Response do
|
60
|
+
let(:response) { ->(_) { Rack::Response.new('test', 204, abc: 1) } }
|
61
|
+
|
62
|
+
it 'status' do
|
63
|
+
subject.call({})
|
64
|
+
expect(subject.response.status).to eq(204)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'body' do
|
68
|
+
subject.call({})
|
69
|
+
expect(subject.response.body).to eq(['test'])
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'header' do
|
73
|
+
subject.call({})
|
74
|
+
expect(subject.response.header).to have_key(:abc)
|
75
|
+
end
|
54
76
|
end
|
55
77
|
end
|
56
78
|
|
@@ -110,11 +110,6 @@ describe Grape::Middleware::Formatter do
|
|
110
110
|
expect(subject.env['api.format']).to eq(:xml)
|
111
111
|
end
|
112
112
|
|
113
|
-
it 'looks for case-indifferent headers' do
|
114
|
-
subject.call('PATH_INFO' => '/info', 'http_accept' => 'application/xml')
|
115
|
-
expect(subject.env['api.format']).to eq(:xml)
|
116
|
-
end
|
117
|
-
|
118
113
|
it 'uses quality rankings to determine formats' do
|
119
114
|
subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; q=0.3,application/xml; q=1.0')
|
120
115
|
expect(subject.env['api.format']).to eq(:xml)
|
@@ -58,6 +58,20 @@ module Grape
|
|
58
58
|
expect(subject.to_hash).to eq(some_thing: :foo, some_thing_more: :foo_bar)
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
62
|
+
describe '#clone' do
|
63
|
+
let(:obj_cloned) { subject.clone }
|
64
|
+
|
65
|
+
context 'complex (i.e. not primitive) data types (ex. entity classes, please see bug #891)' do
|
66
|
+
let(:description) { { entity: double } }
|
67
|
+
|
68
|
+
before { subject[:description] = description }
|
69
|
+
|
70
|
+
it 'copies values; does not duplicate them' do
|
71
|
+
expect(obj_cloned[:description]).to eq description
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
61
75
|
end
|
62
76
|
end
|
63
77
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::ParameterTypes do
|
4
|
+
class FooType
|
5
|
+
def self.parse(_)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class BarType
|
10
|
+
def self.parse
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '::primitive?' do
|
15
|
+
[
|
16
|
+
Integer, Float, Numeric, BigDecimal,
|
17
|
+
Virtus::Attribute::Boolean, String, Symbol,
|
18
|
+
Date, DateTime, Time, Rack::Multipart::UploadedFile
|
19
|
+
].each do |type|
|
20
|
+
it "recognizes #{type} as a primitive" do
|
21
|
+
expect(described_class.primitive?(type)).to be_truthy
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'identifies unknown types' do
|
26
|
+
expect(described_class.primitive?(Object)).to be_falsy
|
27
|
+
expect(described_class.primitive?(FooType)).to be_falsy
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '::structure?' do
|
32
|
+
[
|
33
|
+
Hash, Array, Set
|
34
|
+
].each do |type|
|
35
|
+
it "recognizes #{type} as a structure" do
|
36
|
+
expect(described_class.structure?(type)).to be_truthy
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '::custom_type?' do
|
42
|
+
it 'returns false if the type does not respond to :parse' do
|
43
|
+
expect(described_class.custom_type?(Object)).to be_falsy
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns true if the type responds to :parse with one argument' do
|
47
|
+
expect(described_class.custom_type?(FooType)).to be_truthy
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns false if the type\'s #parse method takes other than one argument' do
|
51
|
+
expect(described_class.custom_type?(BarType)).to be_falsy
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -109,6 +109,16 @@ module Grape
|
|
109
109
|
|
110
110
|
expect(grandchild.clone.to_hash).to eq(some_thing: [:foo, [:bar, :more], :grand_foo_bar], some_thing_more: [:foo_bar])
|
111
111
|
end
|
112
|
+
|
113
|
+
context 'complex (i.e. not primitive) data types (ex. middleware, please see bug #930)' do
|
114
|
+
let(:middleware) { double }
|
115
|
+
|
116
|
+
before { subject[:middleware] = middleware }
|
117
|
+
|
118
|
+
it 'copies values; does not duplicate them' do
|
119
|
+
expect(obj_cloned[:middleware]).to eq [middleware]
|
120
|
+
end
|
121
|
+
end
|
112
122
|
end
|
113
123
|
end
|
114
124
|
end
|
@@ -89,6 +89,35 @@ describe Grape::Validations::ParamsScope do
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
context 'when using custom types' do
|
93
|
+
class CustomType
|
94
|
+
attr_reader :value
|
95
|
+
def self.parse(value)
|
96
|
+
fail if value == 'invalid'
|
97
|
+
new(value)
|
98
|
+
end
|
99
|
+
|
100
|
+
def initialize(value)
|
101
|
+
@value = value
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'coerces the parameter via the type\'s parse method' do
|
106
|
+
subject.params do
|
107
|
+
requires :foo, type: CustomType
|
108
|
+
end
|
109
|
+
subject.get('/types') { params[:foo].value }
|
110
|
+
|
111
|
+
get '/types', foo: 'valid'
|
112
|
+
expect(last_response.status).to eq(200)
|
113
|
+
expect(last_response.body).to eq('valid')
|
114
|
+
|
115
|
+
get '/types', foo: 'invalid'
|
116
|
+
expect(last_response.status).to eq(400)
|
117
|
+
expect(last_response.body).to match(/foo is invalid/)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
92
121
|
context 'array without coerce type explicitly given' do
|
93
122
|
it 'sets the type based on first element' do
|
94
123
|
subject.params do
|
@@ -242,4 +271,59 @@ describe Grape::Validations::ParamsScope do
|
|
242
271
|
end.to raise_error Grape::Exceptions::UnsupportedGroupTypeError
|
243
272
|
end
|
244
273
|
end
|
274
|
+
|
275
|
+
context 'when validations are dependent on a parameter' do
|
276
|
+
before do
|
277
|
+
subject.params do
|
278
|
+
optional :a
|
279
|
+
given :a do
|
280
|
+
requires :b
|
281
|
+
end
|
282
|
+
end
|
283
|
+
subject.get('/test') { declared(params).to_json }
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'applies the validations only if the parameter is present' do
|
287
|
+
get '/test'
|
288
|
+
expect(last_response.status).to eq(200)
|
289
|
+
|
290
|
+
get '/test', a: true
|
291
|
+
expect(last_response.status).to eq(400)
|
292
|
+
expect(last_response.body).to eq('b is missing')
|
293
|
+
|
294
|
+
get '/test', a: true, b: true
|
295
|
+
expect(last_response.status).to eq(200)
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'raises an error if the dependent parameter was never specified' do
|
299
|
+
expect do
|
300
|
+
subject.params do
|
301
|
+
given :c do
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end.to raise_error(Grape::Exceptions::UnknownParameter)
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'includes the parameter within #declared(params)' do
|
308
|
+
get '/test', a: true, b: true
|
309
|
+
|
310
|
+
expect(JSON.parse(last_response.body)).to eq('a' => 'true', 'b' => 'true')
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'returns a sensible error message within a nested context' do
|
314
|
+
subject.params do
|
315
|
+
requires :bar, type: Hash do
|
316
|
+
optional :a
|
317
|
+
given :a do
|
318
|
+
requires :b
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
subject.get('/nested') { 'worked' }
|
323
|
+
|
324
|
+
get '/nested', bar: { a: true }
|
325
|
+
expect(last_response.status).to eq(400)
|
326
|
+
expect(last_response.body).to eq('bar[b] is missing')
|
327
|
+
end
|
328
|
+
end
|
245
329
|
end
|
@@ -11,6 +11,14 @@ describe Grape::Validations::CoerceValidator do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
describe 'coerce' do
|
14
|
+
module CoerceValidatorSpec
|
15
|
+
class User
|
16
|
+
include Virtus.model
|
17
|
+
attribute :id, Integer
|
18
|
+
attribute :name, String
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
14
22
|
context 'i18n' do
|
15
23
|
after :each do
|
16
24
|
I18n.locale = :en
|
@@ -82,14 +90,6 @@ describe Grape::Validations::CoerceValidator do
|
|
82
90
|
end
|
83
91
|
|
84
92
|
context 'complex objects' do
|
85
|
-
module CoerceValidatorSpec
|
86
|
-
class User
|
87
|
-
include Virtus.model
|
88
|
-
attribute :id, Integer
|
89
|
-
attribute :name, String
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
93
|
it 'error on malformed input for complex objects' do
|
94
94
|
subject.params do
|
95
95
|
requires :user, type: CoerceValidatorSpec::User
|
@@ -148,6 +148,27 @@ describe Grape::Validations::CoerceValidator do
|
|
148
148
|
expect(last_response.status).to eq(200)
|
149
149
|
expect(last_response.body).to eq('TrueClass')
|
150
150
|
end
|
151
|
+
|
152
|
+
it 'Array of Complex' do
|
153
|
+
subject.params do
|
154
|
+
requires :arry, coerce: Array[CoerceValidatorSpec::User]
|
155
|
+
end
|
156
|
+
subject.get '/array' do
|
157
|
+
params[:arry].size
|
158
|
+
end
|
159
|
+
|
160
|
+
get 'array', arry: [31]
|
161
|
+
expect(last_response.status).to eq(400)
|
162
|
+
expect(last_response.body).to eq('arry is invalid')
|
163
|
+
|
164
|
+
get 'array', arry: { id: 31, name: 'Alice' }
|
165
|
+
expect(last_response.status).to eq(400)
|
166
|
+
expect(last_response.body).to eq('arry is invalid')
|
167
|
+
|
168
|
+
get 'array', arry: [{ id: 31, name: 'Alice' }]
|
169
|
+
expect(last_response.status).to eq(200)
|
170
|
+
expect(last_response.body).to eq('1')
|
171
|
+
end
|
151
172
|
end
|
152
173
|
|
153
174
|
context 'Set' do
|
@@ -56,6 +56,13 @@ describe Grape::Validations::ValuesValidator do
|
|
56
56
|
{ type: params[:type] }
|
57
57
|
end
|
58
58
|
|
59
|
+
params do
|
60
|
+
optional :type, type: Boolean, desc: 'A boolean', values: [true]
|
61
|
+
end
|
62
|
+
get '/values/optional_boolean' do
|
63
|
+
{ type: params[:type] }
|
64
|
+
end
|
65
|
+
|
59
66
|
params do
|
60
67
|
requires :type, type: Integer, desc: 'An integer', values: [10, 11], default: 10
|
61
68
|
end
|
@@ -174,6 +181,11 @@ describe Grape::Validations::ValuesValidator do
|
|
174
181
|
end.to raise_error Grape::Exceptions::IncompatibleOptionValues
|
175
182
|
end
|
176
183
|
|
184
|
+
it 'allows values to be true or false when setting the type to boolean' do
|
185
|
+
get('/values/optional_boolean', type: true)
|
186
|
+
expect(last_response.status).to eq 200
|
187
|
+
expect(last_response.body).to eq({ type: true }.to_json)
|
188
|
+
end
|
177
189
|
it 'allows values to be a kind of the coerced type not just an instance of it' do
|
178
190
|
get('/values/coercion', type: 10)
|
179
191
|
expect(last_response.status).to eq 200
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bleigh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -317,8 +317,6 @@ files:
|
|
317
317
|
- gemfiles/rails_4.gemfile
|
318
318
|
- grape.gemspec
|
319
319
|
- grape.png
|
320
|
-
- lib/backports/active_support/deep_dup.rb
|
321
|
-
- lib/backports/active_support/duplicable.rb
|
322
320
|
- lib/grape.rb
|
323
321
|
- lib/grape/api.rb
|
324
322
|
- lib/grape/api/helpers.rb
|
@@ -351,6 +349,7 @@ files:
|
|
351
349
|
- lib/grape/exceptions/missing_option.rb
|
352
350
|
- lib/grape/exceptions/missing_vendor_option.rb
|
353
351
|
- lib/grape/exceptions/unknown_options.rb
|
352
|
+
- lib/grape/exceptions/unknown_parameter.rb
|
354
353
|
- lib/grape/exceptions/unknown_validator.rb
|
355
354
|
- lib/grape/exceptions/unsupported_group_type.rb
|
356
355
|
- lib/grape/exceptions/validation.rb
|
@@ -385,8 +384,10 @@ files:
|
|
385
384
|
- lib/grape/presenters/presenter.rb
|
386
385
|
- lib/grape/route.rb
|
387
386
|
- lib/grape/util/content_types.rb
|
387
|
+
- lib/grape/util/file_response.rb
|
388
388
|
- lib/grape/util/inheritable_setting.rb
|
389
389
|
- lib/grape/util/inheritable_values.rb
|
390
|
+
- lib/grape/util/parameter_types.rb
|
390
391
|
- lib/grape/util/stackable_values.rb
|
391
392
|
- lib/grape/util/strict_hash_configuration.rb
|
392
393
|
- lib/grape/validations.rb
|
@@ -450,6 +451,7 @@ files:
|
|
450
451
|
- spec/grape/presenters/presenter_spec.rb
|
451
452
|
- spec/grape/util/inheritable_setting_spec.rb
|
452
453
|
- spec/grape/util/inheritable_values_spec.rb
|
454
|
+
- spec/grape/util/parameter_types_spec.rb
|
453
455
|
- spec/grape/util/stackable_values_spec.rb
|
454
456
|
- spec/grape/util/strict_hash_configuration_spec.rb
|
455
457
|
- spec/grape/validations/attributes_iterator_spec.rb
|
@@ -473,7 +475,7 @@ files:
|
|
473
475
|
- spec/support/endpoint_faker.rb
|
474
476
|
- spec/support/file_streamer.rb
|
475
477
|
- spec/support/versioned_helpers.rb
|
476
|
-
homepage: https://github.com/
|
478
|
+
homepage: https://github.com/ruby-grape/grape
|
477
479
|
licenses:
|
478
480
|
- MIT
|
479
481
|
metadata: {}
|
@@ -492,7 +494,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
492
494
|
- !ruby/object:Gem::Version
|
493
495
|
version: '0'
|
494
496
|
requirements: []
|
495
|
-
rubyforge_project:
|
497
|
+
rubyforge_project:
|
496
498
|
rubygems_version: 2.4.5
|
497
499
|
signing_key:
|
498
500
|
specification_version: 4
|
@@ -543,6 +545,7 @@ test_files:
|
|
543
545
|
- spec/grape/presenters/presenter_spec.rb
|
544
546
|
- spec/grape/util/inheritable_setting_spec.rb
|
545
547
|
- spec/grape/util/inheritable_values_spec.rb
|
548
|
+
- spec/grape/util/parameter_types_spec.rb
|
546
549
|
- spec/grape/util/stackable_values_spec.rb
|
547
550
|
- spec/grape/util/strict_hash_configuration_spec.rb
|
548
551
|
- spec/grape/validations/attributes_iterator_spec.rb
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# Backport from Rails 4.x
|
2
|
-
# https://github.com/rails/rails/blob/4-0-stable/activesupport/lib/active_support/core_ext/object/deep_dup.rb
|
3
|
-
|
4
|
-
require_relative 'duplicable'
|
5
|
-
|
6
|
-
class Object
|
7
|
-
# Returns a deep copy of object if it's duplicable. If it's
|
8
|
-
# not duplicable, returns +self+.
|
9
|
-
#
|
10
|
-
# object = Object.new
|
11
|
-
# dup = object.deep_dup
|
12
|
-
# dup.instance_variable_set(:@a, 1)
|
13
|
-
#
|
14
|
-
# object.instance_variable_defined?(:@a) #=> false
|
15
|
-
# dup.instance_variable_defined?(:@a) #=> true
|
16
|
-
def deep_dup
|
17
|
-
duplicable? ? dup : self
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
class Array
|
22
|
-
# Returns a deep copy of array.
|
23
|
-
#
|
24
|
-
# array = [1, [2, 3]]
|
25
|
-
# dup = array.deep_dup
|
26
|
-
# dup[1][2] = 4
|
27
|
-
#
|
28
|
-
# array[1][2] #=> nil
|
29
|
-
# dup[1][2] #=> 4
|
30
|
-
def deep_dup
|
31
|
-
map(&:deep_dup)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class Hash
|
36
|
-
# Returns a deep copy of hash.
|
37
|
-
#
|
38
|
-
# hash = { a: { b: 'b' } }
|
39
|
-
# dup = hash.deep_dup
|
40
|
-
# dup[:a][:c] = 'c'
|
41
|
-
#
|
42
|
-
# hash[:a][:c] #=> nil
|
43
|
-
# dup[:a][:c] #=> "c"
|
44
|
-
def deep_dup
|
45
|
-
each_with_object(dup) do |(key, value), hash|
|
46
|
-
hash[key.deep_dup] = value.deep_dup
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|