grape 1.3.3 → 1.6.2

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.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -2
  3. data/CONTRIBUTING.md +2 -1
  4. data/README.md +135 -23
  5. data/UPGRADING.md +237 -46
  6. data/grape.gemspec +5 -5
  7. data/lib/grape/api/instance.rb +34 -42
  8. data/lib/grape/api.rb +21 -16
  9. data/lib/grape/cookies.rb +2 -0
  10. data/lib/grape/dsl/callbacks.rb +1 -1
  11. data/lib/grape/dsl/desc.rb +3 -5
  12. data/lib/grape/dsl/headers.rb +5 -2
  13. data/lib/grape/dsl/helpers.rb +8 -5
  14. data/lib/grape/dsl/inside_route.rb +72 -53
  15. data/lib/grape/dsl/middleware.rb +4 -4
  16. data/lib/grape/dsl/parameters.rb +11 -7
  17. data/lib/grape/dsl/request_response.rb +9 -6
  18. data/lib/grape/dsl/routing.rb +8 -9
  19. data/lib/grape/dsl/settings.rb +5 -5
  20. data/lib/grape/dsl/validations.rb +18 -1
  21. data/lib/grape/eager_load.rb +1 -1
  22. data/lib/grape/endpoint.rb +29 -42
  23. data/lib/grape/error_formatter/json.rb +2 -6
  24. data/lib/grape/error_formatter/xml.rb +2 -6
  25. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  26. data/lib/grape/exceptions/validation.rb +2 -3
  27. data/lib/grape/exceptions/validation_errors.rb +1 -1
  28. data/lib/grape/formatter/json.rb +1 -0
  29. data/lib/grape/formatter/serializable_hash.rb +2 -1
  30. data/lib/grape/formatter/xml.rb +1 -0
  31. data/lib/grape/locale/en.yml +1 -1
  32. data/lib/grape/middleware/auth/base.rb +3 -3
  33. data/lib/grape/middleware/auth/dsl.rb +7 -1
  34. data/lib/grape/middleware/base.rb +6 -3
  35. data/lib/grape/middleware/error.rb +11 -13
  36. data/lib/grape/middleware/formatter.rb +7 -7
  37. data/lib/grape/middleware/stack.rb +10 -3
  38. data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
  39. data/lib/grape/middleware/versioner/header.rb +6 -4
  40. data/lib/grape/middleware/versioner/param.rb +1 -0
  41. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  42. data/lib/grape/middleware/versioner/path.rb +2 -0
  43. data/lib/grape/parser/json.rb +1 -1
  44. data/lib/grape/parser/xml.rb +1 -1
  45. data/lib/grape/path.rb +1 -0
  46. data/lib/grape/request.rb +4 -1
  47. data/lib/grape/router/attribute_translator.rb +3 -3
  48. data/lib/grape/router/pattern.rb +1 -1
  49. data/lib/grape/router/route.rb +2 -2
  50. data/lib/grape/router.rb +31 -30
  51. data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
  52. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
  53. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
  54. data/lib/grape/util/base_inheritable.rb +2 -2
  55. data/lib/grape/util/inheritable_setting.rb +1 -3
  56. data/lib/grape/util/lazy_value.rb +4 -2
  57. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  58. data/lib/grape/validations/attributes_iterator.rb +8 -0
  59. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  60. data/lib/grape/validations/params_scope.rb +97 -62
  61. data/lib/grape/validations/single_attribute_iterator.rb +1 -1
  62. data/lib/grape/validations/types/custom_type_coercer.rb +16 -3
  63. data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
  64. data/lib/grape/validations/types/invalid_value.rb +24 -0
  65. data/lib/grape/validations/types/json.rb +2 -1
  66. data/lib/grape/validations/types/primitive_coercer.rb +4 -5
  67. data/lib/grape/validations/types.rb +1 -4
  68. data/lib/grape/validations/validator_factory.rb +1 -1
  69. data/lib/grape/validations/validators/all_or_none.rb +8 -5
  70. data/lib/grape/validations/validators/allow_blank.rb +9 -7
  71. data/lib/grape/validations/validators/as.rb +6 -8
  72. data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
  73. data/lib/grape/validations/validators/base.rb +74 -69
  74. data/lib/grape/validations/validators/coerce.rb +63 -76
  75. data/lib/grape/validations/validators/default.rb +36 -34
  76. data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
  77. data/lib/grape/validations/validators/except_values.rb +13 -11
  78. data/lib/grape/validations/validators/multiple_params_base.rb +24 -19
  79. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
  80. data/lib/grape/validations/validators/presence.rb +7 -4
  81. data/lib/grape/validations/validators/regexp.rb +8 -5
  82. data/lib/grape/validations/validators/same_as.rb +18 -15
  83. data/lib/grape/validations/validators/values.rb +61 -56
  84. data/lib/grape/validations.rb +6 -0
  85. data/lib/grape/version.rb +1 -1
  86. data/lib/grape.rb +7 -3
  87. data/spec/grape/api/custom_validations_spec.rb +77 -45
  88. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  89. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  90. data/spec/grape/api/invalid_format_spec.rb +2 -0
  91. data/spec/grape/api/recognize_path_spec.rb +1 -1
  92. data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
  93. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  94. data/spec/grape/api_remount_spec.rb +25 -19
  95. data/spec/grape/api_spec.rb +576 -211
  96. data/spec/grape/dsl/callbacks_spec.rb +2 -1
  97. data/spec/grape/dsl/headers_spec.rb +39 -9
  98. data/spec/grape/dsl/helpers_spec.rb +3 -2
  99. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  100. data/spec/grape/dsl/logger_spec.rb +16 -18
  101. data/spec/grape/dsl/middleware_spec.rb +2 -1
  102. data/spec/grape/dsl/parameters_spec.rb +2 -0
  103. data/spec/grape/dsl/request_response_spec.rb +1 -0
  104. data/spec/grape/dsl/routing_spec.rb +10 -7
  105. data/spec/grape/endpoint/declared_spec.rb +848 -0
  106. data/spec/grape/endpoint_spec.rb +77 -589
  107. data/spec/grape/entity_spec.rb +29 -23
  108. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  109. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  110. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  111. data/spec/grape/exceptions/validation_spec.rb +5 -3
  112. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  113. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  114. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  115. data/spec/grape/integration/rack_sendfile_spec.rb +13 -9
  116. data/spec/grape/loading_spec.rb +8 -8
  117. data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
  118. data/spec/grape/middleware/auth/strategies_spec.rb +61 -21
  119. data/spec/grape/middleware/base_spec.rb +24 -15
  120. data/spec/grape/middleware/error_spec.rb +3 -3
  121. data/spec/grape/middleware/exception_spec.rb +111 -161
  122. data/spec/grape/middleware/formatter_spec.rb +28 -7
  123. data/spec/grape/middleware/globals_spec.rb +7 -4
  124. data/spec/grape/middleware/stack_spec.rb +15 -12
  125. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  126. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  127. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  128. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  129. data/spec/grape/middleware/versioner_spec.rb +1 -1
  130. data/spec/grape/parser_spec.rb +4 -0
  131. data/spec/grape/path_spec.rb +52 -52
  132. data/spec/grape/presenters/presenter_spec.rb +7 -6
  133. data/spec/grape/request_spec.rb +6 -4
  134. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  135. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  136. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  137. data/spec/grape/util/stackable_values_spec.rb +7 -5
  138. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  139. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +14 -3
  140. data/spec/grape/validations/params_scope_spec.rb +72 -10
  141. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -6
  142. data/spec/grape/validations/types/primitive_coercer_spec.rb +63 -7
  143. data/spec/grape/validations/types_spec.rb +8 -8
  144. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  145. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  146. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  147. data/spec/grape/validations/validators/coerce_spec.rb +248 -33
  148. data/spec/grape/validations/validators/default_spec.rb +121 -78
  149. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  150. data/spec/grape/validations/validators/except_values_spec.rb +4 -3
  151. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  152. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  153. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  154. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  155. data/spec/grape/validations/validators/values_spec.rb +183 -178
  156. data/spec/grape/validations_spec.rb +342 -29
  157. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  158. data/spec/integration/multi_json/json_spec.rb +1 -1
  159. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  160. data/spec/shared/versioning_examples.rb +32 -29
  161. data/spec/spec_helper.rb +12 -12
  162. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  163. data/spec/support/chunks.rb +14 -0
  164. data/spec/support/versioned_helpers.rb +4 -6
  165. metadata +110 -102
@@ -3,76 +3,82 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Grape::Middleware::Error do
6
- # raises a text exception
7
- module ExceptionSpec
8
- class ExceptionApp
6
+ let(:exception_app) do
7
+ Class.new do
9
8
  class << self
10
9
  def call(_env)
11
10
  raise 'rain!'
12
11
  end
13
12
  end
14
13
  end
14
+ end
15
15
 
16
- # raises a non-StandardError (ScriptError) exception
17
- class OtherExceptionApp
16
+ let(:other_exception_app) do
17
+ Class.new do
18
18
  class << self
19
19
  def call(_env)
20
20
  raise NotImplementedError, 'snow!'
21
21
  end
22
22
  end
23
23
  end
24
+ end
24
25
 
25
- # raises a hash error
26
- class ErrorHashApp
26
+ let(:custom_error_app) do
27
+ Class.new do
27
28
  class << self
28
- def error!(message, status)
29
- throw :error, message: { error: message, detail: 'missing widget' }, status: status
30
- end
29
+ class CustomError < Grape::Exceptions::Base; end
31
30
 
32
31
  def call(_env)
33
- error!('rain!', 401)
32
+ raise CustomError.new(status: 400, message: 'failed validation')
34
33
  end
35
34
  end
36
35
  end
36
+ end
37
37
 
38
- # raises an error!
39
- class AccessDeniedApp
38
+ let(:error_hash_app) do
39
+ Class.new do
40
40
  class << self
41
41
  def error!(message, status)
42
- throw :error, message: message, status: status
42
+ throw :error, message: { error: message, detail: 'missing widget' }, status: status
43
43
  end
44
44
 
45
45
  def call(_env)
46
- error!('Access Denied', 401)
46
+ error!('rain!', 401)
47
47
  end
48
48
  end
49
49
  end
50
+ end
50
51
 
51
- # raises a custom error
52
- class CustomError < Grape::Exceptions::Base
53
- end
54
-
55
- class CustomErrorApp
52
+ let(:access_denied_app) do
53
+ Class.new do
56
54
  class << self
55
+ def error!(message, status)
56
+ throw :error, message: message, status: status
57
+ end
58
+
57
59
  def call(_env)
58
- raise CustomError.new(status: 400, message: 'failed validation')
60
+ error!('Access Denied', 401)
59
61
  end
60
62
  end
61
63
  end
62
64
  end
63
65
 
64
- def app
65
- subject
66
+ let(:app) do
67
+ builder = Rack::Builder.new
68
+ builder.use Spec::Support::EndpointFaker
69
+ if options.any?
70
+ builder.use described_class, options
71
+ else
72
+ builder.use described_class
73
+ end
74
+ builder.run running_app
75
+ builder.to_app
66
76
  end
67
77
 
68
78
  context 'with defaults' do
69
- subject do
70
- Rack::Builder.app do
71
- use Spec::Support::EndpointFaker
72
- use Grape::Middleware::Error
73
- run ExceptionSpec::ExceptionApp
74
- end
75
- end
79
+ let(:running_app) { exception_app }
80
+ let(:options) { {} }
81
+
76
82
  it 'does not trap errors by default' do
77
83
  expect { get '/' }.to raise_error(RuntimeError, 'rain!')
78
84
  end
@@ -80,17 +86,14 @@ describe Grape::Middleware::Error do
80
86
 
81
87
  context 'with rescue_all' do
82
88
  context 'StandardError exception' do
83
- subject do
84
- Rack::Builder.app do
85
- use Spec::Support::EndpointFaker
86
- use Grape::Middleware::Error, rescue_all: true
87
- run ExceptionSpec::ExceptionApp
88
- end
89
- end
89
+ let(:running_app) { exception_app }
90
+ let(:options) { { rescue_all: true } }
91
+
90
92
  it 'sets the message appropriately' do
91
93
  get '/'
92
94
  expect(last_response.body).to eq('rain!')
93
95
  end
96
+
94
97
  it 'defaults to a 500 status' do
95
98
  get '/'
96
99
  expect(last_response.status).to eq(500)
@@ -98,13 +101,9 @@ describe Grape::Middleware::Error do
98
101
  end
99
102
 
100
103
  context 'Non-StandardError exception' do
101
- subject do
102
- Rack::Builder.app do
103
- use Spec::Support::EndpointFaker
104
- use Grape::Middleware::Error, rescue_all: true
105
- run ExceptionSpec::OtherExceptionApp
106
- end
107
- end
104
+ let(:running_app) { other_exception_app }
105
+ let(:options) { { rescue_all: true } }
106
+
108
107
  it 'does not trap errors other than StandardError' do
109
108
  expect { get '/' }.to raise_error(NotImplementedError, 'snow!')
110
109
  end
@@ -113,13 +112,9 @@ describe Grape::Middleware::Error do
113
112
 
114
113
  context 'Non-StandardError exception with a provided rescue handler' do
115
114
  context 'default error response' do
116
- subject do
117
- Rack::Builder.app do
118
- use Spec::Support::EndpointFaker
119
- use Grape::Middleware::Error, rescue_handlers: { NotImplementedError => nil }
120
- run ExceptionSpec::OtherExceptionApp
121
- end
122
- end
115
+ let(:running_app) { other_exception_app }
116
+ let(:options) { { rescue_handlers: { NotImplementedError => nil } } }
117
+
123
118
  it 'rescues the exception using the default handler' do
124
119
  get '/'
125
120
  expect(last_response.body).to eq('snow!')
@@ -127,13 +122,9 @@ describe Grape::Middleware::Error do
127
122
  end
128
123
 
129
124
  context 'custom error response' do
130
- subject do
131
- Rack::Builder.app do
132
- use Spec::Support::EndpointFaker
133
- use Grape::Middleware::Error, rescue_handlers: { NotImplementedError => -> { Rack::Response.new('rescued', 200, {}) } }
134
- run ExceptionSpec::OtherExceptionApp
135
- end
136
- end
125
+ let(:running_app) { other_exception_app }
126
+ let(:options) { { rescue_handlers: { NotImplementedError => -> { Rack::Response.new('rescued', 200, {}) } } } }
127
+
137
128
  it 'rescues the exception using the provided handler' do
138
129
  get '/'
139
130
  expect(last_response.body).to eq('rescued')
@@ -142,13 +133,9 @@ describe Grape::Middleware::Error do
142
133
  end
143
134
 
144
135
  context do
145
- subject do
146
- Rack::Builder.app do
147
- use Spec::Support::EndpointFaker
148
- use Grape::Middleware::Error, rescue_all: true, default_status: 500
149
- run ExceptionSpec::ExceptionApp
150
- end
151
- end
136
+ let(:running_app) { exception_app }
137
+ let(:options) { { rescue_all: true, default_status: 500 } }
138
+
152
139
  it 'is possible to specify a different default status code' do
153
140
  get '/'
154
141
  expect(last_response.status).to eq(500)
@@ -156,13 +143,9 @@ describe Grape::Middleware::Error do
156
143
  end
157
144
 
158
145
  context do
159
- subject do
160
- Rack::Builder.app do
161
- use Spec::Support::EndpointFaker
162
- use Grape::Middleware::Error, rescue_all: true, format: :json
163
- run ExceptionSpec::ExceptionApp
164
- end
165
- end
146
+ let(:running_app) { exception_app }
147
+ let(:options) { { rescue_all: true, format: :json } }
148
+
166
149
  it 'is possible to return errors in json format' do
167
150
  get '/'
168
151
  expect(last_response.body).to eq('{"error":"rain!"}')
@@ -170,13 +153,9 @@ describe Grape::Middleware::Error do
170
153
  end
171
154
 
172
155
  context do
173
- subject do
174
- Rack::Builder.app do
175
- use Spec::Support::EndpointFaker
176
- use Grape::Middleware::Error, rescue_all: true, format: :json
177
- run ExceptionSpec::ErrorHashApp
178
- end
179
- end
156
+ let(:running_app) { error_hash_app }
157
+ let(:options) { { rescue_all: true, format: :json } }
158
+
180
159
  it 'is possible to return hash errors in json format' do
181
160
  get '/'
182
161
  expect(['{"error":"rain!","detail":"missing widget"}',
@@ -185,13 +164,9 @@ describe Grape::Middleware::Error do
185
164
  end
186
165
 
187
166
  context do
188
- subject do
189
- Rack::Builder.app do
190
- use Spec::Support::EndpointFaker
191
- use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
192
- run ExceptionSpec::ExceptionApp
193
- end
194
- end
167
+ let(:running_app) { exception_app }
168
+ let(:options) { { rescue_all: true, format: :jsonapi } }
169
+
195
170
  it 'is possible to return errors in jsonapi format' do
196
171
  get '/'
197
172
  expect(last_response.body).to eq('{&quot;error&quot;:&quot;rain!&quot;}')
@@ -199,13 +174,8 @@ describe Grape::Middleware::Error do
199
174
  end
200
175
 
201
176
  context do
202
- subject do
203
- Rack::Builder.app do
204
- use Spec::Support::EndpointFaker
205
- use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
206
- run ExceptionSpec::ErrorHashApp
207
- end
208
- end
177
+ let(:running_app) { error_hash_app }
178
+ let(:options) { { rescue_all: true, format: :jsonapi } }
209
179
 
210
180
  it 'is possible to return hash errors in jsonapi format' do
211
181
  get '/'
@@ -215,13 +185,9 @@ describe Grape::Middleware::Error do
215
185
  end
216
186
 
217
187
  context do
218
- subject do
219
- Rack::Builder.app do
220
- use Spec::Support::EndpointFaker
221
- use Grape::Middleware::Error, rescue_all: true, format: :xml
222
- run ExceptionSpec::ExceptionApp
223
- end
224
- end
188
+ let(:running_app) { exception_app }
189
+ let(:options) { { rescue_all: true, format: :xml } }
190
+
225
191
  it 'is possible to return errors in xml format' do
226
192
  get '/'
227
193
  expect(last_response.body).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <message>rain!</message>\n</error>\n")
@@ -229,13 +195,9 @@ describe Grape::Middleware::Error do
229
195
  end
230
196
 
231
197
  context do
232
- subject do
233
- Rack::Builder.app do
234
- use Spec::Support::EndpointFaker
235
- use Grape::Middleware::Error, rescue_all: true, format: :xml
236
- run ExceptionSpec::ErrorHashApp
237
- end
238
- end
198
+ let(:running_app) { error_hash_app }
199
+ let(:options) { { rescue_all: true, format: :xml } }
200
+
239
201
  it 'is possible to return hash errors in xml format' do
240
202
  get '/'
241
203
  expect(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <detail>missing widget</detail>\n <error>rain!</error>\n</error>\n",
@@ -244,20 +206,19 @@ describe Grape::Middleware::Error do
244
206
  end
245
207
 
246
208
  context do
247
- subject do
248
- Rack::Builder.app do
249
- use Spec::Support::EndpointFaker
250
- use Grape::Middleware::Error,
251
- rescue_all: true,
252
- format: :custom,
253
- error_formatters: {
254
- custom: lambda do |message, _backtrace, _options, _env, _original_exception|
255
- { custom_formatter: message }.inspect
256
- end
257
- }
258
- run ExceptionSpec::ExceptionApp
259
- end
209
+ let(:running_app) { exception_app }
210
+ let(:options) do
211
+ {
212
+ rescue_all: true,
213
+ format: :custom,
214
+ error_formatters: {
215
+ custom: lambda do |message, _backtrace, _options, _env, _original_exception|
216
+ { custom_formatter: message }.inspect
217
+ end
218
+ }
219
+ }
260
220
  end
221
+
261
222
  it 'is possible to specify a custom formatter' do
262
223
  get '/'
263
224
  expect(last_response.body).to eq('{:custom_formatter=&gt;&quot;rain!&quot;}')
@@ -265,13 +226,9 @@ describe Grape::Middleware::Error do
265
226
  end
266
227
 
267
228
  context do
268
- subject do
269
- Rack::Builder.app do
270
- use Spec::Support::EndpointFaker
271
- use Grape::Middleware::Error
272
- run ExceptionSpec::AccessDeniedApp
273
- end
274
- end
229
+ let(:running_app) { access_denied_app }
230
+ let(:options) { {} }
231
+
275
232
  it 'does not trap regular error! codes' do
276
233
  get '/'
277
234
  expect(last_response.status).to eq(401)
@@ -279,13 +236,9 @@ describe Grape::Middleware::Error do
279
236
  end
280
237
 
281
238
  context do
282
- subject do
283
- Rack::Builder.app do
284
- use Spec::Support::EndpointFaker
285
- use Grape::Middleware::Error, rescue_all: false
286
- run ExceptionSpec::CustomErrorApp
287
- end
288
- end
239
+ let(:running_app) { custom_error_app }
240
+ let(:options) { { rescue_all: false } }
241
+
289
242
  it 'responds to custom Grape exceptions appropriately' do
290
243
  get '/'
291
244
  expect(last_response.status).to eq(400)
@@ -294,16 +247,15 @@ describe Grape::Middleware::Error do
294
247
  end
295
248
 
296
249
  context 'with rescue_options :backtrace and :exception set to true' do
297
- subject do
298
- Rack::Builder.app do
299
- use Spec::Support::EndpointFaker
300
- use Grape::Middleware::Error,
301
- rescue_all: true,
302
- format: :json,
303
- rescue_options: { backtrace: true, original_exception: true }
304
- run ExceptionSpec::ExceptionApp
305
- end
250
+ let(:running_app) { exception_app }
251
+ let(:options) do
252
+ {
253
+ rescue_all: true,
254
+ format: :json,
255
+ rescue_options: { backtrace: true, original_exception: true }
256
+ }
306
257
  end
258
+
307
259
  it 'is possible to return the backtrace and the original exception in json format' do
308
260
  get '/'
309
261
  expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original_exception', 'RuntimeError')
@@ -311,16 +263,15 @@ describe Grape::Middleware::Error do
311
263
  end
312
264
 
313
265
  context do
314
- subject do
315
- Rack::Builder.app do
316
- use Spec::Support::EndpointFaker
317
- use Grape::Middleware::Error,
318
- rescue_all: true,
319
- format: :xml,
320
- rescue_options: { backtrace: true, original_exception: true }
321
- run ExceptionSpec::ExceptionApp
322
- end
266
+ let(:running_app) { exception_app }
267
+ let(:options) do
268
+ {
269
+ rescue_all: true,
270
+ format: :xml,
271
+ rescue_options: { backtrace: true, original_exception: true }
272
+ }
323
273
  end
274
+
324
275
  it 'is possible to return the backtrace and the original exception in xml format' do
325
276
  get '/'
326
277
  expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original-exception', 'RuntimeError')
@@ -328,16 +279,15 @@ describe Grape::Middleware::Error do
328
279
  end
329
280
 
330
281
  context do
331
- subject do
332
- Rack::Builder.app do
333
- use Spec::Support::EndpointFaker
334
- use Grape::Middleware::Error,
335
- rescue_all: true,
336
- format: :txt,
337
- rescue_options: { backtrace: true, original_exception: true }
338
- run ExceptionSpec::ExceptionApp
339
- end
282
+ let(:running_app) { exception_app }
283
+ let(:options) do
284
+ {
285
+ rescue_all: true,
286
+ format: :txt,
287
+ rescue_options: { backtrace: true, original_exception: true }
288
+ }
340
289
  end
290
+
341
291
  it 'is possible to return the backtrace and the original exception in txt format' do
342
292
  get '/'
343
293
  expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original exception', 'RuntimeError')
@@ -3,7 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Grape::Middleware::Formatter do
6
- subject { Grape::Middleware::Formatter.new(app) }
6
+ subject { described_class.new(app) }
7
+
7
8
  before { allow(subject).to receive(:dup).and_return(subject) }
8
9
 
9
10
  let(:body) { { 'foo' => 'bar' } }
@@ -11,6 +12,7 @@ describe Grape::Middleware::Formatter do
11
12
 
12
13
  context 'serialization' do
13
14
  let(:body) { { 'abc' => 'def' } }
15
+
14
16
  it 'looks at the bodies for possibly serializable data' do
15
17
  _, _, bodies = *subject.call('PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json')
16
18
  bodies.each { |b| expect(b).to eq(::Grape::Json.dump(body)) }
@@ -18,9 +20,10 @@ describe Grape::Middleware::Formatter do
18
20
 
19
21
  context 'default format' do
20
22
  let(:body) { ['foo'] }
23
+
21
24
  it 'calls #to_json since default format is json' do
22
25
  body.instance_eval do
23
- def to_json
26
+ def to_json(*_args)
24
27
  '"bar"'
25
28
  end
26
29
  end
@@ -31,9 +34,10 @@ describe Grape::Middleware::Formatter do
31
34
 
32
35
  context 'jsonapi' do
33
36
  let(:body) { { 'foos' => [{ 'bar' => 'baz' }] } }
37
+
34
38
  it 'calls #to_json if the content type is jsonapi' do
35
39
  body.instance_eval do
36
- def to_json
40
+ def to_json(*_args)
37
41
  '{"foos":[{"bar":"baz"}] }'
38
42
  end
39
43
  end
@@ -44,6 +48,7 @@ describe Grape::Middleware::Formatter do
44
48
 
45
49
  context 'xml' do
46
50
  let(:body) { +'string' }
51
+
47
52
  it 'calls #to_xml if the content type is xml' do
48
53
  body.instance_eval do
49
54
  def to_xml
@@ -58,6 +63,7 @@ describe Grape::Middleware::Formatter do
58
63
 
59
64
  context 'error handling' do
60
65
  let(:formatter) { double(:formatter) }
66
+
61
67
  before do
62
68
  allow(Grape::Formatter).to receive(:formatter_for) { formatter }
63
69
  end
@@ -67,7 +73,7 @@ describe Grape::Middleware::Formatter do
67
73
 
68
74
  expect do
69
75
  catch(:error) { subject.call('PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json') }
70
- end.to_not raise_error
76
+ end.not_to raise_error
71
77
  end
72
78
 
73
79
  it 'does not rescue other exceptions' do
@@ -147,7 +153,7 @@ describe Grape::Middleware::Formatter do
147
153
  subject.options[:content_types][:custom] = 'application/vnd.test+json'
148
154
  end
149
155
 
150
- it 'it uses the custom type' do
156
+ it 'uses the custom type' do
151
157
  subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test+json')
152
158
  expect(subject.env['api.format']).to eq(:custom)
153
159
  end
@@ -164,26 +170,31 @@ describe Grape::Middleware::Formatter do
164
170
  _, headers, = subject.call('PATH_INFO' => '/info.json')
165
171
  expect(headers['Content-type']).to eq('application/json')
166
172
  end
173
+
167
174
  it 'is set for xml' do
168
175
  _, headers, = subject.call('PATH_INFO' => '/info.xml')
169
176
  expect(headers['Content-type']).to eq('application/xml')
170
177
  end
178
+
171
179
  it 'is set for txt' do
172
180
  _, headers, = subject.call('PATH_INFO' => '/info.txt')
173
181
  expect(headers['Content-type']).to eq('text/plain')
174
182
  end
183
+
175
184
  it 'is set for custom' do
176
185
  subject.options[:content_types] = {}
177
186
  subject.options[:content_types][:custom] = 'application/x-custom'
178
187
  _, headers, = subject.call('PATH_INFO' => '/info.custom')
179
188
  expect(headers['Content-type']).to eq('application/x-custom')
180
189
  end
190
+
181
191
  it 'is set for vendored with registered type' do
182
192
  subject.options[:content_types] = {}
183
193
  subject.options[:content_types][:custom] = 'application/vnd.test+json'
184
194
  _, headers, = subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test+json')
185
195
  expect(headers['Content-type']).to eq('application/vnd.test+json')
186
196
  end
197
+
187
198
  it 'is set to closest generic for custom vendored/versioned without registered type' do
188
199
  _, headers, = subject.call('PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test+json')
189
200
  expect(headers['Content-type']).to eq('application/json')
@@ -198,13 +209,16 @@ describe Grape::Middleware::Formatter do
198
209
  _, _, body = subject.call('PATH_INFO' => '/info.custom')
199
210
  expect(read_chunks(body)).to eq(['CUSTOM FORMAT'])
200
211
  end
212
+
201
213
  context 'default' do
202
214
  let(:body) { ['blah'] }
215
+
203
216
  it 'uses default json formatter' do
204
217
  _, _, body = subject.call('PATH_INFO' => '/info.json')
205
218
  expect(read_chunks(body)).to eq(['["blah"]'])
206
219
  end
207
220
  end
221
+
208
222
  it 'uses custom json formatter' do
209
223
  subject.options[:formatters][:json] = ->(_obj, _env) { 'CUSTOM JSON FORMAT' }
210
224
  _, _, body = subject.call('PATH_INFO' => '/info.json')
@@ -272,6 +286,7 @@ describe Grape::Middleware::Formatter do
272
286
 
273
287
  context 'when body is nil' do
274
288
  let(:io) { double }
289
+
275
290
  before do
276
291
  allow(io).to receive_message_chain(:rewind, :read).and_return(nil)
277
292
  end
@@ -290,6 +305,7 @@ describe Grape::Middleware::Formatter do
290
305
 
291
306
  context 'when body is empty' do
292
307
  let(:io) { double }
308
+
293
309
  before do
294
310
  allow(io).to receive_message_chain(:rewind, :read).and_return('')
295
311
  end
@@ -334,6 +350,7 @@ describe Grape::Middleware::Formatter do
334
350
  expect(subject.env['rack.request.form_hash']['is_boolean']).to be true
335
351
  expect(subject.env['rack.request.form_hash']['string']).to eq('thing')
336
352
  end
353
+
337
354
  it 'rewinds IO' do
338
355
  io = StringIO.new('{"is_boolean":true,"string":"thing"}')
339
356
  io.read
@@ -347,6 +364,7 @@ describe Grape::Middleware::Formatter do
347
364
  expect(subject.env['rack.request.form_hash']['is_boolean']).to be true
348
365
  expect(subject.env['rack.request.form_hash']['string']).to eq('thing')
349
366
  end
367
+
350
368
  it "parses the body from an xml #{method} and copies values into rack.request.from_hash" do
351
369
  io = StringIO.new('<thing><name>Test</name></thing>')
352
370
  subject.call(
@@ -362,6 +380,7 @@ describe Grape::Middleware::Formatter do
362
380
  expect(subject.env['rack.request.form_hash']['thing']['name']['__content__']).to eq('Test')
363
381
  end
364
382
  end
383
+
365
384
  [Rack::Request::FORM_DATA_MEDIA_TYPES, Rack::Request::PARSEABLE_DATA_MEDIA_TYPES].flatten.each do |content_type|
366
385
  it "ignores #{content_type}" do
367
386
  io = StringIO.new('name=Other+Test+Thing')
@@ -380,7 +399,7 @@ describe Grape::Middleware::Formatter do
380
399
 
381
400
  context 'send file' do
382
401
  let(:file) { double(File) }
383
- let(:file_body) { Grape::ServeFile::FileResponse.new(file) }
402
+ let(:file_body) { Grape::ServeStream::StreamResponse.new(file) }
384
403
  let(:app) { ->(_env) { [200, {}, file_body] } }
385
404
 
386
405
  it 'returns a file response' do
@@ -400,10 +419,12 @@ describe Grape::Middleware::Formatter do
400
419
  end
401
420
  end
402
421
  let(:app) { ->(_env) { [200, {}, ['']] } }
422
+
403
423
  before do
404
424
  Grape::Formatter.register :invalid, InvalidFormatter
405
425
  Grape::ContentTypes.register :invalid, 'application/x-invalid'
406
426
  end
427
+
407
428
  after do
408
429
  Grape::ContentTypes.default_elements.delete(:invalid)
409
430
  Grape::Formatter.default_elements.delete(:invalid)
@@ -418,7 +439,7 @@ describe Grape::Middleware::Formatter do
418
439
 
419
440
  context 'custom parser raises exception and rescue options are enabled for backtrace and original_exception' do
420
441
  it 'adds the backtrace and original_exception to the error output' do
421
- subject = Grape::Middleware::Formatter.new(
442
+ subject = described_class.new(
422
443
  app,
423
444
  rescue_options: { backtrace: true, original_exception: true },
424
445
  parsers: { json: ->(_object, _env) { raise StandardError, 'fail' } }
@@ -3,7 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Grape::Middleware::Globals do
6
- subject { Grape::Middleware::Globals.new(blank_app) }
6
+ subject { described_class.new(blank_app) }
7
+
7
8
  before { allow(subject).to receive(:dup).and_return(subject) }
8
9
 
9
10
  let(:blank_app) { ->(_env) { [200, {}, 'Hi there.'] } }
@@ -13,15 +14,17 @@ describe Grape::Middleware::Globals do
13
14
  end
14
15
 
15
16
  context 'environment' do
16
- it 'should set the grape.request environment' do
17
+ it 'sets the grape.request environment' do
17
18
  subject.call({})
18
19
  expect(subject.env['grape.request']).to be_a(Grape::Request)
19
20
  end
20
- it 'should set the grape.request.headers environment' do
21
+
22
+ it 'sets the grape.request.headers environment' do
21
23
  subject.call({})
22
24
  expect(subject.env['grape.request.headers']).to be_a(Hash)
23
25
  end
24
- it 'should set the grape.request.params environment' do
26
+
27
+ it 'sets the grape.request.params environment' do
25
28
  subject.call('QUERY_STRING' => 'test=1', 'rack.input' => StringIO.new)
26
29
  expect(subject.env['grape.request.params']).to be_a(Hash)
27
30
  end