grape 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|