grape 1.1.0 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +278 -44
  3. data/LICENSE +1 -1
  4. data/README.md +514 -69
  5. data/UPGRADING.md +424 -17
  6. data/grape.gemspec +13 -2
  7. data/lib/grape.rb +104 -71
  8. data/lib/grape/api.rb +138 -175
  9. data/lib/grape/api/helpers.rb +2 -0
  10. data/lib/grape/api/instance.rb +283 -0
  11. data/lib/grape/config.rb +34 -0
  12. data/lib/grape/content_types.rb +34 -0
  13. data/lib/grape/cookies.rb +2 -0
  14. data/lib/grape/dsl/api.rb +2 -0
  15. data/lib/grape/dsl/callbacks.rb +22 -0
  16. data/lib/grape/dsl/configuration.rb +2 -0
  17. data/lib/grape/dsl/desc.rb +41 -7
  18. data/lib/grape/dsl/headers.rb +2 -0
  19. data/lib/grape/dsl/helpers.rb +5 -2
  20. data/lib/grape/dsl/inside_route.rb +92 -49
  21. data/lib/grape/dsl/logger.rb +2 -0
  22. data/lib/grape/dsl/middleware.rb +9 -0
  23. data/lib/grape/dsl/parameters.rb +25 -14
  24. data/lib/grape/dsl/request_response.rb +4 -2
  25. data/lib/grape/dsl/routing.rb +17 -10
  26. data/lib/grape/dsl/settings.rb +7 -1
  27. data/lib/grape/dsl/validations.rb +24 -4
  28. data/lib/grape/eager_load.rb +20 -0
  29. data/lib/grape/endpoint.rb +59 -35
  30. data/lib/grape/error_formatter.rb +4 -2
  31. data/lib/grape/error_formatter/base.rb +2 -0
  32. data/lib/grape/error_formatter/json.rb +2 -0
  33. data/lib/grape/error_formatter/txt.rb +2 -0
  34. data/lib/grape/error_formatter/xml.rb +2 -0
  35. data/lib/grape/exceptions/base.rb +20 -14
  36. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  37. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  38. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  39. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  40. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  41. data/lib/grape/exceptions/invalid_response.rb +11 -0
  42. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  43. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  44. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  45. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  46. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  47. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  48. data/lib/grape/exceptions/missing_option.rb +2 -0
  49. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  50. data/lib/grape/exceptions/unknown_options.rb +2 -0
  51. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  52. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  53. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  54. data/lib/grape/exceptions/validation.rb +4 -2
  55. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  56. data/lib/grape/exceptions/validation_errors.rb +16 -13
  57. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  58. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  59. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  60. data/lib/grape/extensions/hash.rb +2 -0
  61. data/lib/grape/extensions/hashie/mash.rb +2 -0
  62. data/lib/grape/formatter.rb +5 -3
  63. data/lib/grape/formatter/json.rb +2 -0
  64. data/lib/grape/formatter/serializable_hash.rb +2 -0
  65. data/lib/grape/formatter/txt.rb +2 -0
  66. data/lib/grape/formatter/xml.rb +2 -0
  67. data/lib/grape/http/headers.rb +50 -18
  68. data/lib/grape/locale/en.yml +3 -1
  69. data/lib/grape/middleware/auth/base.rb +7 -7
  70. data/lib/grape/middleware/auth/dsl.rb +2 -0
  71. data/lib/grape/middleware/auth/strategies.rb +2 -0
  72. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  73. data/lib/grape/middleware/base.rb +10 -7
  74. data/lib/grape/middleware/error.rb +21 -16
  75. data/lib/grape/middleware/filter.rb +2 -0
  76. data/lib/grape/middleware/formatter.rb +8 -6
  77. data/lib/grape/middleware/globals.rb +2 -0
  78. data/lib/grape/middleware/helpers.rb +12 -0
  79. data/lib/grape/middleware/stack.rb +13 -3
  80. data/lib/grape/middleware/versioner.rb +2 -0
  81. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  82. data/lib/grape/middleware/versioner/header.rb +10 -8
  83. data/lib/grape/middleware/versioner/param.rb +3 -1
  84. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
  85. data/lib/grape/middleware/versioner/path.rb +3 -1
  86. data/lib/grape/namespace.rb +14 -2
  87. data/lib/grape/parser.rb +4 -2
  88. data/lib/grape/parser/json.rb +3 -1
  89. data/lib/grape/parser/xml.rb +3 -1
  90. data/lib/grape/path.rb +15 -3
  91. data/lib/grape/presenters/presenter.rb +2 -0
  92. data/lib/grape/request.rb +19 -10
  93. data/lib/grape/router.rb +30 -29
  94. data/lib/grape/router/attribute_translator.rb +41 -8
  95. data/lib/grape/router/pattern.rb +20 -16
  96. data/lib/grape/router/route.rb +14 -28
  97. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  98. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  99. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  100. data/lib/grape/util/base_inheritable.rb +43 -0
  101. data/lib/grape/util/cache.rb +20 -0
  102. data/lib/grape/util/endpoint_configuration.rb +8 -0
  103. data/lib/grape/util/env.rb +19 -17
  104. data/lib/grape/util/inheritable_setting.rb +2 -0
  105. data/lib/grape/util/inheritable_values.rb +7 -25
  106. data/lib/grape/util/json.rb +2 -0
  107. data/lib/grape/util/lazy_block.rb +27 -0
  108. data/lib/grape/util/lazy_object.rb +43 -0
  109. data/lib/grape/util/lazy_value.rb +98 -0
  110. data/lib/grape/util/registrable.rb +2 -0
  111. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  112. data/lib/grape/util/stackable_values.rb +21 -34
  113. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  114. data/lib/grape/util/xml.rb +2 -0
  115. data/lib/grape/validations.rb +2 -0
  116. data/lib/grape/validations/attributes_iterator.rb +16 -6
  117. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  118. data/lib/grape/validations/params_scope.rb +51 -30
  119. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  120. data/lib/grape/validations/types.rb +13 -38
  121. data/lib/grape/validations/types/array_coercer.rb +65 -0
  122. data/lib/grape/validations/types/build_coercer.rb +47 -49
  123. data/lib/grape/validations/types/custom_type_coercer.rb +29 -51
  124. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  125. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  126. data/lib/grape/validations/types/file.rb +22 -18
  127. data/lib/grape/validations/types/invalid_value.rb +24 -0
  128. data/lib/grape/validations/types/json.rb +46 -39
  129. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  130. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  131. data/lib/grape/validations/types/set_coercer.rb +40 -0
  132. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  133. data/lib/grape/validations/validator_factory.rb +8 -11
  134. data/lib/grape/validations/validators/all_or_none.rb +8 -13
  135. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  136. data/lib/grape/validations/validators/as.rb +5 -4
  137. data/lib/grape/validations/validators/at_least_one_of.rb +7 -13
  138. data/lib/grape/validations/validators/base.rb +20 -16
  139. data/lib/grape/validations/validators/coerce.rb +46 -29
  140. data/lib/grape/validations/validators/default.rb +6 -6
  141. data/lib/grape/validations/validators/exactly_one_of.rb +10 -23
  142. data/lib/grape/validations/validators/except_values.rb +4 -2
  143. data/lib/grape/validations/validators/multiple_params_base.rb +17 -10
  144. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -18
  145. data/lib/grape/validations/validators/presence.rb +3 -1
  146. data/lib/grape/validations/validators/regexp.rb +4 -2
  147. data/lib/grape/validations/validators/same_as.rb +26 -0
  148. data/lib/grape/validations/validators/values.rb +18 -6
  149. data/lib/grape/version.rb +3 -1
  150. data/spec/grape/api/custom_validations_spec.rb +5 -3
  151. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  152. data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
  153. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  154. data/spec/grape/api/instance_spec.rb +104 -0
  155. data/spec/grape/api/invalid_format_spec.rb +2 -0
  156. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  157. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  158. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  160. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  161. data/spec/grape/api/recognize_path_spec.rb +2 -0
  162. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  163. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  164. data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
  165. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  166. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  167. data/spec/grape/api_remount_spec.rb +473 -0
  168. data/spec/grape/api_spec.rb +565 -12
  169. data/spec/grape/config_spec.rb +19 -0
  170. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  171. data/spec/grape/dsl/configuration_spec.rb +2 -0
  172. data/spec/grape/dsl/desc_spec.rb +42 -16
  173. data/spec/grape/dsl/headers_spec.rb +2 -0
  174. data/spec/grape/dsl/helpers_spec.rb +4 -2
  175. data/spec/grape/dsl/inside_route_spec.rb +184 -33
  176. data/spec/grape/dsl/logger_spec.rb +2 -0
  177. data/spec/grape/dsl/middleware_spec.rb +10 -0
  178. data/spec/grape/dsl/parameters_spec.rb +2 -0
  179. data/spec/grape/dsl/request_response_spec.rb +2 -0
  180. data/spec/grape/dsl/routing_spec.rb +12 -0
  181. data/spec/grape/dsl/settings_spec.rb +2 -0
  182. data/spec/grape/dsl/validations_spec.rb +2 -0
  183. data/spec/grape/endpoint/declared_spec.rb +601 -0
  184. data/spec/grape/endpoint_spec.rb +53 -523
  185. data/spec/grape/entity_spec.rb +9 -1
  186. data/spec/grape/exceptions/base_spec.rb +67 -0
  187. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  188. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  189. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  190. data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
  191. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  192. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  193. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  194. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  195. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  196. data/spec/grape/exceptions/validation_errors_spec.rb +8 -4
  197. data/spec/grape/exceptions/validation_spec.rb +3 -1
  198. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  199. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  200. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  201. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  202. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  203. data/spec/grape/integration/rack_spec.rb +25 -7
  204. data/spec/grape/loading_spec.rb +2 -0
  205. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  206. data/spec/grape/middleware/auth/dsl_spec.rb +5 -3
  207. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  208. data/spec/grape/middleware/base_spec.rb +10 -0
  209. data/spec/grape/middleware/error_spec.rb +3 -1
  210. data/spec/grape/middleware/exception_spec.rb +4 -2
  211. data/spec/grape/middleware/formatter_spec.rb +33 -16
  212. data/spec/grape/middleware/globals_spec.rb +2 -0
  213. data/spec/grape/middleware/stack_spec.rb +12 -0
  214. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  215. data/spec/grape/middleware/versioner/header_spec.rb +9 -1
  216. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  217. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  218. data/spec/grape/middleware/versioner_spec.rb +2 -0
  219. data/spec/grape/named_api_spec.rb +21 -0
  220. data/spec/grape/parser_spec.rb +7 -5
  221. data/spec/grape/path_spec.rb +6 -4
  222. data/spec/grape/presenters/presenter_spec.rb +2 -0
  223. data/spec/grape/request_spec.rb +26 -0
  224. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  225. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  226. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  227. data/spec/grape/util/stackable_values_spec.rb +3 -1
  228. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  229. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  230. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  231. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
  232. data/spec/grape/validations/params_scope_spec.rb +213 -9
  233. data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
  234. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  235. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  236. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  237. data/spec/grape/validations/types_spec.rb +9 -36
  238. data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
  239. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  240. data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
  241. data/spec/grape/validations/validators/coerce_spec.rb +476 -135
  242. data/spec/grape/validations/validators/default_spec.rb +172 -0
  243. data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
  244. data/spec/grape/validations/validators/except_values_spec.rb +4 -1
  245. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
  246. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  247. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  248. data/spec/grape/validations/validators/same_as_spec.rb +65 -0
  249. data/spec/grape/validations/validators/values_spec.rb +30 -5
  250. data/spec/grape/validations_spec.rb +388 -50
  251. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  252. data/spec/integration/multi_json/json_spec.rb +2 -0
  253. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  254. data/spec/shared/versioning_examples.rb +22 -20
  255. data/spec/spec_helper.rb +12 -1
  256. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  257. data/spec/support/chunks.rb +14 -0
  258. data/spec/support/content_type_helpers.rb +2 -0
  259. data/spec/support/eager_load.rb +19 -0
  260. data/spec/support/endpoint_faker.rb +2 -0
  261. data/spec/support/file_streamer.rb +2 -0
  262. data/spec/support/integer_helpers.rb +2 -0
  263. data/spec/support/versioned_helpers.rb +8 -8
  264. metadata +86 -48
  265. data/Appraisals +0 -32
  266. data/Dangerfile +0 -2
  267. data/Gemfile +0 -33
  268. data/Gemfile.lock +0 -231
  269. data/Guardfile +0 -10
  270. data/RELEASING.md +0 -111
  271. data/Rakefile +0 -25
  272. data/benchmark/simple.rb +0 -27
  273. data/benchmark/simple_with_type_coercer.rb +0 -22
  274. data/gemfiles/multi_json.gemfile +0 -35
  275. data/gemfiles/multi_xml.gemfile +0 -35
  276. data/gemfiles/rack_1.5.2.gemfile +0 -35
  277. data/gemfiles/rack_edge.gemfile +0 -35
  278. data/gemfiles/rails_3.gemfile +0 -36
  279. data/gemfiles/rails_4.gemfile +0 -35
  280. data/gemfiles/rails_5.gemfile +0 -35
  281. data/gemfiles/rails_edge.gemfile +0 -35
  282. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  283. data/lib/grape/util/content_types.rb +0 -26
  284. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
  285. data/pkg/grape-0.17.0.gem +0 -0
  286. data/pkg/grape-0.19.0.gem +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Endpoint do
@@ -8,7 +10,7 @@ describe Grape::Endpoint do
8
10
  end
9
11
 
10
12
  describe '.before_each' do
11
- after { Grape::Endpoint.before_each(nil) }
13
+ after { Grape::Endpoint.before_each.clear }
12
14
 
13
15
  it 'is settable via block' do
14
16
  block = ->(_endpoint) { 'noop' }
@@ -149,7 +151,7 @@ describe Grape::Endpoint do
149
151
  it 'includes headers passed as symbols' do
150
152
  env = Rack::MockRequest.env_for('/headers')
151
153
  env['HTTP_SYMBOL_HEADER'.to_sym] = 'Goliath passes symbols'
152
- body = subject.call(env)[2].body.first
154
+ body = read_chunks(subject.call(env)[2]).join
153
155
  expect(JSON.parse(body)['Symbol-Header']).to eq('Goliath passes symbols')
154
156
  end
155
157
  end
@@ -278,527 +280,6 @@ describe Grape::Endpoint do
278
280
  end
279
281
  end
280
282
 
281
- describe '#declared' do
282
- before do
283
- subject.format :json
284
- subject.params do
285
- requires :first
286
- optional :second
287
- optional :third, default: 'third-default'
288
- optional :nested, type: Hash do
289
- optional :fourth
290
- optional :fifth
291
- optional :nested_two, type: Hash do
292
- optional :sixth
293
- optional :nested_three, type: Hash do
294
- optional :seventh
295
- end
296
- end
297
- end
298
- optional :nested_arr, type: Array do
299
- optional :eighth
300
- end
301
- end
302
- end
303
-
304
- context 'when params are not built with default class' do
305
- it 'returns an object that corresponds with the params class - hash with indifferent access' do
306
- subject.params do
307
- build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
308
- end
309
- subject.get '/declared' do
310
- d = declared(params, include_missing: true)
311
- { declared_class: d.class.to_s }
312
- end
313
-
314
- get '/declared?first=present'
315
- expect(JSON.parse(last_response.body)['declared_class']).to eq('ActiveSupport::HashWithIndifferentAccess')
316
- end
317
-
318
- it 'returns an object that corresponds with the params class - hashie mash' do
319
- subject.params do
320
- build_with Grape::Extensions::Hashie::Mash::ParamBuilder
321
- end
322
- subject.get '/declared' do
323
- d = declared(params, include_missing: true)
324
- { declared_class: d.class.to_s }
325
- end
326
-
327
- get '/declared?first=present'
328
- expect(JSON.parse(last_response.body)['declared_class']).to eq('Hashie::Mash')
329
- end
330
-
331
- it 'returns an object that corresponds with the params class - hash' do
332
- subject.params do
333
- build_with Grape::Extensions::Hash::ParamBuilder
334
- end
335
- subject.get '/declared' do
336
- d = declared(params, include_missing: true)
337
- { declared_class: d.class.to_s }
338
- end
339
-
340
- get '/declared?first=present'
341
- expect(JSON.parse(last_response.body)['declared_class']).to eq('Hash')
342
- end
343
- end
344
-
345
- it 'should show nil for nested params if include_missing is true' do
346
- subject.get '/declared' do
347
- declared(params, include_missing: true)
348
- end
349
-
350
- get '/declared?first=present'
351
- expect(last_response.status).to eq(200)
352
- expect(JSON.parse(last_response.body)['nested']['fourth']).to be_nil
353
- end
354
-
355
- it 'does not work in a before filter' do
356
- subject.before do
357
- declared(params)
358
- end
359
- subject.get('/declared') { declared(params) }
360
-
361
- expect { get('/declared') }.to raise_error(
362
- Grape::DSL::InsideRoute::MethodNotYetAvailable
363
- )
364
- end
365
-
366
- it 'has as many keys as there are declared params' do
367
- subject.get '/declared' do
368
- declared(params)
369
- end
370
- get '/declared?first=present'
371
- expect(last_response.status).to eq(200)
372
- expect(JSON.parse(last_response.body).keys.size).to eq(5)
373
- end
374
-
375
- it 'has a optional param with default value all the time' do
376
- subject.get '/declared' do
377
- declared(params)
378
- end
379
- get '/declared?first=one'
380
- expect(last_response.status).to eq(200)
381
- expect(JSON.parse(last_response.body)['third']).to eql('third-default')
382
- end
383
-
384
- it 'builds nested params' do
385
- subject.get '/declared' do
386
- declared(params)
387
- end
388
-
389
- get '/declared?first=present&nested[fourth]=1'
390
- expect(last_response.status).to eq(200)
391
- expect(JSON.parse(last_response.body)['nested'].keys.size).to eq 3
392
- end
393
-
394
- it 'builds nested params when given array' do
395
- subject.get '/dummy' do
396
- end
397
- subject.params do
398
- requires :first
399
- optional :second
400
- optional :third, default: 'third-default'
401
- optional :nested, type: Array do
402
- optional :fourth
403
- end
404
- end
405
- subject.get '/declared' do
406
- declared(params)
407
- end
408
-
409
- get '/declared?first=present&nested[][fourth]=1&nested[][fourth]=2'
410
- expect(last_response.status).to eq(200)
411
- expect(JSON.parse(last_response.body)['nested'].size).to eq 2
412
- end
413
-
414
- context 'sets nested objects when the param is missing' do
415
- it 'to be a hash when include_missing is true' do
416
- subject.get '/declared' do
417
- declared(params, include_missing: true)
418
- end
419
-
420
- get '/declared?first=present'
421
- expect(last_response.status).to eq(200)
422
- expect(JSON.parse(last_response.body)['nested']).to be_a(Hash)
423
- end
424
-
425
- it 'to be an array when include_missing is true' do
426
- subject.get '/declared' do
427
- declared(params, include_missing: true)
428
- end
429
-
430
- get '/declared?first=present'
431
- expect(last_response.status).to eq(200)
432
- expect(JSON.parse(last_response.body)['nested_arr']).to be_a(Array)
433
- end
434
-
435
- it 'to be nil when include_missing is false' do
436
- subject.get '/declared' do
437
- declared(params, include_missing: false)
438
- end
439
-
440
- get '/declared?first=present'
441
- expect(last_response.status).to eq(200)
442
- expect(JSON.parse(last_response.body)['nested']).to be_nil
443
- end
444
- end
445
-
446
- it 'filters out any additional params that are given' do
447
- subject.get '/declared' do
448
- declared(params)
449
- end
450
- get '/declared?first=one&other=two'
451
- expect(last_response.status).to eq(200)
452
- expect(JSON.parse(last_response.body).key?(:other)).to eq false
453
- end
454
-
455
- it 'stringifies if that option is passed' do
456
- subject.get '/declared' do
457
- declared(params, stringify: true)
458
- end
459
-
460
- get '/declared?first=one&other=two'
461
- expect(last_response.status).to eq(200)
462
- expect(JSON.parse(last_response.body)['first']).to eq 'one'
463
- end
464
-
465
- it 'does not include missing attributes if that option is passed' do
466
- subject.get '/declared' do
467
- error! 'expected nil', 400 if declared(params, include_missing: false).key?(:second)
468
- ''
469
- end
470
-
471
- get '/declared?first=one&other=two'
472
- expect(last_response.status).to eq(200)
473
- end
474
-
475
- it 'does not include aliased missing attributes if that option is passed' do
476
- subject.params do
477
- optional :aliased_original, as: :aliased
478
- end
479
- subject.get '/declared' do
480
- error! 'expected nil', 400 if declared(params, include_missing: false).key?(:aliased)
481
- ''
482
- end
483
-
484
- get '/declared?first=one&other=two'
485
- expect(last_response.status).to eq(200)
486
- end
487
-
488
- it 'includes attributes with value that evaluates to false' do
489
- subject.params do
490
- requires :first
491
- optional :boolean
492
- end
493
-
494
- subject.post '/declared' do
495
- error!('expected false', 400) if declared(params, include_missing: false)[:boolean] != false
496
- ''
497
- end
498
-
499
- post '/declared', ::Grape::Json.dump(first: 'one', boolean: false), 'CONTENT_TYPE' => 'application/json'
500
- expect(last_response.status).to eq(201)
501
- end
502
-
503
- it 'includes attributes with value that evaluates to nil' do
504
- subject.params do
505
- requires :first
506
- optional :second
507
- end
508
-
509
- subject.post '/declared' do
510
- error!('expected nil', 400) unless declared(params, include_missing: false)[:second].nil?
511
- ''
512
- end
513
-
514
- post '/declared', ::Grape::Json.dump(first: 'one', second: nil), 'CONTENT_TYPE' => 'application/json'
515
- expect(last_response.status).to eq(201)
516
- end
517
-
518
- it 'includes missing attributes with defaults when there are nested hashes' do
519
- subject.get '/dummy' do
520
- end
521
-
522
- subject.params do
523
- requires :first
524
- optional :second
525
- optional :third, default: nil
526
- optional :nested, type: Hash do
527
- optional :fourth, default: nil
528
- optional :fifth, default: nil
529
- requires :nested_nested, type: Hash do
530
- optional :sixth, default: 'sixth-default'
531
- optional :seven, default: nil
532
- end
533
- end
534
- end
535
-
536
- subject.get '/declared' do
537
- declared(params, include_missing: false)
538
- end
539
-
540
- get '/declared?first=present&nested[fourth]=&nested[nested_nested][sixth]=sixth'
541
- json = JSON.parse(last_response.body)
542
- expect(last_response.status).to eq(200)
543
- expect(json['first']).to eq 'present'
544
- expect(json['nested'].keys).to eq %w[fourth fifth nested_nested]
545
- expect(json['nested']['fourth']).to eq ''
546
- expect(json['nested']['nested_nested'].keys).to eq %w[sixth seven]
547
- expect(json['nested']['nested_nested']['sixth']).to eq 'sixth'
548
- end
549
-
550
- it 'does not include missing attributes when there are nested hashes' do
551
- subject.get '/dummy' do
552
- end
553
-
554
- subject.params do
555
- requires :first
556
- optional :second
557
- optional :third
558
- optional :nested, type: Hash do
559
- optional :fourth
560
- optional :fifth
561
- end
562
- end
563
-
564
- subject.get '/declared' do
565
- declared(params, include_missing: false)
566
- end
567
-
568
- get '/declared?first=present&nested[fourth]=4'
569
- json = JSON.parse(last_response.body)
570
- expect(last_response.status).to eq(200)
571
- expect(json['first']).to eq 'present'
572
- expect(json['nested'].keys).to eq %w[fourth]
573
- expect(json['nested']['fourth']).to eq '4'
574
- end
575
- end
576
-
577
- describe '#declared; call from child namespace' do
578
- before do
579
- subject.format :json
580
- subject.namespace :parent do
581
- params do
582
- requires :parent_name, type: String
583
- end
584
-
585
- namespace ':parent_name' do
586
- params do
587
- requires :child_name, type: String
588
- requires :child_age, type: Integer
589
- end
590
-
591
- namespace ':child_name' do
592
- params do
593
- requires :grandchild_name, type: String
594
- end
595
-
596
- get ':grandchild_name' do
597
- {
598
- 'params' => params,
599
- 'without_parent_namespaces' => declared(params, include_parent_namespaces: false),
600
- 'with_parent_namespaces' => declared(params, include_parent_namespaces: true)
601
- }
602
- end
603
- end
604
- end
605
- end
606
-
607
- get '/parent/foo/bar/baz', child_age: 5, extra: 'hello'
608
- end
609
-
610
- let(:parsed_response) { JSON.parse(last_response.body, symbolize_names: true) }
611
-
612
- it { expect(last_response.status).to eq 200 }
613
-
614
- context 'with include_parent_namespaces: false' do
615
- it 'returns declared parameters only from current namespace' do
616
- expect(parsed_response[:without_parent_namespaces]).to eq(
617
- grandchild_name: 'baz'
618
- )
619
- end
620
- end
621
-
622
- context 'with include_parent_namespaces: true' do
623
- it 'returns declared parameters from every parent namespace' do
624
- expect(parsed_response[:with_parent_namespaces]).to eq(
625
- parent_name: 'foo',
626
- child_name: 'bar',
627
- grandchild_name: 'baz',
628
- child_age: 5
629
- )
630
- end
631
- end
632
-
633
- context 'without declaration' do
634
- it 'returns all requested parameters' do
635
- expect(parsed_response[:params]).to eq(
636
- parent_name: 'foo',
637
- child_name: 'bar',
638
- grandchild_name: 'baz',
639
- child_age: 5,
640
- extra: 'hello'
641
- )
642
- end
643
- end
644
- end
645
-
646
- describe '#declared; from a nested mounted endpoint' do
647
- before do
648
- doubly_mounted = Class.new(Grape::API)
649
- doubly_mounted.namespace :more do
650
- params do
651
- requires :y, type: Integer
652
- end
653
- route_param :y do
654
- get do
655
- {
656
- params: params,
657
- declared_params: declared(params)
658
- }
659
- end
660
- end
661
- end
662
-
663
- mounted = Class.new(Grape::API)
664
- mounted.namespace :another do
665
- params do
666
- requires :mount_space, type: Integer
667
- end
668
- route_param :mount_space do
669
- mount doubly_mounted
670
- end
671
- end
672
-
673
- subject.format :json
674
- subject.namespace :something do
675
- params do
676
- requires :id, type: Integer
677
- end
678
- resource ':id' do
679
- mount mounted
680
- end
681
- end
682
- end
683
-
684
- it 'can access parent attributes' do
685
- get '/something/123/another/456/more/789'
686
- expect(last_response.status).to eq 200
687
- json = JSON.parse(last_response.body, symbolize_names: true)
688
-
689
- # test all three levels of params
690
- expect(json[:declared_params][:y]).to eq 789
691
- expect(json[:declared_params][:mount_space]).to eq 456
692
- expect(json[:declared_params][:id]).to eq 123
693
- end
694
- end
695
-
696
- describe '#declared; mixed nesting' do
697
- before do
698
- subject.format :json
699
- subject.resource :users do
700
- route_param :id, type: Integer, desc: 'ID desc' do
701
- # Adding this causes route_setting(:declared_params) to be nil for the
702
- # get block in namespace 'foo' below
703
- get do
704
- end
705
-
706
- namespace 'foo' do
707
- get do
708
- {
709
- params: params,
710
- declared_params: declared(params),
711
- declared_params_no_parent: declared(params, include_parent_namespaces: false)
712
- }
713
- end
714
- end
715
- end
716
- end
717
- end
718
-
719
- it 'can access parent route_param' do
720
- get '/users/123/foo', bar: 'bar'
721
- expect(last_response.status).to eq 200
722
- json = JSON.parse(last_response.body, symbolize_names: true)
723
-
724
- expect(json[:declared_params][:id]).to eq 123
725
- expect(json[:declared_params_no_parent][:id]).to eq nil
726
- end
727
- end
728
-
729
- describe '#declared; with multiple route_param' do
730
- before do
731
- mounted = Class.new(Grape::API)
732
- mounted.namespace :albums do
733
- get do
734
- declared(params)
735
- end
736
- end
737
-
738
- subject.format :json
739
- subject.namespace :artists do
740
- route_param :id, type: Integer do
741
- get do
742
- declared(params)
743
- end
744
-
745
- params do
746
- requires :filter, type: String
747
- end
748
- get :some_route do
749
- declared(params)
750
- end
751
- end
752
-
753
- route_param :artist_id, type: Integer do
754
- namespace :compositions do
755
- get do
756
- declared(params)
757
- end
758
- end
759
- end
760
-
761
- route_param :compositor_id, type: Integer do
762
- mount mounted
763
- end
764
- end
765
- end
766
-
767
- it 'return only :id without :artist_id' do
768
- get '/artists/1'
769
- json = JSON.parse(last_response.body, symbolize_names: true)
770
-
771
- expect(json.key?(:id)).to be_truthy
772
- expect(json.key?(:artist_id)).not_to be_truthy
773
- end
774
-
775
- it 'return only :artist_id without :id' do
776
- get '/artists/1/compositions'
777
- json = JSON.parse(last_response.body, symbolize_names: true)
778
-
779
- expect(json.key?(:artist_id)).to be_truthy
780
- expect(json.key?(:id)).not_to be_truthy
781
- end
782
-
783
- it 'return :filter and :id parameters in declared for second enpoint inside route_param' do
784
- get '/artists/1/some_route', filter: 'some_filter'
785
- json = JSON.parse(last_response.body, symbolize_names: true)
786
-
787
- expect(json.key?(:filter)).to be_truthy
788
- expect(json.key?(:id)).to be_truthy
789
- expect(json.key?(:artist_id)).not_to be_truthy
790
- end
791
-
792
- it 'return :compositor_id for mounter in route_param' do
793
- get '/artists/1/albums'
794
- json = JSON.parse(last_response.body, symbolize_names: true)
795
-
796
- expect(json.key?(:compositor_id)).to be_truthy
797
- expect(json.key?(:id)).not_to be_truthy
798
- expect(json.key?(:artist_id)).not_to be_truthy
799
- end
800
- end
801
-
802
283
  describe '#params' do
803
284
  it 'is available to the caller' do
804
285
  subject.get('/hey') do
@@ -939,6 +420,19 @@ describe Grape::Endpoint do
939
420
  expect(last_response.status).to eq(201)
940
421
  expect(last_response.body).to eq('Bob')
941
422
  end
423
+
424
+ # Rack swallowed this error until v2.2.0
425
+ it 'returns a 400 if given an invalid multipart body', if: Gem::Version.new(Rack.release) >= Gem::Version.new('2.2.0') do
426
+ subject.params do
427
+ requires :file, type: Rack::Multipart::UploadedFile
428
+ end
429
+ subject.post '/upload' do
430
+ params[:file][:filename]
431
+ end
432
+ post '/upload', { file: '' }, 'CONTENT_TYPE' => 'multipart/form-data; boundary=foobar'
433
+ expect(last_response.status).to eq(400)
434
+ expect(last_response.body).to eq('Empty message body supplied with multipart/form-data; boundary=foobar content-type')
435
+ end
942
436
  end
943
437
 
944
438
  it 'responds with a 415 for an unsupported content-type' do
@@ -1089,6 +583,36 @@ describe Grape::Endpoint do
1089
583
  expect(last_response.headers['X-Custom']).to eq('value')
1090
584
  end
1091
585
 
586
+ it 'merges additional headers with headers set before call' do
587
+ subject.before do
588
+ header 'X-Before-Test', 'before-sample'
589
+ end
590
+
591
+ subject.get '/hey' do
592
+ header 'X-Test', 'test-sample'
593
+ error!({ 'dude' => 'rad' }, 403, 'X-Error' => 'error')
594
+ end
595
+
596
+ get '/hey.json'
597
+ expect(last_response.headers['X-Before-Test']).to eq('before-sample')
598
+ expect(last_response.headers['X-Test']).to eq('test-sample')
599
+ expect(last_response.headers['X-Error']).to eq('error')
600
+ end
601
+
602
+ it 'does not merges additional headers with headers set after call' do
603
+ subject.after do
604
+ header 'X-After-Test', 'after-sample'
605
+ end
606
+
607
+ subject.get '/hey' do
608
+ error!({ 'dude' => 'rad' }, 403, 'X-Error' => 'error')
609
+ end
610
+
611
+ get '/hey.json'
612
+ expect(last_response.headers['X-Error']).to eq('error')
613
+ expect(last_response.headers['X-After-Test']).to be_nil
614
+ end
615
+
1092
616
  it 'sets the status code for the endpoint' do
1093
617
  memoized_endpoint = nil
1094
618
 
@@ -1492,6 +1016,9 @@ describe Grape::Endpoint do
1492
1016
  have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1493
1017
  filters: [],
1494
1018
  type: :after }),
1019
+ have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1020
+ filters: [],
1021
+ type: :finally }),
1495
1022
  have_attributes(name: 'endpoint_run.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1496
1023
  env: an_instance_of(Hash) }),
1497
1024
  have_attributes(name: 'format_response.grape', payload: { env: an_instance_of(Hash),
@@ -1518,6 +1045,9 @@ describe Grape::Endpoint do
1518
1045
  have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1519
1046
  filters: [],
1520
1047
  type: :after }),
1048
+ have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
1049
+ filters: [],
1050
+ type: :finally }),
1521
1051
  have_attributes(name: 'format_response.grape', payload: { env: an_instance_of(Hash),
1522
1052
  formatter: a_kind_of(Module) })
1523
1053
  )