grape 0.19.2 → 1.0.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/Appraisals +8 -0
- data/CHANGELOG.md +40 -22
- data/Gemfile +1 -0
- data/Gemfile.lock +58 -59
- data/LICENSE +1 -1
- data/README.md +94 -49
- data/Rakefile +1 -0
- data/UPGRADING.md +89 -0
- data/benchmark/simple_with_type_coercer.rb +22 -0
- data/gemfiles/multi_json.gemfile +36 -0
- data/gemfiles/multi_xml.gemfile +36 -0
- data/gemfiles/rack_1.5.2.gemfile +1 -0
- data/gemfiles/rack_edge.gemfile +1 -0
- data/gemfiles/rails_3.gemfile +1 -0
- data/gemfiles/rails_4.gemfile +1 -0
- data/gemfiles/rails_5.gemfile +1 -0
- data/gemfiles/rails_edge.gemfile +1 -0
- data/grape.gemspec +0 -3
- data/lib/grape.rb +40 -17
- data/lib/grape/dsl/helpers.rb +32 -18
- data/lib/grape/dsl/inside_route.rb +2 -2
- data/lib/grape/dsl/parameters.rb +26 -0
- data/lib/grape/dsl/routing.rb +1 -1
- data/lib/grape/dsl/settings.rb +1 -1
- data/lib/grape/endpoint.rb +20 -16
- data/lib/grape/error_formatter/json.rb +1 -1
- data/lib/grape/error_formatter/txt.rb +1 -1
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +26 -0
- data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +18 -0
- data/lib/grape/extensions/deep_mergeable_hash.rb +19 -0
- data/lib/grape/extensions/deep_symbolize_hash.rb +30 -0
- data/lib/grape/extensions/hash.rb +23 -0
- data/lib/grape/extensions/hashie/mash.rb +24 -0
- data/lib/grape/formatter/json.rb +1 -1
- data/lib/grape/formatter/serializable_hash.rb +2 -2
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/globals.rb +1 -1
- data/lib/grape/parser/json.rb +2 -2
- data/lib/grape/parser/xml.rb +2 -2
- data/lib/grape/request.rb +11 -10
- data/lib/grape/util/json.rb +8 -0
- data/lib/grape/util/xml.rb +8 -0
- data/lib/grape/validations.rb +4 -0
- data/lib/grape/validations/params_scope.rb +77 -39
- data/lib/grape/validations/types/build_coercer.rb +27 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +18 -4
- data/lib/grape/validations/types/file.rb +2 -3
- data/lib/grape/validations/validator_factory.rb +18 -0
- data/lib/grape/validations/validators/base.rb +4 -5
- data/lib/grape/validations/validators/coerce.rb +4 -0
- data/lib/grape/validations/validators/except_values.rb +20 -0
- data/lib/grape/validations/validators/values.rb +25 -5
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/invalid_format_spec.rb +3 -3
- data/spec/grape/api_spec.rb +28 -16
- data/spec/grape/dsl/helpers_spec.rb +25 -6
- data/spec/grape/endpoint_spec.rb +117 -13
- data/spec/grape/extensions/param_builders/hash_spec.rb +83 -0
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +105 -0
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +79 -0
- data/spec/grape/middleware/formatter_spec.rb +6 -2
- data/spec/grape/request_spec.rb +13 -3
- data/spec/grape/validations/instance_behaivour_spec.rb +44 -0
- data/spec/grape/validations/params_scope_spec.rb +23 -0
- data/spec/grape/validations/types_spec.rb +19 -0
- data/spec/grape/validations/validators/coerce_spec.rb +117 -8
- data/spec/grape/validations/validators/except_values_spec.rb +191 -0
- data/spec/grape/validations/validators/values_spec.rb +78 -0
- data/spec/integration/multi_json/json_spec.rb +7 -0
- data/spec/integration/multi_xml/xml_spec.rb +7 -0
- metadata +30 -46
- data/pkg/grape-0.18.0.gem +0 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Extensions::Hash::ParamBuilder do
|
4
|
+
subject { Class.new(Grape::API) }
|
5
|
+
|
6
|
+
def app
|
7
|
+
subject
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'in an endpoint' do
|
11
|
+
context '#params' do
|
12
|
+
before do
|
13
|
+
subject.params do
|
14
|
+
build_with Grape::Extensions::Hash::ParamBuilder
|
15
|
+
end
|
16
|
+
|
17
|
+
subject.get do
|
18
|
+
params.class
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should be of type Hash' do
|
23
|
+
get '/'
|
24
|
+
expect(last_response.status).to eq(200)
|
25
|
+
expect(last_response.body).to eq('Hash')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'in an api' do
|
31
|
+
before do
|
32
|
+
subject.send(:include, Grape::Extensions::Hash::ParamBuilder)
|
33
|
+
end
|
34
|
+
|
35
|
+
context '#params' do
|
36
|
+
before do
|
37
|
+
subject.get do
|
38
|
+
params.class
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should be Hash' do
|
43
|
+
get '/'
|
44
|
+
expect(last_response.status).to eq(200)
|
45
|
+
expect(last_response.body).to eq('Hash')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'symbolizes params keys' do
|
50
|
+
subject.params do
|
51
|
+
optional :a, type: Hash do
|
52
|
+
optional :b, type: Hash do
|
53
|
+
optional :c, type: String
|
54
|
+
end
|
55
|
+
optional :d, type: Array
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
subject.get '/foo' do
|
60
|
+
[params[:a][:b][:c], params[:a][:d]]
|
61
|
+
end
|
62
|
+
|
63
|
+
get '/foo', 'a' => { b: { c: 'bar' }, 'd' => ['foo'] }
|
64
|
+
expect(last_response.status).to eq(200)
|
65
|
+
expect(last_response.body).to eq('["bar", ["foo"]]')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'symbolizes the params' do
|
69
|
+
subject.params do
|
70
|
+
build_with Grape::Extensions::Hash::ParamBuilder
|
71
|
+
requires :a, type: String
|
72
|
+
end
|
73
|
+
|
74
|
+
subject.get '/foo' do
|
75
|
+
[params[:a], params['a']]
|
76
|
+
end
|
77
|
+
|
78
|
+
get '/foo', a: 'bar'
|
79
|
+
expect(last_response.status).to eq(200)
|
80
|
+
expect(last_response.body).to eq('["bar", nil]')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder do
|
4
|
+
subject { Class.new(Grape::API) }
|
5
|
+
|
6
|
+
def app
|
7
|
+
subject
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'in an endpoint' do
|
11
|
+
context '#params' do
|
12
|
+
before do
|
13
|
+
subject.params do
|
14
|
+
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
|
15
|
+
end
|
16
|
+
|
17
|
+
subject.get do
|
18
|
+
params.class
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should be of type Hash' do
|
23
|
+
get '/'
|
24
|
+
expect(last_response.status).to eq(200)
|
25
|
+
expect(last_response.body).to eq('ActiveSupport::HashWithIndifferentAccess')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'in an api' do
|
31
|
+
before do
|
32
|
+
subject.send(:include, Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder)
|
33
|
+
end
|
34
|
+
|
35
|
+
context '#params' do
|
36
|
+
before do
|
37
|
+
subject.get do
|
38
|
+
params.class
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'is a Hash' do
|
43
|
+
get '/'
|
44
|
+
expect(last_response.status).to eq(200)
|
45
|
+
expect(last_response.body).to eq('ActiveSupport::HashWithIndifferentAccess')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'parses sub hash params' do
|
49
|
+
subject.params do
|
50
|
+
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
|
51
|
+
|
52
|
+
optional :a, type: Hash do
|
53
|
+
optional :b, type: Hash do
|
54
|
+
optional :c, type: String
|
55
|
+
end
|
56
|
+
optional :d, type: Array
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
subject.get '/foo' do
|
61
|
+
[params[:a]['b'][:c], params['a'][:d]]
|
62
|
+
end
|
63
|
+
|
64
|
+
get '/foo', a: { b: { c: 'bar' }, d: ['foo'] }
|
65
|
+
expect(last_response.status).to eq(200)
|
66
|
+
expect(last_response.body).to eq('["bar", ["foo"]]')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'params are indifferent to symbol or string keys' do
|
70
|
+
subject.params do
|
71
|
+
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
|
72
|
+
optional :a, type: Hash do
|
73
|
+
optional :b, type: Hash do
|
74
|
+
optional :c, type: String
|
75
|
+
end
|
76
|
+
optional :d, type: Array
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
subject.get '/foo' do
|
81
|
+
[params[:a]['b'][:c], params['a'][:d]]
|
82
|
+
end
|
83
|
+
|
84
|
+
get '/foo', 'a' => { b: { c: 'bar' }, 'd' => ['foo'] }
|
85
|
+
expect(last_response.status).to eq(200)
|
86
|
+
expect(last_response.body).to eq('["bar", ["foo"]]')
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'responds to string keys' do
|
90
|
+
subject.params do
|
91
|
+
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
|
92
|
+
requires :a, type: String
|
93
|
+
end
|
94
|
+
|
95
|
+
subject.get '/foo' do
|
96
|
+
[params[:a], params['a']]
|
97
|
+
end
|
98
|
+
|
99
|
+
get '/foo', a: 'bar'
|
100
|
+
expect(last_response.status).to eq(200)
|
101
|
+
expect(last_response.body).to eq('["bar", "bar"]')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Extensions::Hashie::Mash::ParamBuilder do
|
4
|
+
subject { Class.new(Grape::API) }
|
5
|
+
|
6
|
+
def app
|
7
|
+
subject
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'in an endpoint' do
|
11
|
+
context '#params' do
|
12
|
+
before do
|
13
|
+
subject.params do
|
14
|
+
build_with Grape::Extensions::Hashie::Mash::ParamBuilder
|
15
|
+
end
|
16
|
+
|
17
|
+
subject.get do
|
18
|
+
params.class
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should be of type Hashie::Mash' do
|
23
|
+
get '/'
|
24
|
+
expect(last_response.status).to eq(200)
|
25
|
+
expect(last_response.body).to eq('Hashie::Mash')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'in an api' do
|
31
|
+
before do
|
32
|
+
subject.send(:include, Grape::Extensions::Hashie::Mash::ParamBuilder)
|
33
|
+
end
|
34
|
+
|
35
|
+
context '#params' do
|
36
|
+
before do
|
37
|
+
subject.get do
|
38
|
+
params.class
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should be Hashie::Mash' do
|
43
|
+
get '/'
|
44
|
+
expect(last_response.status).to eq(200)
|
45
|
+
expect(last_response.body).to eq('Hashie::Mash')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'in a nested namespace api' do
|
50
|
+
before do
|
51
|
+
subject.namespace :foo do
|
52
|
+
get do
|
53
|
+
params.class
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should be Hashie::Mash' do
|
59
|
+
get '/foo'
|
60
|
+
expect(last_response.status).to eq(200)
|
61
|
+
expect(last_response.body).to eq('Hashie::Mash')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'is indifferent to key or symbol access' do
|
66
|
+
subject.params do
|
67
|
+
build_with Grape::Extensions::Hashie::Mash::ParamBuilder
|
68
|
+
requires :a, type: String
|
69
|
+
end
|
70
|
+
subject.get '/foo' do
|
71
|
+
[params[:a], params['a']]
|
72
|
+
end
|
73
|
+
|
74
|
+
get '/foo', a: 'bar'
|
75
|
+
expect(last_response.status).to eq(200)
|
76
|
+
expect(last_response.body).to eq('["bar", "bar"]')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -11,7 +11,7 @@ describe Grape::Middleware::Formatter do
|
|
11
11
|
let(:body) { { 'abc' => 'def' } }
|
12
12
|
it 'looks at the bodies for possibly serializable data' do
|
13
13
|
_, _, bodies = *subject.call('PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json')
|
14
|
-
bodies.each { |b| expect(b).to eq(
|
14
|
+
bodies.each { |b| expect(b).to eq(::Grape::Json.dump(body)) }
|
15
15
|
end
|
16
16
|
|
17
17
|
context 'default format' do
|
@@ -274,7 +274,11 @@ describe Grape::Middleware::Formatter do
|
|
274
274
|
'rack.input' => io,
|
275
275
|
'CONTENT_LENGTH' => io.length
|
276
276
|
)
|
277
|
-
|
277
|
+
if Object.const_defined? :MultiXml
|
278
|
+
expect(subject.env['rack.request.form_hash']['thing']['name']).to eq('Test')
|
279
|
+
else
|
280
|
+
expect(subject.env['rack.request.form_hash']['thing']['name']['__content__']).to eq('Test')
|
281
|
+
end
|
278
282
|
end
|
279
283
|
[Rack::Request::FORM_DATA_MEDIA_TYPES, Rack::Request::PARSEABLE_DATA_MEDIA_TYPES].flatten.each do |content_type|
|
280
284
|
it "ignores #{content_type}" do
|
data/spec/grape/request_spec.rb
CHANGED
@@ -30,8 +30,18 @@ module Grape
|
|
30
30
|
}
|
31
31
|
end
|
32
32
|
|
33
|
-
it 'returns
|
34
|
-
expect(request.params).to eq('a' => '123', 'b' => 'xyz')
|
33
|
+
it 'by default returns stringified parameter keys' do
|
34
|
+
expect(request.params).to eq(ActiveSupport::HashWithIndifferentAccess.new('a' => '123', 'b' => 'xyz'))
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when build_params_with: Grape::Extensions::Hash::ParamBuilder is specified' do
|
38
|
+
let(:request) do
|
39
|
+
Grape::Request.new(env, build_params_with: Grape::Extensions::Hash::ParamBuilder)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns symbolized params' do
|
43
|
+
expect(request.params).to eq(a: '123', b: 'xyz')
|
44
|
+
end
|
35
45
|
end
|
36
46
|
|
37
47
|
describe 'with grape.routing_args' do
|
@@ -47,7 +57,7 @@ module Grape
|
|
47
57
|
end
|
48
58
|
|
49
59
|
it 'cuts version and route_info' do
|
50
|
-
expect(request.params).to eq(
|
60
|
+
expect(request.params).to eq(ActiveSupport::HashWithIndifferentAccess.new(a: '123', b: 'xyz', c: 'ccc'))
|
51
61
|
end
|
52
62
|
end
|
53
63
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Validator with instance variables' do
|
4
|
+
let(:validator_type) do
|
5
|
+
Class.new(Grape::Validations::Base) do
|
6
|
+
def validate_param!(_attr_name, _params)
|
7
|
+
if @instance_variable
|
8
|
+
raise Grape::Exceptions::Validation, params: ['params'],
|
9
|
+
message: 'This should never happen'
|
10
|
+
end
|
11
|
+
@instance_variable = true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
before do
|
17
|
+
Grape::Validations.register_validator('instance_validator', validator_type)
|
18
|
+
end
|
19
|
+
|
20
|
+
after do
|
21
|
+
Grape::Validations.deregister_validator('instance_validator')
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:app) do
|
25
|
+
Class.new(Grape::API) do
|
26
|
+
params do
|
27
|
+
optional :param_to_validate, instance_validator: true
|
28
|
+
optional :another_param_to_validate, instance_validator: true
|
29
|
+
end
|
30
|
+
get do
|
31
|
+
'noop'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'passes validation every time' do
|
37
|
+
expect(validator_type).to receive(:new).exactly(4).times.and_call_original
|
38
|
+
|
39
|
+
2.times do
|
40
|
+
get '/', param_to_validate: 'value', another_param_to_validate: 'value'
|
41
|
+
expect(last_response.status).to eq 200
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -473,6 +473,29 @@ describe Grape::Validations::ParamsScope do
|
|
473
473
|
end
|
474
474
|
end
|
475
475
|
|
476
|
+
context 'when validations are dependent on a parameter within an array param' do
|
477
|
+
before do
|
478
|
+
subject.params do
|
479
|
+
requires :foos, type: Array do
|
480
|
+
optional :foo_type, :baz_type
|
481
|
+
given :foo_type do
|
482
|
+
requires :bar
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
subject.post('/test') { declared(params).to_json }
|
487
|
+
end
|
488
|
+
|
489
|
+
it 'applies the constraint within each value' do
|
490
|
+
post '/test',
|
491
|
+
{ foos: [{ foo_type: 'a' }, { baz_type: 'c' }] }.to_json,
|
492
|
+
'CONTENT_TYPE' => 'application/json'
|
493
|
+
|
494
|
+
expect(last_response.status).to eq(400)
|
495
|
+
expect(last_response.body).to eq('foos[0][bar] is missing')
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
476
499
|
context 'when validations are dependent on a parameter with specific value' do
|
477
500
|
# build test cases from all combinations of declarations and options
|
478
501
|
a_decls = %i(optional requires)
|
@@ -90,4 +90,23 @@ describe Grape::Validations::Types do
|
|
90
90
|
expect(described_class.custom?(TypesSpec::BarType)).to be_falsy
|
91
91
|
end
|
92
92
|
end
|
93
|
+
|
94
|
+
describe '::build_coercer' do
|
95
|
+
it 'has internal cache variables' do
|
96
|
+
expect(described_class.instance_variable_get(:@__cache)).to be_a(Hash)
|
97
|
+
expect(described_class.instance_variable_get(:@__cache_write_lock)).to be_a(Mutex)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'caches the result of the Virtus::Attribute.build method' do
|
101
|
+
original_cache = described_class.instance_variable_get(:@__cache)
|
102
|
+
described_class.instance_variable_set(:@__cache, {})
|
103
|
+
|
104
|
+
coercer = 'TestCoercer'
|
105
|
+
expect(Virtus::Attribute).to receive(:build).once.and_return(coercer)
|
106
|
+
expect(described_class.build_coercer(Array[String])).to eq(coercer)
|
107
|
+
expect(described_class.build_coercer(Array[String])).to eq(coercer)
|
108
|
+
|
109
|
+
described_class.instance_variable_set(:@__cache, original_cache)
|
110
|
+
end
|
111
|
+
end
|
93
112
|
end
|
@@ -280,12 +280,49 @@ describe Grape::Validations::CoerceValidator do
|
|
280
280
|
expect(last_response.body).to eq('TrueClass')
|
281
281
|
end
|
282
282
|
|
283
|
+
it 'Boolean' do
|
284
|
+
subject.params do
|
285
|
+
optional :boolean, type: Boolean, default: true
|
286
|
+
end
|
287
|
+
subject.get '/boolean' do
|
288
|
+
params[:boolean].class
|
289
|
+
end
|
290
|
+
|
291
|
+
get '/boolean'
|
292
|
+
expect(last_response.status).to eq(200)
|
293
|
+
expect(last_response.body).to eq('TrueClass')
|
294
|
+
|
295
|
+
get '/boolean', boolean: true
|
296
|
+
expect(last_response.status).to eq(200)
|
297
|
+
expect(last_response.body).to eq('TrueClass')
|
298
|
+
|
299
|
+
get '/boolean', boolean: false
|
300
|
+
expect(last_response.status).to eq(200)
|
301
|
+
expect(last_response.body).to eq('FalseClass')
|
302
|
+
|
303
|
+
get '/boolean', boolean: 'true'
|
304
|
+
expect(last_response.status).to eq(200)
|
305
|
+
expect(last_response.body).to eq('TrueClass')
|
306
|
+
|
307
|
+
get '/boolean', boolean: 'false'
|
308
|
+
expect(last_response.status).to eq(200)
|
309
|
+
expect(last_response.body).to eq('FalseClass')
|
310
|
+
|
311
|
+
get '/boolean', boolean: 123
|
312
|
+
expect(last_response.status).to eq(400)
|
313
|
+
expect(last_response.body).to eq('boolean is invalid')
|
314
|
+
|
315
|
+
get '/boolean', boolean: '123'
|
316
|
+
expect(last_response.status).to eq(400)
|
317
|
+
expect(last_response.body).to eq('boolean is invalid')
|
318
|
+
end
|
319
|
+
|
283
320
|
it 'Rack::Multipart::UploadedFile' do
|
284
321
|
subject.params do
|
285
322
|
requires :file, type: Rack::Multipart::UploadedFile
|
286
323
|
end
|
287
324
|
subject.post '/upload' do
|
288
|
-
params[:file]
|
325
|
+
params[:file][:filename]
|
289
326
|
end
|
290
327
|
|
291
328
|
post '/upload', file: Rack::Test::UploadedFile.new(__FILE__)
|
@@ -302,7 +339,7 @@ describe Grape::Validations::CoerceValidator do
|
|
302
339
|
requires :file, coerce: File
|
303
340
|
end
|
304
341
|
subject.post '/upload' do
|
305
|
-
params[:file]
|
342
|
+
params[:file][:filename]
|
306
343
|
end
|
307
344
|
|
308
345
|
post '/upload', file: Rack::Test::UploadedFile.new(__FILE__)
|
@@ -625,7 +662,7 @@ describe Grape::Validations::CoerceValidator do
|
|
625
662
|
|
626
663
|
get '/', a: %w(the other)
|
627
664
|
expect(last_response.status).to eq(200)
|
628
|
-
expect(last_response.body).to eq('
|
665
|
+
expect(last_response.body).to eq('["the", "other"]')
|
629
666
|
|
630
667
|
get '/', a: { a: 1, b: 2 }
|
631
668
|
expect(last_response.status).to eq(400)
|
@@ -633,27 +670,27 @@ describe Grape::Validations::CoerceValidator do
|
|
633
670
|
|
634
671
|
get '/', a: [1, 2, 3]
|
635
672
|
expect(last_response.status).to eq(200)
|
636
|
-
expect(last_response.body).to eq('
|
673
|
+
expect(last_response.body).to eq('["1", "2", "3"]')
|
637
674
|
end
|
638
675
|
|
639
676
|
it 'allows multiple collection types' do
|
640
677
|
get '/', b: [1, 2, 3]
|
641
678
|
expect(last_response.status).to eq(200)
|
642
|
-
expect(last_response.body).to eq('
|
679
|
+
expect(last_response.body).to eq('[1, 2, 3]')
|
643
680
|
|
644
681
|
get '/', b: %w(1 2 3)
|
645
682
|
expect(last_response.status).to eq(200)
|
646
|
-
expect(last_response.body).to eq('
|
683
|
+
expect(last_response.body).to eq('[1, 2, 3]')
|
647
684
|
|
648
685
|
get '/', b: [1, true, 'three']
|
649
686
|
expect(last_response.status).to eq(200)
|
650
|
-
expect(last_response.body).to eq('
|
687
|
+
expect(last_response.body).to eq('["1", "true", "three"]')
|
651
688
|
end
|
652
689
|
|
653
690
|
it 'allows collections with multiple types' do
|
654
691
|
get '/', c: [1, '2', true, 'three']
|
655
692
|
expect(last_response.status).to eq(200)
|
656
|
-
expect(last_response.body).to eq('
|
693
|
+
expect(last_response.body).to eq('[1, 2, "true", "three"]')
|
657
694
|
|
658
695
|
get '/', d: '1'
|
659
696
|
expect(last_response.status).to eq(200)
|
@@ -669,6 +706,78 @@ describe Grape::Validations::CoerceValidator do
|
|
669
706
|
end
|
670
707
|
end
|
671
708
|
|
709
|
+
context 'when params is Hashie::Mash' do
|
710
|
+
context 'for primitive collections' do
|
711
|
+
before do
|
712
|
+
subject.params do
|
713
|
+
build_with Grape::Extensions::Hashie::Mash::ParamBuilder
|
714
|
+
optional :a, types: [String, Array[String]]
|
715
|
+
optional :b, types: [Array[Integer], Array[String]]
|
716
|
+
optional :c, type: Array[Integer, String]
|
717
|
+
optional :d, types: [Integer, String, Set[Integer, String]]
|
718
|
+
end
|
719
|
+
subject.get '/' do
|
720
|
+
(
|
721
|
+
params.a ||
|
722
|
+
params.b ||
|
723
|
+
params.c ||
|
724
|
+
params.d
|
725
|
+
).inspect
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
it 'allows singular form declaration' do
|
730
|
+
get '/', a: 'one way'
|
731
|
+
expect(last_response.status).to eq(200)
|
732
|
+
expect(last_response.body).to eq('"one way"')
|
733
|
+
|
734
|
+
get '/', a: %w(the other)
|
735
|
+
expect(last_response.status).to eq(200)
|
736
|
+
expect(last_response.body).to eq('#<Hashie::Array ["the", "other"]>')
|
737
|
+
|
738
|
+
get '/', a: { a: 1, b: 2 }
|
739
|
+
expect(last_response.status).to eq(400)
|
740
|
+
expect(last_response.body).to eq('a is invalid')
|
741
|
+
|
742
|
+
get '/', a: [1, 2, 3]
|
743
|
+
expect(last_response.status).to eq(200)
|
744
|
+
expect(last_response.body).to eq('#<Hashie::Array ["1", "2", "3"]>')
|
745
|
+
end
|
746
|
+
|
747
|
+
it 'allows multiple collection types' do
|
748
|
+
get '/', b: [1, 2, 3]
|
749
|
+
expect(last_response.status).to eq(200)
|
750
|
+
expect(last_response.body).to eq('#<Hashie::Array [1, 2, 3]>')
|
751
|
+
|
752
|
+
get '/', b: %w(1 2 3)
|
753
|
+
expect(last_response.status).to eq(200)
|
754
|
+
expect(last_response.body).to eq('#<Hashie::Array [1, 2, 3]>')
|
755
|
+
|
756
|
+
get '/', b: [1, true, 'three']
|
757
|
+
expect(last_response.status).to eq(200)
|
758
|
+
expect(last_response.body).to eq('#<Hashie::Array ["1", "true", "three"]>')
|
759
|
+
end
|
760
|
+
|
761
|
+
it 'allows collections with multiple types' do
|
762
|
+
get '/', c: [1, '2', true, 'three']
|
763
|
+
expect(last_response.status).to eq(200)
|
764
|
+
expect(last_response.body).to eq('#<Hashie::Array [1, 2, "true", "three"]>')
|
765
|
+
|
766
|
+
get '/', d: '1'
|
767
|
+
expect(last_response.status).to eq(200)
|
768
|
+
expect(last_response.body).to eq('1')
|
769
|
+
|
770
|
+
get '/', d: 'one'
|
771
|
+
expect(last_response.status).to eq(200)
|
772
|
+
expect(last_response.body).to eq('"one"')
|
773
|
+
|
774
|
+
get '/', d: %w(1 two)
|
775
|
+
expect(last_response.status).to eq(200)
|
776
|
+
expect(last_response.body).to eq('#<Set: {1, "two"}>')
|
777
|
+
end
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
672
781
|
context 'custom coercion rules' do
|
673
782
|
before do
|
674
783
|
subject.params do
|