grape 1.1.0 → 1.5.3

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 (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
  )