grape 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/CONTRIBUTING.md +31 -1
  4. data/README.md +38 -8
  5. data/grape.gemspec +2 -2
  6. data/lib/grape/api.rb +2 -2
  7. data/lib/grape/content_types.rb +2 -8
  8. data/lib/grape/dsl/desc.rb +3 -2
  9. data/lib/grape/dsl/inside_route.rb +6 -6
  10. data/lib/grape/dsl/parameters.rb +6 -1
  11. data/lib/grape/dsl/request_response.rb +3 -2
  12. data/lib/grape/dsl/settings.rb +2 -6
  13. data/lib/grape/endpoint.rb +21 -19
  14. data/lib/grape/error_formatter/base.rb +1 -1
  15. data/lib/grape/exceptions/base.rb +4 -3
  16. data/lib/grape/exceptions/missing_group_type.rb +1 -6
  17. data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
  18. data/lib/grape/exceptions/validation_errors.rb +1 -6
  19. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
  20. data/lib/grape/extensions/hash.rb +4 -7
  21. data/lib/grape/extensions/hashie/mash.rb +3 -3
  22. data/lib/grape/formatter/serializable_hash.rb +7 -7
  23. data/lib/grape/middleware/auth/base.rb +1 -1
  24. data/lib/grape/middleware/error.rb +1 -1
  25. data/lib/grape/middleware/formatter.rb +1 -1
  26. data/lib/grape/middleware/stack.rb +1 -1
  27. data/lib/grape/middleware/versioner/header.rb +11 -19
  28. data/lib/grape/request.rb +1 -1
  29. data/lib/grape/router/attribute_translator.rb +1 -1
  30. data/lib/grape/router/route.rb +1 -3
  31. data/lib/grape/types/invalid_value.rb +8 -0
  32. data/lib/grape/util/cache.rb +1 -1
  33. data/lib/grape/util/lazy_value.rb +3 -11
  34. data/lib/grape/util/strict_hash_configuration.rb +3 -4
  35. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  36. data/lib/grape/validations/params_scope.rb +9 -3
  37. data/lib/grape/validations/single_attribute_iterator.rb +3 -1
  38. data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
  39. data/lib/grape/validations/types/invalid_value.rb +0 -7
  40. data/lib/grape/validations/validators/base.rb +9 -20
  41. data/lib/grape/validations/validators/default_validator.rb +2 -20
  42. data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
  43. data/lib/grape/validations/validators/values_validator.rb +14 -5
  44. data/lib/grape/version.rb +1 -1
  45. data/lib/grape.rb +19 -3
  46. data/spec/grape/api/custom_validations_spec.rb +14 -57
  47. data/spec/grape/api_remount_spec.rb +36 -0
  48. data/spec/grape/api_spec.rb +15 -21
  49. data/spec/grape/dsl/desc_spec.rb +84 -85
  50. data/spec/grape/dsl/inside_route_spec.rb +6 -10
  51. data/spec/grape/dsl/request_response_spec.rb +21 -2
  52. data/spec/grape/endpoint_spec.rb +11 -10
  53. data/spec/grape/exceptions/body_parse_errors_spec.rb +40 -0
  54. data/spec/grape/exceptions/invalid_accept_header_spec.rb +3 -0
  55. data/spec/grape/exceptions/missing_group_type_spec.rb +5 -9
  56. data/spec/grape/exceptions/unsupported_group_type_spec.rb +5 -9
  57. data/spec/grape/grape_spec.rb +9 -0
  58. data/spec/grape/integration/rack_spec.rb +6 -5
  59. data/spec/grape/middleware/base_spec.rb +7 -5
  60. data/spec/grape/middleware/formatter_spec.rb +7 -7
  61. data/spec/grape/request_spec.rb +4 -14
  62. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +6 -8
  63. data/spec/grape/validations/single_attribute_iterator_spec.rb +8 -9
  64. data/spec/grape/validations/validators/base_spec.rb +38 -0
  65. data/spec/grape/validations/validators/values_spec.rb +56 -0
  66. data/spec/grape/validations_spec.rb +36 -12
  67. data/spec/shared/deprecated_class_examples.rb +16 -0
  68. metadata +112 -114
  69. data/lib/grape/config.rb +0 -34
  70. data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
  71. data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
  72. data/spec/grape/config_spec.rb +0 -17
  73. data/spec/grape/dsl/configuration_spec.rb +0 -14
  74. data/spec/grape/validations/attributes_iterator_spec.rb +0 -4
@@ -1,48 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- describe Grape::Validations do
4
- context 'deprecated Grape::Validations::Base' do
5
- subject do
6
- Class.new(Grape::API) do
7
- params do
8
- requires :text, validator_with_old_base: true
9
- end
10
- get do
11
- end
12
- end
13
- end
14
-
15
- let(:validator_with_old_base) do
16
- Class.new(Grape::Validations::Base) do
17
- def validate_param!(_attr_name, _params)
18
- true
19
- end
20
- end
21
- end
22
-
23
- before do
24
- described_class.register_validator('validator_with_old_base', validator_with_old_base)
25
- allow(Warning).to receive(:warn)
26
- end
3
+ require 'shared/deprecated_class_examples'
27
4
 
28
- after do
29
- described_class.deregister_validator('validator_with_old_base')
30
- end
31
-
32
- def app
33
- subject
5
+ describe Grape::Validations do
6
+ describe 'Grape::Validations::Base' do
7
+ let(:deprecated_class) do
8
+ Class.new(Grape::Validations::Base)
34
9
  end
35
10
 
36
- it 'puts a deprecation warning' do
37
- expect(Warning).to receive(:warn) do |message|
38
- expect(message).to include('`Grape::Validations::Base` is deprecated')
39
- end
40
-
41
- get '/'
42
- end
11
+ it_behaves_like 'deprecated class'
43
12
  end
44
13
 
45
- context 'using a custom length validator' do
14
+ describe 'using a custom length validator' do
46
15
  subject do
47
16
  Class.new(Grape::API) do
48
17
  params do
@@ -64,6 +33,7 @@ describe Grape::Validations do
64
33
  end
65
34
  end
66
35
  end
36
+ let(:app) { Rack::Builder.new(subject) }
67
37
 
68
38
  before do
69
39
  described_class.register_validator('default_length', default_length_validator)
@@ -73,10 +43,6 @@ describe Grape::Validations do
73
43
  described_class.deregister_validator('default_length')
74
44
  end
75
45
 
76
- def app
77
- subject
78
- end
79
-
80
46
  it 'under 140 characters' do
81
47
  get '/', text: 'abc'
82
48
  expect(last_response.status).to eq 200
@@ -96,7 +62,7 @@ describe Grape::Validations do
96
62
  end
97
63
  end
98
64
 
99
- context 'using a custom body-only validator' do
65
+ describe 'using a custom body-only validator' do
100
66
  subject do
101
67
  Class.new(Grape::API) do
102
68
  params do
@@ -115,6 +81,7 @@ describe Grape::Validations do
115
81
  end
116
82
  end
117
83
  end
84
+ let(:app) { Rack::Builder.new(subject) }
118
85
 
119
86
  before do
120
87
  described_class.register_validator('in_body', in_body_validator)
@@ -124,10 +91,6 @@ describe Grape::Validations do
124
91
  described_class.deregister_validator('in_body')
125
92
  end
126
93
 
127
- def app
128
- subject
129
- end
130
-
131
94
  it 'allows field in body' do
132
95
  get '/', text: 'abc'
133
96
  expect(last_response.status).to eq 200
@@ -141,7 +104,7 @@ describe Grape::Validations do
141
104
  end
142
105
  end
143
106
 
144
- context 'using a custom validator with message_key' do
107
+ describe 'using a custom validator with message_key' do
145
108
  subject do
146
109
  Class.new(Grape::API) do
147
110
  params do
@@ -160,6 +123,7 @@ describe Grape::Validations do
160
123
  end
161
124
  end
162
125
  end
126
+ let(:app) { Rack::Builder.new(subject) }
163
127
 
164
128
  before do
165
129
  described_class.register_validator('with_message_key', message_key_validator)
@@ -169,10 +133,6 @@ describe Grape::Validations do
169
133
  described_class.deregister_validator('with_message_key')
170
134
  end
171
135
 
172
- def app
173
- subject
174
- end
175
-
176
136
  it 'fails with message' do
177
137
  get '/', text: 'foobar'
178
138
  expect(last_response.status).to eq 400
@@ -180,7 +140,7 @@ describe Grape::Validations do
180
140
  end
181
141
  end
182
142
 
183
- context 'using a custom request/param validator' do
143
+ describe 'using a custom request/param validator' do
184
144
  subject do
185
145
  Class.new(Grape::API) do
186
146
  params do
@@ -208,6 +168,7 @@ describe Grape::Validations do
208
168
  end
209
169
  end
210
170
  end
171
+ let(:app) { Rack::Builder.new(subject) }
211
172
 
212
173
  before do
213
174
  described_class.register_validator('admin', admin_validator)
@@ -217,10 +178,6 @@ describe Grape::Validations do
217
178
  described_class.deregister_validator('admin')
218
179
  end
219
180
 
220
- def app
221
- subject
222
- end
223
-
224
181
  it 'fail when non-admin user sets an admin field' do
225
182
  get '/', admin_field: 'tester', non_admin_field: 'toaster'
226
183
  expect(last_response.status).to eq 400
@@ -147,6 +147,42 @@ describe Grape::API do
147
147
  end
148
148
  end
149
149
 
150
+ context 'when the params are configured via a configuration' do
151
+ subject(:a_remounted_api) do
152
+ Class.new(described_class) do
153
+ params do
154
+ requires configuration[:required_attr_name], type: String
155
+ end
156
+
157
+ get(mounted { configuration[:endpoint] }) do
158
+ status 200
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'when the configured param is my_attr' do
164
+ it 'requires the configured params' do
165
+ root_api.mount a_remounted_api, with: {
166
+ required_attr_name: 'my_attr',
167
+ endpoint: 'test'
168
+ }
169
+ get 'test?another_attr=1'
170
+ expect(last_response.status).to eq 400
171
+ get 'test?my_attr=1'
172
+ expect(last_response.status).to eq 200
173
+
174
+ root_api.mount a_remounted_api, with: {
175
+ required_attr_name: 'another_attr',
176
+ endpoint: 'test_b'
177
+ }
178
+ get 'test_b?another_attr=1'
179
+ expect(last_response.status).to eq 200
180
+ get 'test_b?my_attr=1'
181
+ expect(last_response.status).to eq 400
182
+ end
183
+ end
184
+ end
185
+
150
186
  context 'when executing a standard block within a `mounted` block with all dynamic params' do
151
187
  subject(:a_remounted_api) do
152
188
  Class.new(described_class) do
@@ -103,22 +103,6 @@ describe Grape::API do
103
103
  }
104
104
  end
105
105
  end
106
-
107
- # Behavior as defined by rfc2616 when no header is defined
108
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
109
- describe 'no specified accept header' do
110
- # subject.version 'v1', using: :header
111
- # subject.get '/hello' do
112
- # 'hello'
113
- # end
114
-
115
- # it 'routes' do
116
- # get '/hello'
117
- # last_response.status.should eql 200
118
- # end
119
- end
120
-
121
- # pending 'routes if any media type is allowed'
122
106
  end
123
107
 
124
108
  describe '.version using accept_version_header' do
@@ -448,9 +432,10 @@ describe Grape::API do
448
432
  expect(last_response.body).to eql 'hiya'
449
433
  end
450
434
 
435
+ objects = ['string', :symbol, 1, -1.1, {}, [], true, false, nil].freeze
451
436
  %i[put post].each do |verb|
452
437
  context verb.to_s do
453
- ['string', :symbol, 1, -1.1, {}, [], true, false, nil].each do |object|
438
+ objects.each do |object|
454
439
  it "allows a(n) #{object.class} json object in params" do
455
440
  subject.format :json
456
441
  subject.send(verb) do
@@ -1601,7 +1586,7 @@ describe Grape::API do
1601
1586
 
1602
1587
  subject.get(:hello) { 'Hello, world.' }
1603
1588
  get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
1604
- expect(basic_auth_context).to be_a_kind_of(Grape::Endpoint)
1589
+ expect(basic_auth_context).to be_a(Grape::Endpoint)
1605
1590
  end
1606
1591
 
1607
1592
  it 'has access to helper methods' do
@@ -3067,7 +3052,7 @@ describe Grape::API do
3067
3052
  expect(route.description).to eq('first method')
3068
3053
  expect(route.route_foo).to be_nil
3069
3054
  expect(route.params).to eq({})
3070
- expect(route.options).to be_a_kind_of(Hash)
3055
+ expect(route.options).to be_a(Hash)
3071
3056
  end
3072
3057
 
3073
3058
  it 'has params which does not include format and version as named captures' do
@@ -3326,7 +3311,7 @@ describe Grape::API do
3326
3311
  it 'is able to cascade' do
3327
3312
  subject.mount lambda { |env|
3328
3313
  headers = {}
3329
- headers['X-Cascade'] == 'pass' unless env['PATH_INFO'].include?('boo')
3314
+ headers['X-Cascade'] == 'pass' if env['PATH_INFO'].exclude?('boo')
3330
3315
  [200, headers, ['Farfegnugen']]
3331
3316
  } => '/'
3332
3317
 
@@ -3725,7 +3710,7 @@ describe Grape::API do
3725
3710
  it 'sets the instance' do
3726
3711
  expect(subject.instance).to be_nil
3727
3712
  subject.compile
3728
- expect(subject.instance).to be_kind_of(subject.base_instance)
3713
+ expect(subject.instance).to be_a(subject.base_instance)
3729
3714
  end
3730
3715
  end
3731
3716
 
@@ -4241,6 +4226,15 @@ describe Grape::API do
4241
4226
  end
4242
4227
  end
4243
4228
 
4229
+ it 'does not override methods inherited from Class' do
4230
+ Class.define_method(:test_method) {}
4231
+ subclass = Class.new(described_class)
4232
+ expect(subclass).not_to receive(:add_setup)
4233
+ subclass.test_method
4234
+ ensure
4235
+ Class.remove_method(:test_method)
4236
+ end
4237
+
4244
4238
  context 'overriding via composition' do
4245
4239
  module Inherited
4246
4240
  def inherited(api)
@@ -1,99 +1,98 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Grape
4
- module DSL
5
- module DescSpec
6
- class Dummy
7
- extend Grape::DSL::Desc
8
- end
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
- describe Desc do
11
- subject { Class.new(DescSpec::Dummy) }
10
+ end
12
11
 
13
- describe '.desc' do
14
- it 'sets a description' do
15
- desc_text = 'The description'
16
- options = { message: 'none' }
17
- subject.desc desc_text, options
18
- expect(subject.namespace_setting(:description)).to eq(options.merge(description: desc_text))
19
- expect(subject.route_setting(:description)).to eq(options.merge(description: desc_text))
20
- end
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
- it 'can be set with a block' do
23
- expected_options = {
24
- summary: 'summary',
25
- description: 'The description',
26
- detail: 'more details',
27
- params: { first: :param },
28
- entity: Object,
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
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]
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
50
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
+ }
51
51
 
52
- subject.desc 'The description' do
53
- summary 'summary'
54
- detail 'more details'
55
- params(first: :param)
56
- success Object
57
- failure [[401, 'Unauthorized', 'Entities::Error']]
58
- named 'My named route'
59
- body_name 'My body name'
60
- headers [
61
- XAuthToken: {
62
- description: 'Valdates your identity',
63
- required: true
64
- },
65
- XOptionalHeader: {
66
- description: 'Not really needed',
67
- required: false
68
- }
69
- ]
70
- hidden false
71
- deprecated false
72
- is_array true
73
- nickname 'nickname'
74
- produces %w[array of mime_types]
75
- consumes %w[array of mime_types]
76
- tags %w[tag1 tag2]
77
- security %w[array of security schemes]
78
- end
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
79
80
 
80
- expect(subject.namespace_setting(:description)).to eq(expected_options)
81
- expect(subject.route_setting(:description)).to eq(expected_options)
82
- end
81
+ expect(subject.namespace_setting(:description)).to eq(expected_options)
82
+ expect(subject.route_setting(:description)).to eq(expected_options)
83
+ end
83
84
 
84
- it 'can be set with options and a block' do
85
- expect(subject).to receive(:warn).with('[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.')
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.')
86
87
 
87
- desc_text = 'The description'
88
- detail_text = 'more details'
89
- options = { message: 'none' }
90
- subject.desc desc_text, options do
91
- detail detail_text
92
- end
93
- expect(subject.namespace_setting(:description)).to eq(description: desc_text, detail: detail_text)
94
- expect(subject.route_setting(:description)).to eq(description: desc_text, detail: detail_text)
95
- 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
96
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)
97
96
  end
98
97
  end
99
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(subject).to receive(:warn).with(/Use sendfile or stream/)
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(subject).to receive(:warn).with(/Use stream to use a Stream object/)
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(subject).to receive(:warn).with(/Use sendfile or stream/)
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(subject).not_to receive(:warn)
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(subject).not_to receive(:warn)
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(subject).not_to receive(:warn)
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 rescue_grape_exceptions to true' do
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.rescue_from :grape_exceptions
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
 
@@ -75,7 +75,7 @@ describe Grape::Endpoint do
75
75
  it 'sets itself in the env upon call' do
76
76
  subject.get('/') { 'Hello world.' }
77
77
  get '/'
78
- expect(last_request.env['api.endpoint']).to be_kind_of(described_class)
78
+ expect(last_request.env['api.endpoint']).to be_a(described_class)
79
79
  end
80
80
 
81
81
  describe '#status' do
@@ -138,7 +138,7 @@ 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 eq(
141
+ expect(JSON.parse(last_response.body)).to include(
142
142
  'Host' => 'example.org',
143
143
  'Cookie' => ''
144
144
  )
@@ -173,7 +173,7 @@ describe Grape::Endpoint do
173
173
 
174
174
  get('/get/cookies')
175
175
 
176
- 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 [
177
177
  'cookie3=symbol',
178
178
  'cookie4=secret+code+here',
179
179
  'my-awesome-cookie1=is+cool',
@@ -198,8 +198,9 @@ describe Grape::Endpoint do
198
198
  end
199
199
  get('/username', {}, 'HTTP_COOKIE' => 'username=user; sandbox=false')
200
200
  expect(last_response.body).to eq('user_test')
201
- expect(last_response.headers['Set-Cookie']).to match(/username=user_test/)
202
- expect(last_response.headers['Set-Cookie']).to match(/sandbox=true/)
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/)
203
204
  end
204
205
 
205
206
  it 'deletes cookie' do
@@ -213,10 +214,10 @@ describe Grape::Endpoint do
213
214
  end
214
215
  get '/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2'
215
216
  expect(last_response.body).to eq('3')
216
- cookies = last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
217
+ cookies = Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }.to_h do |set_cookie|
217
218
  cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
218
219
  [cookie.name, cookie]
219
- end.to_h
220
+ end
220
221
  expect(cookies.size).to eq(2)
221
222
  %w[and_this delete_this_cookie].each do |cookie_name|
222
223
  cookie = cookies[cookie_name]
@@ -237,10 +238,10 @@ describe Grape::Endpoint do
237
238
  end
238
239
  get('/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2')
239
240
  expect(last_response.body).to eq('3')
240
- cookies = last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
241
+ cookies = Array(last_response.headers['Set-Cookie']).flat_map { |h| h.split("\n") }.to_h do |set_cookie|
241
242
  cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
242
243
  [cookie.name, cookie]
243
- end.to_h
244
+ end
244
245
  expect(cookies.size).to eq(2)
245
246
  %w[and_this delete_this_cookie].each do |cookie_name|
246
247
  cookie = cookies[cookie_name]
@@ -432,7 +433,7 @@ describe Grape::Endpoint do
432
433
  end
433
434
  post '/upload', { file: '' }, 'CONTENT_TYPE' => 'multipart/form-data; boundary=foobar'
434
435
  expect(last_response.status).to eq(400)
435
- expect(last_response.body).to eq('empty message body supplied with multipart/form-data; boundary=foobar content-type')
436
+ expect(last_response.body).to eq('file is invalid')
436
437
  end
437
438
  end
438
439