grape 1.7.1 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/CONTRIBUTING.md +1 -1
- data/README.md +12 -4
- data/grape.gemspec +2 -2
- data/lib/grape/api.rb +2 -2
- data/lib/grape/content_types.rb +2 -8
- data/lib/grape/dsl/desc.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +5 -5
- data/lib/grape/dsl/request_response.rb +2 -1
- data/lib/grape/dsl/settings.rb +2 -6
- data/lib/grape/endpoint.rb +19 -17
- data/lib/grape/error_formatter/base.rb +1 -1
- data/lib/grape/exceptions/base.rb +2 -2
- data/lib/grape/exceptions/missing_group_type.rb +1 -6
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
- data/lib/grape/exceptions/validation_errors.rb +1 -6
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
- data/lib/grape/extensions/hash.rb +4 -7
- data/lib/grape/extensions/hashie/mash.rb +3 -3
- data/lib/grape/formatter/serializable_hash.rb +7 -7
- data/lib/grape/middleware/auth/base.rb +1 -1
- data/lib/grape/middleware/error.rb +1 -1
- data/lib/grape/middleware/formatter.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +11 -19
- data/lib/grape/router/route.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -11
- data/lib/grape/util/strict_hash_configuration.rb +3 -4
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +8 -2
- data/lib/grape/validations/single_attribute_iterator.rb +3 -1
- data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
- data/lib/grape/validations/validators/base.rb +9 -20
- data/lib/grape/validations/validators/default_validator.rb +2 -20
- data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
- data/lib/grape/validations/validators/values_validator.rb +14 -5
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +11 -3
- data/spec/grape/api/custom_validations_spec.rb +14 -57
- data/spec/grape/api_remount_spec.rb +36 -0
- data/spec/grape/api_spec.rb +10 -1
- data/spec/grape/dsl/desc_spec.rb +84 -87
- data/spec/grape/dsl/inside_route_spec.rb +6 -10
- data/spec/grape/dsl/request_response_spec.rb +21 -2
- data/spec/grape/endpoint_spec.rb +8 -8
- data/spec/grape/exceptions/body_parse_errors_spec.rb +40 -0
- data/spec/grape/exceptions/missing_group_type_spec.rb +5 -9
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +5 -9
- data/spec/grape/grape_spec.rb +9 -0
- data/spec/grape/middleware/formatter_spec.rb +1 -1
- data/spec/grape/request_spec.rb +4 -14
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +6 -8
- data/spec/grape/validations/single_attribute_iterator_spec.rb +8 -9
- data/spec/grape/validations/validators/base_spec.rb +38 -0
- data/spec/grape/validations/validators/values_spec.rb +37 -0
- data/spec/grape/validations_spec.rb +7 -6
- data/spec/shared/deprecated_class_examples.rb +16 -0
- metadata +17 -22
- data/lib/grape/config.rb +0 -34
- data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
- data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
- data/spec/grape/config_spec.rb +0 -17
data/spec/grape/dsl/desc_spec.rb
CHANGED
@@ -1,101 +1,98 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
describe Grape::DSL::Desc do
|
4
|
+
subject { dummy_class }
|
5
|
+
|
6
|
+
let(:dummy_class) do
|
7
|
+
Class.new do
|
8
|
+
extend Grape::DSL::Desc
|
9
9
|
end
|
10
|
-
|
11
|
-
subject { Class.new(DescSpec::Dummy) }
|
10
|
+
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
describe '.desc' do
|
13
|
+
it 'sets a description' do
|
14
|
+
desc_text = 'The description'
|
15
|
+
options = { message: 'none' }
|
16
|
+
subject.desc desc_text, options
|
17
|
+
expect(subject.namespace_setting(:description)).to eq(options.merge(description: desc_text))
|
18
|
+
expect(subject.route_setting(:description)).to eq(options.merge(description: desc_text))
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
}
|
42
|
-
],
|
43
|
-
hidden: false,
|
44
|
-
deprecated: false,
|
45
|
-
is_array: true,
|
46
|
-
nickname: 'nickname',
|
47
|
-
produces: %w[array of mime_types],
|
48
|
-
consumes: %w[array of mime_types],
|
49
|
-
tags: %w[tag1 tag2],
|
50
|
-
security: %w[array of security schemes]
|
21
|
+
it 'can be set with a block' do
|
22
|
+
expected_options = {
|
23
|
+
summary: 'summary',
|
24
|
+
description: 'The description',
|
25
|
+
detail: 'more details',
|
26
|
+
params: { first: :param },
|
27
|
+
entity: Object,
|
28
|
+
default: { code: 400, message: 'Invalid' },
|
29
|
+
http_codes: [[401, 'Unauthorized', 'Entities::Error']],
|
30
|
+
named: 'My named route',
|
31
|
+
body_name: 'My body name',
|
32
|
+
headers: [
|
33
|
+
XAuthToken: {
|
34
|
+
description: 'Valdates your identity',
|
35
|
+
required: true
|
36
|
+
},
|
37
|
+
XOptionalHeader: {
|
38
|
+
description: 'Not really needed',
|
39
|
+
required: false
|
51
40
|
}
|
41
|
+
],
|
42
|
+
hidden: false,
|
43
|
+
deprecated: false,
|
44
|
+
is_array: true,
|
45
|
+
nickname: 'nickname',
|
46
|
+
produces: %w[array of mime_types],
|
47
|
+
consumes: %w[array of mime_types],
|
48
|
+
tags: %w[tag1 tag2],
|
49
|
+
security: %w[array of security schemes]
|
50
|
+
}
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
52
|
+
subject.desc 'The description' do
|
53
|
+
summary 'summary'
|
54
|
+
detail 'more details'
|
55
|
+
params(first: :param)
|
56
|
+
success Object
|
57
|
+
default code: 400, message: 'Invalid'
|
58
|
+
failure [[401, 'Unauthorized', 'Entities::Error']]
|
59
|
+
named 'My named route'
|
60
|
+
body_name 'My body name'
|
61
|
+
headers [
|
62
|
+
XAuthToken: {
|
63
|
+
description: 'Valdates your identity',
|
64
|
+
required: true
|
65
|
+
},
|
66
|
+
XOptionalHeader: {
|
67
|
+
description: 'Not really needed',
|
68
|
+
required: false
|
69
|
+
}
|
70
|
+
]
|
71
|
+
hidden false
|
72
|
+
deprecated false
|
73
|
+
is_array true
|
74
|
+
nickname 'nickname'
|
75
|
+
produces %w[array of mime_types]
|
76
|
+
consumes %w[array of mime_types]
|
77
|
+
tags %w[tag1 tag2]
|
78
|
+
security %w[array of security schemes]
|
79
|
+
end
|
81
80
|
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
expect(subject.namespace_setting(:description)).to eq(expected_options)
|
82
|
+
expect(subject.route_setting(:description)).to eq(expected_options)
|
83
|
+
end
|
85
84
|
|
86
|
-
|
87
|
-
|
85
|
+
it 'can be set with options and a block' do
|
86
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with('Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.')
|
88
87
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
expect(subject.namespace_setting(:description)).to eq(description: desc_text, detail: detail_text)
|
96
|
-
expect(subject.route_setting(:description)).to eq(description: desc_text, detail: detail_text)
|
97
|
-
end
|
88
|
+
desc_text = 'The description'
|
89
|
+
detail_text = 'more details'
|
90
|
+
options = { message: 'none' }
|
91
|
+
subject.desc desc_text, options do
|
92
|
+
detail detail_text
|
98
93
|
end
|
94
|
+
expect(subject.namespace_setting(:description)).to eq(description: desc_text, detail: detail_text)
|
95
|
+
expect(subject.route_setting(:description)).to eq(description: desc_text, detail: detail_text)
|
99
96
|
end
|
100
97
|
end
|
101
98
|
end
|
@@ -203,16 +203,12 @@ describe Grape::Endpoint do
|
|
203
203
|
end
|
204
204
|
|
205
205
|
describe '#file' do
|
206
|
-
before do
|
207
|
-
allow(subject).to receive(:warn)
|
208
|
-
end
|
209
|
-
|
210
206
|
describe 'set' do
|
211
207
|
context 'as file path' do
|
212
208
|
let(:file_path) { '/some/file/path' }
|
213
209
|
|
214
210
|
it 'emits a warning that this method is deprecated' do
|
215
|
-
expect(
|
211
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with(/Use sendfile or stream/)
|
216
212
|
|
217
213
|
subject.file file_path
|
218
214
|
end
|
@@ -228,7 +224,7 @@ describe Grape::Endpoint do
|
|
228
224
|
let(:file_object) { double('StreamerObject', each: nil) }
|
229
225
|
|
230
226
|
it 'emits a warning that this method is deprecated' do
|
231
|
-
expect(
|
227
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with(/Use stream to use a Stream object/)
|
232
228
|
|
233
229
|
subject.file file_object
|
234
230
|
end
|
@@ -243,7 +239,7 @@ describe Grape::Endpoint do
|
|
243
239
|
|
244
240
|
describe 'get' do
|
245
241
|
it 'emits a warning that this method is deprecated' do
|
246
|
-
expect(
|
242
|
+
expect(ActiveSupport::Deprecation).to receive(:warn).with(/Use sendfile or stream/)
|
247
243
|
|
248
244
|
subject.file
|
249
245
|
end
|
@@ -273,7 +269,7 @@ describe Grape::Endpoint do
|
|
273
269
|
end
|
274
270
|
|
275
271
|
it 'sends no deprecation warnings' do
|
276
|
-
expect(
|
272
|
+
expect(ActiveSupport::Deprecation).not_to receive(:warn)
|
277
273
|
|
278
274
|
subject.sendfile file_path
|
279
275
|
end
|
@@ -334,7 +330,7 @@ describe Grape::Endpoint do
|
|
334
330
|
end
|
335
331
|
|
336
332
|
it 'emits no deprecation warnings' do
|
337
|
-
expect(
|
333
|
+
expect(ActiveSupport::Deprecation).not_to receive(:warn)
|
338
334
|
|
339
335
|
subject.stream file_path
|
340
336
|
end
|
@@ -384,7 +380,7 @@ describe Grape::Endpoint do
|
|
384
380
|
end
|
385
381
|
|
386
382
|
it 'emits no deprecation warnings' do
|
387
|
-
expect(
|
383
|
+
expect(ActiveSupport::Deprecation).not_to receive(:warn)
|
388
384
|
|
389
385
|
subject.stream stream_object
|
390
386
|
end
|
@@ -148,13 +148,32 @@ module Grape
|
|
148
148
|
it 'sets rescue all to true' do
|
149
149
|
expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
|
150
150
|
expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true)
|
151
|
+
expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, nil)
|
151
152
|
subject.rescue_from :grape_exceptions
|
152
153
|
end
|
153
154
|
|
154
|
-
it 'sets
|
155
|
+
it 'sets given proc as rescue handler' do
|
156
|
+
rescue_handler_proc = proc {}
|
155
157
|
expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
|
156
158
|
expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true)
|
157
|
-
subject.
|
159
|
+
expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, rescue_handler_proc)
|
160
|
+
subject.rescue_from :grape_exceptions, rescue_handler_proc
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'sets given block as rescue handler' do
|
164
|
+
rescue_handler_proc = proc {}
|
165
|
+
expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
|
166
|
+
expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true)
|
167
|
+
expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, rescue_handler_proc)
|
168
|
+
subject.rescue_from :grape_exceptions, &rescue_handler_proc
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'sets a rescue handler declared through :with option' do
|
172
|
+
with_block = -> { 'hello' }
|
173
|
+
expect(subject).to receive(:namespace_inheritable).with(:rescue_all, true)
|
174
|
+
expect(subject).to receive(:namespace_inheritable).with(:rescue_grape_exceptions, true)
|
175
|
+
expect(subject).to receive(:namespace_inheritable).with(:grape_exceptions_rescue_handler, an_instance_of(Proc))
|
176
|
+
subject.rescue_from :grape_exceptions, with: with_block
|
158
177
|
end
|
159
178
|
end
|
160
179
|
|
data/spec/grape/endpoint_spec.rb
CHANGED
@@ -138,10 +138,9 @@ describe Grape::Endpoint do
|
|
138
138
|
|
139
139
|
it 'includes request headers' do
|
140
140
|
get '/headers'
|
141
|
-
expect(JSON.parse(last_response.body)).to
|
141
|
+
expect(JSON.parse(last_response.body)).to include(
|
142
142
|
'Host' => 'example.org',
|
143
|
-
'Cookie' => ''
|
144
|
-
'Version' => 'HTTP/1.0'
|
143
|
+
'Cookie' => ''
|
145
144
|
)
|
146
145
|
end
|
147
146
|
|
@@ -174,7 +173,7 @@ describe Grape::Endpoint do
|
|
174
173
|
|
175
174
|
get('/get/cookies')
|
176
175
|
|
177
|
-
expect(last_response.headers['Set-Cookie'].split("\n").sort).to eql [
|
176
|
+
expect(Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }.sort).to eql [
|
178
177
|
'cookie3=symbol',
|
179
178
|
'cookie4=secret+code+here',
|
180
179
|
'my-awesome-cookie1=is+cool',
|
@@ -199,8 +198,9 @@ describe Grape::Endpoint do
|
|
199
198
|
end
|
200
199
|
get('/username', {}, 'HTTP_COOKIE' => 'username=user; sandbox=false')
|
201
200
|
expect(last_response.body).to eq('user_test')
|
202
|
-
|
203
|
-
expect(
|
201
|
+
cookies = Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }
|
202
|
+
expect(cookies.first).to match(/username=user_test/)
|
203
|
+
expect(cookies.second).to match(/sandbox=true/)
|
204
204
|
end
|
205
205
|
|
206
206
|
it 'deletes cookie' do
|
@@ -214,7 +214,7 @@ describe Grape::Endpoint do
|
|
214
214
|
end
|
215
215
|
get '/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2'
|
216
216
|
expect(last_response.body).to eq('3')
|
217
|
-
cookies = last_response.headers['Set-Cookie'].split("\n").to_h do |set_cookie|
|
217
|
+
cookies = Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }.to_h do |set_cookie|
|
218
218
|
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
219
219
|
[cookie.name, cookie]
|
220
220
|
end
|
@@ -238,7 +238,7 @@ describe Grape::Endpoint do
|
|
238
238
|
end
|
239
239
|
get('/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2')
|
240
240
|
expect(last_response.body).to eq('3')
|
241
|
-
cookies = last_response.headers['Set-Cookie'].split("\n").to_h do |set_cookie|
|
241
|
+
cookies = Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }.to_h do |set_cookie|
|
242
242
|
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
243
243
|
[cookie.name, cookie]
|
244
244
|
end
|
@@ -91,6 +91,46 @@ describe Grape::Exceptions::ValidationErrors do
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
+
context 'api with rescue_from :grape_exceptions handler with block' do
|
95
|
+
subject { Class.new(Grape::API) }
|
96
|
+
|
97
|
+
before do
|
98
|
+
subject.rescue_from :grape_exceptions do |e|
|
99
|
+
rack_response "Custom Error Contents, Original Message: #{e.message}", 400
|
100
|
+
end
|
101
|
+
|
102
|
+
subject.params do
|
103
|
+
requires :beer
|
104
|
+
end
|
105
|
+
|
106
|
+
subject.post '/beer' do
|
107
|
+
'beer received'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def app
|
112
|
+
subject
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'with content_type json' do
|
116
|
+
it 'returns body parsing error message' do
|
117
|
+
post '/beer', 'test', 'CONTENT_TYPE' => 'application/json'
|
118
|
+
expect(last_response.status).to eq 400
|
119
|
+
expect(last_response.body).to include 'message body does not match declared format'
|
120
|
+
expect(last_response.body).to include 'Custom Error Contents, Original Message'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'with content_type xml' do
|
125
|
+
it 'returns body parsing error message' do
|
126
|
+
post '/beer', 'test', 'CONTENT_TYPE' => 'application/xml'
|
127
|
+
expect(last_response.status).to eq 400
|
128
|
+
expect(last_response.body).to include 'message body does not match declared format'
|
129
|
+
expect(last_response.body).to include 'Custom Error Contents, Original Message'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
94
134
|
context 'api without a rescue handler' do
|
95
135
|
subject { Class.new(Grape::API) }
|
96
136
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'shared/deprecated_class_examples'
|
4
|
+
|
3
5
|
RSpec.describe Grape::Exceptions::MissingGroupType do
|
4
6
|
describe '#message' do
|
5
7
|
subject { described_class.new.message }
|
@@ -7,15 +9,9 @@ RSpec.describe Grape::Exceptions::MissingGroupType do
|
|
7
9
|
it { is_expected.to include 'group type is required' }
|
8
10
|
end
|
9
11
|
|
10
|
-
describe '
|
11
|
-
|
12
|
-
|
13
|
-
it 'puts a deprecation warning' do
|
14
|
-
expect(Warning).to receive(:warn) do |message|
|
15
|
-
expect(message).to include('`Grape::Exceptions::MissingGroupTypeError` is deprecated')
|
16
|
-
end
|
12
|
+
describe 'Grape::Exceptions::MissingGroupTypeError' do
|
13
|
+
let(:deprecated_class) { Grape::Exceptions::MissingGroupTypeError }
|
17
14
|
|
18
|
-
|
19
|
-
end
|
15
|
+
it_behaves_like 'deprecated class'
|
20
16
|
end
|
21
17
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'shared/deprecated_class_examples'
|
4
|
+
|
3
5
|
RSpec.describe Grape::Exceptions::UnsupportedGroupType do
|
4
6
|
subject { described_class.new }
|
5
7
|
|
@@ -9,15 +11,9 @@ RSpec.describe Grape::Exceptions::UnsupportedGroupType do
|
|
9
11
|
it { is_expected.to include 'group type must be Array, Hash, JSON or Array[JSON]' }
|
10
12
|
end
|
11
13
|
|
12
|
-
describe '
|
13
|
-
|
14
|
-
|
15
|
-
it 'puts a deprecation warning' do
|
16
|
-
expect(Warning).to receive(:warn) do |message|
|
17
|
-
expect(message).to include('`Grape::Exceptions::UnsupportedGroupTypeError` is deprecated')
|
18
|
-
end
|
14
|
+
describe 'Grape::Exceptions::UnsupportedGroupTypeError' do
|
15
|
+
let(:deprecated_class) { Grape::Exceptions::UnsupportedGroupTypeError }
|
19
16
|
|
20
|
-
|
21
|
-
end
|
17
|
+
it_behaves_like 'deprecated class'
|
22
18
|
end
|
23
19
|
end
|
@@ -405,7 +405,7 @@ describe Grape::Middleware::Formatter do
|
|
405
405
|
env = { 'PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json' }
|
406
406
|
status, headers, body = subject.call(env)
|
407
407
|
expect(status).to be == 200
|
408
|
-
expect(headers).to be == { '
|
408
|
+
expect(headers.transform_keys(&:downcase)).to be == { 'content-type' => 'application/json' }
|
409
409
|
expect(read_chunks(body)).to be == ['data']
|
410
410
|
end
|
411
411
|
end
|
data/spec/grape/request_spec.rb
CHANGED
@@ -62,29 +62,19 @@ module Grape
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
describe 'when the
|
65
|
+
describe 'when the build_params_with is set to Hashie' do
|
66
66
|
subject(:request_params) { described_class.new(env, **opts).params }
|
67
67
|
|
68
|
-
before do
|
69
|
-
Grape.configure do |config|
|
70
|
-
config.param_builder = Grape::Extensions::Hashie::Mash::ParamBuilder
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
after do
|
75
|
-
Grape.config.reset
|
76
|
-
end
|
77
|
-
|
78
68
|
context 'when the API does not include a specific param builder' do
|
79
69
|
let(:opts) { {} }
|
80
70
|
|
81
|
-
it { is_expected.to be_a(
|
71
|
+
it { is_expected.to be_a(Hash) }
|
82
72
|
end
|
83
73
|
|
84
74
|
context 'when the API includes a specific param builder' do
|
85
|
-
let(:opts) { { build_params_with: Grape::Extensions::
|
75
|
+
let(:opts) { { build_params_with: Grape::Extensions::Hashie::Mash::ParamBuilder } }
|
86
76
|
|
87
|
-
it { is_expected.to be_a(
|
77
|
+
it { is_expected.to be_a(Hashie::Mash) }
|
88
78
|
end
|
89
79
|
end
|
90
80
|
|
@@ -12,8 +12,8 @@ describe Grape::Validations::MultipleAttributesIterator do
|
|
12
12
|
{ first: 'string', second: 'string' }
|
13
13
|
end
|
14
14
|
|
15
|
-
it 'yields the whole params hash
|
16
|
-
expect { |b| iterator.each(&b) }.to yield_with_args(params
|
15
|
+
it 'yields the whole params hash without the list of attrs' do
|
16
|
+
expect { |b| iterator.each(&b) }.to yield_with_args(params)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -23,17 +23,15 @@ describe Grape::Validations::MultipleAttributesIterator do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'yields each element of the array without the list of attrs' do
|
26
|
-
expect { |b| iterator.each(&b) }.to yield_successive_args(
|
26
|
+
expect { |b| iterator.each(&b) }.to yield_successive_args(params[0], params[1])
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
context 'when params is empty optional placeholder' do
|
31
|
-
let(:params)
|
32
|
-
[Grape::DSL::Parameters::EmptyOptionalValue, { first: 'string2', second: 'string2' }]
|
33
|
-
end
|
31
|
+
let(:params) { [Grape::DSL::Parameters::EmptyOptionalValue] }
|
34
32
|
|
35
|
-
it '
|
36
|
-
expect { |b| iterator.each(&b) }.to yield_successive_args
|
33
|
+
it 'does not yield it' do
|
34
|
+
expect { |b| iterator.each(&b) }.to yield_successive_args
|
37
35
|
end
|
38
36
|
end
|
39
37
|
end
|
@@ -14,7 +14,7 @@ describe Grape::Validations::SingleAttributeIterator do
|
|
14
14
|
|
15
15
|
it 'yields params and every single attribute from the list' do
|
16
16
|
expect { |b| iterator.each(&b) }
|
17
|
-
.to yield_successive_args([params, :first, false
|
17
|
+
.to yield_successive_args([params, :first, false], [params, :second, false])
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -25,8 +25,8 @@ describe Grape::Validations::SingleAttributeIterator do
|
|
25
25
|
|
26
26
|
it 'yields every single attribute from the list for each of the array elements' do
|
27
27
|
expect { |b| iterator.each(&b) }.to yield_successive_args(
|
28
|
-
[params[0], :first, false
|
29
|
-
[params[1], :first, false
|
28
|
+
[params[0], :first, false], [params[0], :second, false],
|
29
|
+
[params[1], :first, false], [params[1], :second, false]
|
30
30
|
)
|
31
31
|
end
|
32
32
|
|
@@ -35,9 +35,9 @@ describe Grape::Validations::SingleAttributeIterator do
|
|
35
35
|
|
36
36
|
it 'marks params with empty values' do
|
37
37
|
expect { |b| iterator.each(&b) }.to yield_successive_args(
|
38
|
-
[params[0], :first, true
|
39
|
-
[params[1], :first, true
|
40
|
-
[params[2], :first, false
|
38
|
+
[params[0], :first, true], [params[0], :second, true],
|
39
|
+
[params[1], :first, true], [params[1], :second, true],
|
40
|
+
[params[2], :first, false], [params[2], :second, false]
|
41
41
|
)
|
42
42
|
end
|
43
43
|
end
|
@@ -45,10 +45,9 @@ describe Grape::Validations::SingleAttributeIterator do
|
|
45
45
|
context 'when missing optional value' do
|
46
46
|
let(:params) { [Grape::DSL::Parameters::EmptyOptionalValue, 10] }
|
47
47
|
|
48
|
-
it '
|
48
|
+
it 'does not yield skipped values' do
|
49
49
|
expect { |b| iterator.each(&b) }.to yield_successive_args(
|
50
|
-
[params[
|
51
|
-
[params[1], :first, false, false], [params[1], :second, false, false]
|
50
|
+
[params[1], :first, false], [params[1], :second, false]
|
52
51
|
)
|
53
52
|
end
|
54
53
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Grape::Validations::Validators::Base do
|
4
|
+
describe '#inherited' do
|
5
|
+
context 'when validator is anonymous' do
|
6
|
+
subject(:custom_validator) { Class.new(described_class) }
|
7
|
+
|
8
|
+
it 'does not register the validator' do
|
9
|
+
expect(Grape::Validations).not_to receive(:register_validator)
|
10
|
+
custom_validator
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Anonymous class does not have a name and class A < B would leak.
|
15
|
+
# Simulates inherited callback
|
16
|
+
context "when validator's underscored name does not end with _validator" do
|
17
|
+
subject(:custom_validator) { described_class.inherited(TestModule::CustomValidatorABC) }
|
18
|
+
|
19
|
+
before { stub_const('TestModule::CustomValidatorABC', Class.new) }
|
20
|
+
|
21
|
+
it 'registers the custom validator with a short name' do
|
22
|
+
expect(Grape::Validations).to receive(:register_validator).with('custom_validator_abc', TestModule::CustomValidatorABC)
|
23
|
+
custom_validator
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when validator's underscored name ends with _validator" do
|
28
|
+
subject(:custom_validator) { described_class.inherited(TestModule::CustomValidator) }
|
29
|
+
|
30
|
+
before { stub_const('TestModule::CustomValidator', Class.new) }
|
31
|
+
|
32
|
+
it 'registers the custom validator with short name not ending with validator' do
|
33
|
+
expect(Grape::Validations).to receive(:register_validator).with('custom', TestModule::CustomValidator)
|
34
|
+
custom_validator
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|