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::AttributesIterator do
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe 'Validator with instance variables' do
4
6
  let(:validator_type) do
5
7
  Class.new(Grape::Validations::Base) do
6
8
  def validate_param!(_attr_name, _params)
7
- if @instance_variable
8
- raise Grape::Exceptions::Validation, params: ['params'],
9
- message: 'This should never happen'
9
+ if instance_variable_defined?(:@instance_variable) && @instance_variable
10
+ raise Grape::Exceptions::Validation.new(params: ['params'],
11
+ message: 'This should never happen')
10
12
  end
11
13
  @instance_variable = true
12
14
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Grape::Validations::MultipleAttributesIterator do
6
+ describe '#each' do
7
+ subject(:iterator) { described_class.new(validator, scope, params) }
8
+ let(:scope) { Grape::Validations::ParamsScope.new(api: Class.new(Grape::API)) }
9
+ let(:validator) { double(attrs: %i[first second third]) }
10
+
11
+ context 'when params is a hash' do
12
+ let(:params) do
13
+ { first: 'string', second: 'string' }
14
+ end
15
+
16
+ it 'yields the whole params hash and the skipped flag without the list of attrs' do
17
+ expect { |b| iterator.each(&b) }.to yield_with_args(params, false)
18
+ end
19
+ end
20
+
21
+ context 'when params is an array' do
22
+ let(:params) do
23
+ [{ first: 'string1', second: 'string1' }, { first: 'string2', second: 'string2' }]
24
+ end
25
+
26
+ it 'yields each element of the array without the list of attrs' do
27
+ expect { |b| iterator.each(&b) }.to yield_successive_args([params[0], false], [params[1], false])
28
+ end
29
+ end
30
+
31
+ context 'when params is empty optional placeholder' do
32
+ let(:params) do
33
+ [Grape::DSL::Parameters::EmptyOptionalValue, { first: 'string2', second: 'string2' }]
34
+ end
35
+
36
+ it 'yields each element of the array without the list of attrs' do
37
+ expect { |b| iterator.each(&b) }.to yield_successive_args([Grape::DSL::Parameters::EmptyOptionalValue, true], [params[1], false])
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Grape::Validations::ParamsScope do
@@ -30,7 +32,7 @@ describe Grape::Validations::ParamsScope do
30
32
  context 'when the default value is false' do
31
33
  before do
32
34
  subject.params do
33
- optional :bool, type: Virtus::Attribute::Boolean, default: false
35
+ optional :bool, type: Grape::API::Boolean, default: false
34
36
  end
35
37
  subject.get
36
38
  end
@@ -121,14 +123,14 @@ describe Grape::Validations::ParamsScope do
121
123
  end
122
124
  end
123
125
 
124
- context 'param alias' do
126
+ context 'param renaming' do
125
127
  it do
126
128
  subject.params do
127
129
  requires :foo, as: :bar
128
130
  optional :super, as: :hiper
129
131
  end
130
- subject.get('/alias') { "#{declared(params)['bar']}-#{declared(params)['hiper']}" }
131
- get '/alias', foo: 'any', super: 'any2'
132
+ subject.get('/renaming') { "#{declared(params)['bar']}-#{declared(params)['hiper']}" }
133
+ get '/renaming', foo: 'any', super: 'any2'
132
134
 
133
135
  expect(last_response.status).to eq(200)
134
136
  expect(last_response.body).to eq('any-any2')
@@ -138,8 +140,8 @@ describe Grape::Validations::ParamsScope do
138
140
  subject.params do
139
141
  requires :foo, as: :bar, type: String, coerce_with: ->(c) { c.strip }
140
142
  end
141
- subject.get('/alias-coerced') { "#{params['bar']}-#{params['foo']}" }
142
- get '/alias-coerced', foo: ' there we go '
143
+ subject.get('/renaming-coerced') { "#{params['bar']}-#{params['foo']}" }
144
+ get '/renaming-coerced', foo: ' there we go '
143
145
 
144
146
  expect(last_response.status).to eq(200)
145
147
  expect(last_response.body).to eq('there we go-')
@@ -149,12 +151,35 @@ describe Grape::Validations::ParamsScope do
149
151
  subject.params do
150
152
  requires :foo, as: :bar, allow_blank: false
151
153
  end
152
- subject.get('/alias-not-blank') {}
153
- get '/alias-not-blank', foo: ''
154
+ subject.get('/renaming-not-blank') {}
155
+ get '/renaming-not-blank', foo: ''
154
156
 
155
157
  expect(last_response.status).to eq(400)
156
158
  expect(last_response.body).to eq('foo is empty')
157
159
  end
160
+
161
+ it do
162
+ subject.params do
163
+ requires :foo, as: :bar, allow_blank: false
164
+ end
165
+ subject.get('/renaming-not-blank-with-value') {}
166
+ get '/renaming-not-blank-with-value', foo: 'any'
167
+
168
+ expect(last_response.status).to eq(200)
169
+ end
170
+
171
+ it do
172
+ subject.params do
173
+ requires :foo, as: :baz, type: Hash do
174
+ requires :bar, as: :qux
175
+ end
176
+ end
177
+ subject.get('/nested-renaming') { declared(params).to_json }
178
+ get '/nested-renaming', foo: { bar: 'any' }
179
+
180
+ expect(last_response.status).to eq(200)
181
+ expect(last_response.body).to eq('{"baz":{"qux":"any"}}')
182
+ end
158
183
  end
159
184
 
160
185
  context 'array without coerce type explicitly given' do
@@ -479,7 +504,50 @@ describe Grape::Validations::ParamsScope do
479
504
  end.to_not raise_error
480
505
  end
481
506
 
482
- it 'allows aliasing of dependent parameters' do
507
+ it 'does not raise an error if when using nested given' do
508
+ expect do
509
+ subject.params do
510
+ optional :a, type: Hash do
511
+ requires :b
512
+ end
513
+ given :a do
514
+ requires :c
515
+ given :c do
516
+ requires :d
517
+ end
518
+ end
519
+ end
520
+ end.to_not raise_error
521
+ end
522
+
523
+ it 'allows nested dependent parameters' do
524
+ subject.params do
525
+ optional :a
526
+ given a: ->(val) { val == 'a' } do
527
+ optional :b
528
+ given b: ->(val) { val == 'b' } do
529
+ optional :c
530
+ given c: ->(val) { val == 'c' } do
531
+ requires :d
532
+ end
533
+ end
534
+ end
535
+ end
536
+ subject.get('/') { declared(params).to_json }
537
+
538
+ get '/'
539
+ expect(last_response.status).to eq 200
540
+
541
+ get '/', a: 'a', b: 'b', c: 'c'
542
+ expect(last_response.status).to eq 400
543
+ expect(last_response.body).to eq 'd is missing'
544
+
545
+ get '/', a: 'a', b: 'b', c: 'c', d: 'd'
546
+ expect(last_response.status).to eq 200
547
+ expect(last_response.body).to eq({ a: 'a', b: 'b', c: 'c', d: 'd' }.to_json)
548
+ end
549
+
550
+ it 'allows renaming of dependent parameters' do
483
551
  subject.params do
484
552
  optional :a
485
553
  given :a do
@@ -497,6 +565,34 @@ describe Grape::Validations::ParamsScope do
497
565
  expect(body.keys).to_not include('b')
498
566
  end
499
567
 
568
+ it 'allows renaming of dependent on parameter' do
569
+ subject.params do
570
+ optional :a, as: :b
571
+ given b: ->(val) { val == 'x' } do
572
+ requires :c
573
+ end
574
+ end
575
+ subject.get('/') { declared(params) }
576
+
577
+ get '/', a: 'x'
578
+ expect(last_response.status).to eq 400
579
+ expect(last_response.body).to eq 'c is missing'
580
+
581
+ get '/', a: 'y'
582
+ expect(last_response.status).to eq 200
583
+ end
584
+
585
+ it 'raises an error if the dependent parameter is not the renamed one' do
586
+ expect do
587
+ subject.params do
588
+ optional :a, as: :b
589
+ given :a do
590
+ requires :c
591
+ end
592
+ end
593
+ end.to raise_error(Grape::Exceptions::UnknownParameter)
594
+ end
595
+
500
596
  it 'does not validate nested requires when given is false' do
501
597
  subject.params do
502
598
  requires :a, type: String, allow_blank: false, values: %w[x y z]
@@ -537,6 +633,32 @@ describe Grape::Validations::ParamsScope do
537
633
  expect(last_response.status).to eq(200)
538
634
  end
539
635
 
636
+ it 'detect unmet nested dependency' do
637
+ subject.params do
638
+ requires :a, type: String, allow_blank: false, values: %w[x y z]
639
+ given a: ->(val) { val == 'z' } do
640
+ requires :inner3, type: Array, allow_blank: false do
641
+ requires :bar, type: String, allow_blank: false
642
+ given bar: ->(val) { val == 'b' } do
643
+ requires :baz, type: Array do
644
+ optional :baz_category, type: String
645
+ end
646
+ end
647
+ given bar: ->(val) { val == 'c' } do
648
+ requires :baz, type: Array do
649
+ requires :baz_category, type: String
650
+ end
651
+ end
652
+ end
653
+ end
654
+ end
655
+ subject.get('/nested-dependency') { declared(params).to_json }
656
+
657
+ get '/nested-dependency', a: 'z', inner3: [{ bar: 'c', baz: [{ unrelated: 'nope' }] }]
658
+ expect(last_response.status).to eq(400)
659
+ expect(last_response.body).to eq 'inner3[0][baz][0][baz_category] is missing'
660
+ end
661
+
540
662
  it 'includes the parameter within #declared(params)' do
541
663
  get '/test', a: true, b: true
542
664
 
@@ -592,7 +714,53 @@ describe Grape::Validations::ParamsScope do
592
714
  end
593
715
  end
594
716
 
717
+ context 'default value in given block' do
718
+ before do
719
+ subject.params do
720
+ optional :a, values: %w[a b]
721
+ given a: ->(val) { val == 'a' } do
722
+ optional :b, default: 'default'
723
+ end
724
+ end
725
+ subject.get('/') { params.to_json }
726
+ end
727
+
728
+ context 'when dependency meets' do
729
+ it 'sets default value for dependent parameter' do
730
+ get '/', a: 'a'
731
+ expect(last_response.body).to eq({ a: 'a', b: 'default' }.to_json)
732
+ end
733
+ end
734
+
735
+ context 'when dependency does not meet' do
736
+ it 'does not set default value for dependent parameter' do
737
+ get '/', a: 'b'
738
+ expect(last_response.body).to eq({ a: 'b' }.to_json)
739
+ end
740
+ end
741
+ end
742
+
595
743
  context 'when validations are dependent on a parameter within an array param' do
744
+ before do
745
+ subject.params do
746
+ requires :foos, type: Array do
747
+ optional :foo
748
+ given :foo do
749
+ requires :bar
750
+ end
751
+ end
752
+ end
753
+ subject.get('/test') { 'ok' }
754
+ end
755
+
756
+ it 'should pass none Hash params' do
757
+ get '/test', foos: ['']
758
+ expect(last_response.status).to eq(200)
759
+ expect(last_response.body).to eq('ok')
760
+ end
761
+ end
762
+
763
+ context 'when validations are dependent on a parameter within an array param within #declared(params).to_json' do
596
764
  before do
597
765
  subject.params do
598
766
  requires :foos, type: Array do
@@ -948,4 +1116,40 @@ describe Grape::Validations::ParamsScope do
948
1116
  end
949
1117
  end
950
1118
  end
1119
+
1120
+ context 'with exactly_one_of validation for optional parameters within an Hash param' do
1121
+ before do
1122
+ subject.params do
1123
+ optional :memo, type: Hash do
1124
+ optional :text, type: String
1125
+ optional :custom_body, type: Hash, coerce_with: JSON
1126
+ exactly_one_of :text, :custom_body
1127
+ end
1128
+ end
1129
+ subject.get('test')
1130
+ end
1131
+
1132
+ context 'when correct data is provided' do
1133
+ it 'returns a successful response' do
1134
+ get 'test', memo: {}
1135
+ expect(last_response.status).to eq(200)
1136
+
1137
+ get 'test', memo: { text: 'HOGEHOGE' }
1138
+ expect(last_response.status).to eq(200)
1139
+
1140
+ get 'test', memo: { custom_body: '{ "xxx": "yyy" }' }
1141
+ expect(last_response.status).to eq(200)
1142
+ end
1143
+ end
1144
+
1145
+ context 'when invalid data is provided' do
1146
+ it 'returns a failure response' do
1147
+ get 'test', memo: { text: 'HOGEHOGE', custom_body: '{ "xxx": "yyy" }' }
1148
+ expect(last_response.status).to eq(400)
1149
+
1150
+ get 'test', memo: '{ "custom_body": "HOGE" }'
1151
+ expect(last_response.status).to eq(400)
1152
+ end
1153
+ end
1154
+ end
951
1155
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Grape::Validations::SingleAttributeIterator do
6
+ describe '#each' do
7
+ subject(:iterator) { described_class.new(validator, scope, params) }
8
+ let(:scope) { Grape::Validations::ParamsScope.new(api: Class.new(Grape::API)) }
9
+ let(:validator) { double(attrs: %i[first second]) }
10
+
11
+ context 'when params is a hash' do
12
+ let(:params) do
13
+ { first: 'string', second: 'string' }
14
+ end
15
+
16
+ it 'yields params and every single attribute from the list' do
17
+ expect { |b| iterator.each(&b) }
18
+ .to yield_successive_args([params, :first, false, false], [params, :second, false, false])
19
+ end
20
+ end
21
+
22
+ context 'when params is an array' do
23
+ let(:params) do
24
+ [{ first: 'string1', second: 'string1' }, { first: 'string2', second: 'string2' }]
25
+ end
26
+
27
+ it 'yields every single attribute from the list for each of the array elements' do
28
+ expect { |b| iterator.each(&b) }.to yield_successive_args(
29
+ [params[0], :first, false, false], [params[0], :second, false, false],
30
+ [params[1], :first, false, false], [params[1], :second, false, false]
31
+ )
32
+ end
33
+
34
+ context 'empty values' do
35
+ let(:params) { [{}, '', 10] }
36
+
37
+ it 'marks params with empty values' do
38
+ expect { |b| iterator.each(&b) }.to yield_successive_args(
39
+ [params[0], :first, true, false], [params[0], :second, true, false],
40
+ [params[1], :first, true, false], [params[1], :second, true, false],
41
+ [params[2], :first, false, false], [params[2], :second, false, false]
42
+ )
43
+ end
44
+ end
45
+
46
+ context 'when missing optional value' do
47
+ let(:params) { [Grape::DSL::Parameters::EmptyOptionalValue, 10] }
48
+
49
+ it 'marks params with skipped values' do
50
+ expect { |b| iterator.each(&b) }.to yield_successive_args(
51
+ [params[0], :first, false, true], [params[0], :second, false, true],
52
+ [params[1], :first, false, false], [params[1], :second, false, false],
53
+ )
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Grape::Validations::Types::ArrayCoercer do
6
+ subject { described_class.new(type) }
7
+
8
+ describe '#call' do
9
+ context 'an array of primitives' do
10
+ let(:type) { Array[String] }
11
+
12
+ it 'coerces elements in the array' do
13
+ expect(subject.call([10, 20])).to eq(%w[10 20])
14
+ end
15
+ end
16
+
17
+ context 'an array of arrays' do
18
+ let(:type) { Array[Array[Integer]] }
19
+
20
+ it 'coerces elements in the nested array' do
21
+ expect(subject.call([%w[10 20]])).to eq([[10, 20]])
22
+ expect(subject.call([['10'], ['20']])).to eq([[10], [20]])
23
+ end
24
+ end
25
+
26
+ context 'an array of sets' do
27
+ let(:type) { Array[Set[Integer]] }
28
+
29
+ it 'coerces elements in the nested set' do
30
+ expect(subject.call([%w[10 20]])).to eq([Set[10, 20]])
31
+ expect(subject.call([['10'], ['20']])).to eq([Set[10], Set[20]])
32
+ end
33
+ end
34
+ end
35
+ end