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::Validations::ExceptValuesValidator do
@@ -6,6 +8,7 @@ describe Grape::Validations::ExceptValuesValidator do
6
8
  DEFAULT_EXCEPTS = ['invalid-type1', 'invalid-type2', 'invalid-type3'].freeze
7
9
  class << self
8
10
  attr_accessor :excepts
11
+
9
12
  def excepts
10
13
  @excepts ||= []
11
14
  [DEFAULT_EXCEPTS + @excepts].flatten.uniq
@@ -110,7 +113,7 @@ describe Grape::Validations::ExceptValuesValidator do
110
113
  optional: { type: Array[Integer], except_values: [10, 11], default: 12 },
111
114
  tests: [
112
115
  { value: 'invalid-type1', rc: 400, body: { error: 'type is invalid' }.to_json },
113
- { value: 10, rc: 400, body: { error: 'type has a value not allowed' }.to_json },
116
+ { value: 10, rc: 400, body: { error: 'type is invalid' }.to_json },
114
117
  { value: [10], rc: 400, body: { error: 'type has a value not allowed' }.to_json },
115
118
  { value: ['3'], rc: 200, body: { type: [3] }.to_json },
116
119
  { value: [3], rc: 200, body: { type: [3] }.to_json },
@@ -1,62 +1,221 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::MutualExclusionValidator do
4
6
  describe '#validate!' do
5
- let(:scope) do
6
- Struct.new(:opts) do
7
- def params(arg)
8
- arg
7
+ subject(:validate) { post path, params }
8
+
9
+ module ValidationsSpec
10
+ module MutualExclusionValidatorSpec
11
+ class API < Grape::API
12
+ rescue_from Grape::Exceptions::ValidationErrors do |e|
13
+ error!(e.errors.transform_keys! { |key| key.join(',') }, 400)
14
+ end
15
+
16
+ params do
17
+ optional :beer
18
+ optional :wine
19
+ optional :grapefruit
20
+ mutually_exclusive :beer, :wine, :grapefruit
21
+ end
22
+ post do
23
+ end
24
+
25
+ params do
26
+ optional :beer
27
+ optional :wine
28
+ optional :grapefruit
29
+ optional :other
30
+ mutually_exclusive :beer, :wine, :grapefruit
31
+ end
32
+ post 'mixed-params' do
33
+ end
34
+
35
+ params do
36
+ optional :beer
37
+ optional :wine
38
+ optional :grapefruit
39
+ mutually_exclusive :beer, :wine, :grapefruit, message: 'you should not mix beer and wine'
40
+ end
41
+ post '/custom-message' do
42
+ end
43
+
44
+ params do
45
+ requires :item, type: Hash do
46
+ optional :beer
47
+ optional :wine
48
+ optional :grapefruit
49
+ mutually_exclusive :beer, :wine, :grapefruit
50
+ end
51
+ end
52
+ post '/nested-hash' do
53
+ end
54
+
55
+ params do
56
+ optional :item, type: Hash do
57
+ optional :beer
58
+ optional :wine
59
+ optional :grapefruit
60
+ mutually_exclusive :beer, :wine, :grapefruit
61
+ end
62
+ end
63
+ post '/nested-optional-hash' do
64
+ end
65
+
66
+ params do
67
+ requires :items, type: Array do
68
+ optional :beer
69
+ optional :wine
70
+ optional :grapefruit
71
+ mutually_exclusive :beer, :wine, :grapefruit
72
+ end
73
+ end
74
+ post '/nested-array' do
75
+ end
76
+
77
+ params do
78
+ requires :items, type: Array do
79
+ requires :nested_items, type: Array do
80
+ optional :beer, :wine, :grapefruit, type: Boolean
81
+ mutually_exclusive :beer, :wine, :grapefruit
82
+ end
83
+ end
84
+ end
85
+ post '/deeply-nested-array' do
86
+ end
9
87
  end
10
88
  end
11
89
  end
12
- let(:mutually_exclusive_params) { %i[beer wine grapefruit] }
13
- let(:validator) { described_class.new(mutually_exclusive_params, {}, false, scope.new) }
90
+
91
+ def app
92
+ ValidationsSpec::MutualExclusionValidatorSpec::API
93
+ end
14
94
 
15
95
  context 'when all mutually exclusive params are present' do
96
+ let(:path) { '/' }
16
97
  let(:params) { { beer: true, wine: true, grapefruit: true } }
17
98
 
18
- it 'raises a validation exception' do
19
- expect do
20
- validator.validate! params
21
- end.to raise_error(Grape::Exceptions::Validation)
99
+ it 'returns a validation error' do
100
+ validate
101
+ expect(last_response.status).to eq 400
102
+ expect(JSON.parse(last_response.body)).to eq(
103
+ 'beer,wine,grapefruit' => ['are mutually exclusive']
104
+ )
22
105
  end
23
106
 
24
107
  context 'mixed with other params' do
25
- let(:mixed_params) { params.merge!(other: true, andanother: true) }
108
+ let(:path) { '/mixed-params' }
109
+ let(:params) { { beer: true, wine: true, grapefruit: true, other: true } }
26
110
 
27
- it 'still raises a validation exception' do
28
- expect do
29
- validator.validate! mixed_params
30
- end.to raise_error(Grape::Exceptions::Validation)
111
+ it 'returns a validation error' do
112
+ validate
113
+ expect(last_response.status).to eq 400
114
+ expect(JSON.parse(last_response.body)).to eq(
115
+ 'beer,wine,grapefruit' => ['are mutually exclusive']
116
+ )
31
117
  end
32
118
  end
33
119
  end
34
120
 
35
121
  context 'when a subset of mutually exclusive params are present' do
122
+ let(:path) { '/' }
36
123
  let(:params) { { beer: true, grapefruit: true } }
37
124
 
38
- it 'raises a validation exception' do
39
- expect do
40
- validator.validate! params
41
- end.to raise_error(Grape::Exceptions::Validation)
125
+ it 'returns a validation error' do
126
+ validate
127
+ expect(last_response.status).to eq 400
128
+ expect(JSON.parse(last_response.body)).to eq(
129
+ 'beer,grapefruit' => ['are mutually exclusive']
130
+ )
42
131
  end
43
132
  end
44
133
 
45
- context 'when params keys come as strings' do
46
- let(:params) { { 'beer' => true, 'grapefruit' => true } }
134
+ context 'when custom message is specified' do
135
+ let(:path) { '/custom-message' }
136
+ let(:params) { { beer: true, wine: true } }
47
137
 
48
- it 'raises a validation exception' do
49
- expect do
50
- validator.validate! params
51
- end.to raise_error(Grape::Exceptions::Validation)
138
+ it 'returns a validation error' do
139
+ validate
140
+ expect(last_response.status).to eq 400
141
+ expect(JSON.parse(last_response.body)).to eq(
142
+ 'beer,wine' => ['you should not mix beer and wine']
143
+ )
52
144
  end
53
145
  end
54
146
 
55
147
  context 'when no mutually exclusive params are present' do
148
+ let(:path) { '/' }
56
149
  let(:params) { { beer: true, somethingelse: true } }
57
150
 
58
- it 'params' do
59
- expect(validator.validate!(params)).to eql params
151
+ it 'does not return a validation error' do
152
+ validate
153
+ expect(last_response.status).to eq 201
154
+ end
155
+ end
156
+
157
+ context 'when mutually exclusive params are nested inside required hash' do
158
+ let(:path) { '/nested-hash' }
159
+ let(:params) { { item: { beer: true, wine: true } } }
160
+
161
+ it 'returns a validation error with full names of the params' do
162
+ validate
163
+ expect(last_response.status).to eq 400
164
+ expect(JSON.parse(last_response.body)).to eq(
165
+ 'item[beer],item[wine]' => ['are mutually exclusive']
166
+ )
167
+ end
168
+ end
169
+
170
+ context 'when mutually exclusive params are nested inside optional hash' do
171
+ let(:path) { '/nested-optional-hash' }
172
+
173
+ context 'when params are passed' do
174
+ let(:params) { { item: { beer: true, wine: true } } }
175
+
176
+ it 'returns a validation error with full names of the params' do
177
+ validate
178
+ expect(last_response.status).to eq 400
179
+ expect(JSON.parse(last_response.body)).to eq(
180
+ 'item[beer],item[wine]' => ['are mutually exclusive']
181
+ )
182
+ end
183
+ end
184
+
185
+ context 'when params are empty' do
186
+ let(:params) { {} }
187
+
188
+ it 'does not return a validation error' do
189
+ validate
190
+ expect(last_response.status).to eq 201
191
+ end
192
+ end
193
+ end
194
+
195
+ context 'when mutually exclusive params are nested inside array' do
196
+ let(:path) { '/nested-array' }
197
+ let(:params) { { items: [{ beer: true, wine: true }, { wine: true, grapefruit: true }] } }
198
+
199
+ it 'returns a validation error with full names of the params' do
200
+ validate
201
+ expect(last_response.status).to eq 400
202
+ expect(JSON.parse(last_response.body)).to eq(
203
+ 'items[0][beer],items[0][wine]' => ['are mutually exclusive'],
204
+ 'items[1][wine],items[1][grapefruit]' => ['are mutually exclusive']
205
+ )
206
+ end
207
+ end
208
+
209
+ context 'when mutually exclusive params are deeply nested' do
210
+ let(:path) { '/deeply-nested-array' }
211
+ let(:params) { { items: [{ nested_items: [{ beer: true, wine: true }] }] } }
212
+
213
+ it 'returns a validation error with full names of the params' do
214
+ validate
215
+ expect(last_response.status).to eq 400
216
+ expect(JSON.parse(last_response.body)).to eq(
217
+ 'items[0][nested_items][0][beer],items[0][nested_items][0][wine]' => ['are mutually exclusive']
218
+ )
60
219
  end
61
220
  end
62
221
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::PresenceValidator do
@@ -269,4 +271,32 @@ describe Grape::Validations::PresenceValidator do
269
271
  expect(last_response.body).to eq('Hello optional'.to_json)
270
272
  end
271
273
  end
274
+
275
+ context 'with a custom type' do
276
+ it 'does not validate their type when it is missing' do
277
+ class CustomType
278
+ def self.parse(value)
279
+ return if value.blank?
280
+
281
+ new
282
+ end
283
+ end
284
+
285
+ subject.params do
286
+ requires :custom, type: CustomType
287
+ end
288
+ subject.get '/custom' do
289
+ 'custom'
290
+ end
291
+
292
+ get 'custom'
293
+
294
+ expect(last_response.status).to eq(400)
295
+ expect(last_response.body).to eq('{"error":"custom is missing"}')
296
+
297
+ get 'custom', custom: 'filled'
298
+
299
+ expect(last_response.status).to eq(200)
300
+ end
301
+ end
272
302
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::RegexpValidator do
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Grape::Validations::SameAsValidator do
6
+ module ValidationsSpec
7
+ module SameAsValidatorSpec
8
+ class API < Grape::API
9
+ params do
10
+ requires :password
11
+ requires :password_confirmation, same_as: :password
12
+ end
13
+ post do
14
+ end
15
+
16
+ params do
17
+ requires :password
18
+ requires :password_confirmation, same_as: { value: :password, message: 'not match' }
19
+ end
20
+ post '/custom-message' do
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ def app
27
+ ValidationsSpec::SameAsValidatorSpec::API
28
+ end
29
+
30
+ describe '/' do
31
+ context 'is the same' do
32
+ it do
33
+ post '/', password: '987654', password_confirmation: '987654'
34
+ expect(last_response.status).to eq(201)
35
+ expect(last_response.body).to eq('')
36
+ end
37
+ end
38
+
39
+ context 'is not the same' do
40
+ it do
41
+ post '/', password: '123456', password_confirmation: 'whatever'
42
+ expect(last_response.status).to eq(400)
43
+ expect(last_response.body).to eq('password_confirmation is not the same as password')
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '/custom-message' do
49
+ context 'is the same' do
50
+ it do
51
+ post '/custom-message', password: '987654', password_confirmation: '987654'
52
+ expect(last_response.status).to eq(201)
53
+ expect(last_response.body).to eq('')
54
+ end
55
+ end
56
+
57
+ context 'is not the same' do
58
+ it do
59
+ post '/custom-message', password: '123456', password_confirmation: 'whatever'
60
+ expect(last_response.status).to eq(400)
61
+ expect(last_response.body).to eq('password_confirmation not match')
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::ValuesValidator do
@@ -222,6 +224,11 @@ describe Grape::Validations::ValuesValidator do
222
224
  requires :type, values: { proc: ->(v) { ValuesModel.values.include? v }, message: 'failed check' }
223
225
  end
224
226
  get '/proc/message'
227
+
228
+ params do
229
+ optional :name, type: String, values: %w[a b], allow_blank: true
230
+ end
231
+ get '/allow_blank'
225
232
  end
226
233
  end
227
234
  end
@@ -312,7 +319,7 @@ describe Grape::Validations::ValuesValidator do
312
319
  expect(last_response.status).to eq 200
313
320
  end
314
321
 
315
- it 'allows for an optional param with a list of values' do
322
+ it 'accepts for an optional param with a list of values' do
316
323
  put('/optional_with_array_of_string_values', optional: nil)
317
324
  expect(last_response.status).to eq 200
318
325
  end
@@ -431,11 +438,21 @@ describe Grape::Validations::ValuesValidator do
431
438
  end.to raise_error Grape::Exceptions::IncompatibleOptionValues
432
439
  end
433
440
 
434
- it 'allows values to be true or false when setting the type to boolean' do
435
- get('/values/optional_boolean', type: true)
436
- expect(last_response.status).to eq 200
437
- expect(last_response.body).to eq({ type: true }.to_json)
441
+ context 'boolean values' do
442
+ it 'allows a value from the list' do
443
+ get('/values/optional_boolean', type: true)
444
+
445
+ expect(last_response.status).to eq 200
446
+ expect(last_response.body).to eq({ type: true }.to_json)
447
+ end
448
+
449
+ it 'rejects a value which is not in the list' do
450
+ get('/values/optional_boolean', type: false)
451
+
452
+ expect(last_response.body).to eq({ error: 'type does not have a valid value' }.to_json)
453
+ end
438
454
  end
455
+
439
456
  it 'allows values to be a kind of the coerced type not just an instance of it' do
440
457
  get('/values/coercion', type: 10)
441
458
  expect(last_response.status).to eq 200
@@ -462,6 +479,14 @@ describe Grape::Validations::ValuesValidator do
462
479
  end.to raise_error Grape::Exceptions::IncompatibleOptionValues
463
480
  end
464
481
 
482
+ it 'allows a blank value when the allow_blank option is true' do
483
+ get 'allow_blank', name: nil
484
+ expect(last_response.status).to eq(200)
485
+
486
+ get 'allow_blank', name: ''
487
+ expect(last_response.status).to eq(200)
488
+ end
489
+
465
490
  context 'with a lambda values' do
466
491
  subject do
467
492
  Class.new(Grape::API) do