grape 1.2.5 → 1.4.0

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 (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -0
  3. data/LICENSE +1 -1
  4. data/README.md +53 -16
  5. data/UPGRADING.md +231 -23
  6. data/grape.gemspec +10 -1
  7. data/lib/grape.rb +6 -7
  8. data/lib/grape/api.rb +4 -2
  9. data/lib/grape/api/helpers.rb +2 -0
  10. data/lib/grape/api/instance.rb +36 -33
  11. data/lib/grape/config.rb +2 -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 +2 -0
  16. data/lib/grape/dsl/configuration.rb +2 -0
  17. data/lib/grape/dsl/desc.rb +2 -0
  18. data/lib/grape/dsl/headers.rb +2 -0
  19. data/lib/grape/dsl/helpers.rb +4 -2
  20. data/lib/grape/dsl/inside_route.rb +83 -34
  21. data/lib/grape/dsl/logger.rb +2 -0
  22. data/lib/grape/dsl/middleware.rb +2 -0
  23. data/lib/grape/dsl/parameters.rb +8 -6
  24. data/lib/grape/dsl/request_response.rb +4 -2
  25. data/lib/grape/dsl/routing.rb +9 -5
  26. data/lib/grape/dsl/settings.rb +7 -1
  27. data/lib/grape/dsl/validations.rb +20 -1
  28. data/lib/grape/eager_load.rb +3 -1
  29. data/lib/grape/endpoint.rb +21 -13
  30. data/lib/grape/error_formatter.rb +3 -1
  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 +11 -13
  36. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  37. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  38. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  39. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  40. data/lib/grape/exceptions/invalid_response.rb +2 -0
  41. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  42. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  43. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  44. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  45. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  46. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  47. data/lib/grape/exceptions/missing_option.rb +2 -0
  48. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  49. data/lib/grape/exceptions/unknown_options.rb +2 -0
  50. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  51. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  52. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  53. data/lib/grape/exceptions/validation.rb +3 -1
  54. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  55. data/lib/grape/exceptions/validation_errors.rb +13 -12
  56. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  57. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  58. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  59. data/lib/grape/extensions/hash.rb +2 -0
  60. data/lib/grape/extensions/hashie/mash.rb +2 -0
  61. data/lib/grape/formatter.rb +5 -3
  62. data/lib/grape/formatter/json.rb +2 -0
  63. data/lib/grape/formatter/serializable_hash.rb +2 -0
  64. data/lib/grape/formatter/txt.rb +2 -0
  65. data/lib/grape/formatter/xml.rb +2 -0
  66. data/lib/grape/http/headers.rb +50 -18
  67. data/lib/grape/middleware/auth/base.rb +2 -0
  68. data/lib/grape/middleware/auth/dsl.rb +2 -0
  69. data/lib/grape/middleware/auth/strategies.rb +2 -0
  70. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  71. data/lib/grape/middleware/base.rb +7 -7
  72. data/lib/grape/middleware/error.rb +3 -1
  73. data/lib/grape/middleware/filter.rb +2 -0
  74. data/lib/grape/middleware/formatter.rb +8 -6
  75. data/lib/grape/middleware/globals.rb +2 -0
  76. data/lib/grape/middleware/helpers.rb +2 -0
  77. data/lib/grape/middleware/stack.rb +4 -1
  78. data/lib/grape/middleware/versioner.rb +2 -0
  79. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  80. data/lib/grape/middleware/versioner/header.rb +6 -4
  81. data/lib/grape/middleware/versioner/param.rb +3 -1
  82. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
  83. data/lib/grape/middleware/versioner/path.rb +3 -1
  84. data/lib/grape/namespace.rb +14 -2
  85. data/lib/grape/parser.rb +3 -1
  86. data/lib/grape/parser/json.rb +2 -0
  87. data/lib/grape/parser/xml.rb +2 -0
  88. data/lib/grape/path.rb +15 -3
  89. data/lib/grape/presenters/presenter.rb +2 -0
  90. data/lib/grape/request.rb +15 -8
  91. data/lib/grape/router.rb +30 -29
  92. data/lib/grape/router/attribute_translator.rb +39 -8
  93. data/lib/grape/router/pattern.rb +20 -16
  94. data/lib/grape/router/route.rb +12 -26
  95. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  96. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  97. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  98. data/lib/grape/util/base_inheritable.rb +15 -6
  99. data/lib/grape/util/cache.rb +20 -0
  100. data/lib/grape/util/endpoint_configuration.rb +2 -0
  101. data/lib/grape/util/env.rb +19 -17
  102. data/lib/grape/util/inheritable_setting.rb +2 -0
  103. data/lib/grape/util/inheritable_values.rb +2 -0
  104. data/lib/grape/util/json.rb +2 -0
  105. data/lib/grape/util/lazy_block.rb +2 -0
  106. data/lib/grape/util/lazy_object.rb +43 -0
  107. data/lib/grape/util/lazy_value.rb +2 -0
  108. data/lib/grape/util/registrable.rb +2 -0
  109. data/lib/grape/util/reverse_stackable_values.rb +4 -0
  110. data/lib/grape/util/stackable_values.rb +10 -20
  111. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  112. data/lib/grape/util/xml.rb +2 -0
  113. data/lib/grape/validations.rb +2 -0
  114. data/lib/grape/validations/attributes_iterator.rb +3 -3
  115. data/lib/grape/validations/multiple_attributes_iterator.rb +2 -0
  116. data/lib/grape/validations/params_scope.rb +27 -14
  117. data/lib/grape/validations/single_attribute_iterator.rb +13 -2
  118. data/lib/grape/validations/types.rb +12 -34
  119. data/lib/grape/validations/types/array_coercer.rb +65 -0
  120. data/lib/grape/validations/types/build_coercer.rb +47 -49
  121. data/lib/grape/validations/types/custom_type_coercer.rb +15 -49
  122. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  123. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  124. data/lib/grape/validations/types/file.rb +22 -18
  125. data/lib/grape/validations/types/json.rb +46 -39
  126. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  127. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  128. data/lib/grape/validations/types/set_coercer.rb +40 -0
  129. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  130. data/lib/grape/validations/validator_factory.rb +2 -0
  131. data/lib/grape/validations/validators/all_or_none.rb +3 -1
  132. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  133. data/lib/grape/validations/validators/as.rb +2 -0
  134. data/lib/grape/validations/validators/at_least_one_of.rb +3 -1
  135. data/lib/grape/validations/validators/base.rb +8 -5
  136. data/lib/grape/validations/validators/coerce.rb +39 -29
  137. data/lib/grape/validations/validators/default.rb +2 -1
  138. data/lib/grape/validations/validators/exactly_one_of.rb +6 -2
  139. data/lib/grape/validations/validators/except_values.rb +3 -1
  140. data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
  141. data/lib/grape/validations/validators/mutual_exclusion.rb +3 -1
  142. data/lib/grape/validations/validators/presence.rb +3 -1
  143. data/lib/grape/validations/validators/regexp.rb +4 -2
  144. data/lib/grape/validations/validators/same_as.rb +6 -3
  145. data/lib/grape/validations/validators/values.rb +17 -5
  146. data/lib/grape/version.rb +3 -1
  147. data/spec/grape/api/custom_validations_spec.rb +5 -3
  148. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  149. data/spec/grape/api/defines_boolean_in_params_spec.rb +5 -3
  150. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  151. data/spec/grape/api/instance_spec.rb +104 -0
  152. data/spec/grape/api/invalid_format_spec.rb +2 -0
  153. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  154. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  155. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  156. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  157. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  158. data/spec/grape/api/recognize_path_spec.rb +2 -0
  159. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  160. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  161. data/spec/grape/api/routes_with_requirements_spec.rb +2 -0
  162. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  163. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  164. data/spec/grape/api_remount_spec.rb +2 -0
  165. data/spec/grape/api_spec.rb +99 -11
  166. data/spec/grape/config_spec.rb +2 -0
  167. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  168. data/spec/grape/dsl/configuration_spec.rb +2 -0
  169. data/spec/grape/dsl/desc_spec.rb +2 -0
  170. data/spec/grape/dsl/headers_spec.rb +2 -0
  171. data/spec/grape/dsl/helpers_spec.rb +4 -2
  172. data/spec/grape/dsl/inside_route_spec.rb +177 -33
  173. data/spec/grape/dsl/logger_spec.rb +2 -0
  174. data/spec/grape/dsl/middleware_spec.rb +2 -0
  175. data/spec/grape/dsl/parameters_spec.rb +2 -0
  176. data/spec/grape/dsl/request_response_spec.rb +2 -0
  177. data/spec/grape/dsl/routing_spec.rb +2 -0
  178. data/spec/grape/dsl/settings_spec.rb +2 -0
  179. data/spec/grape/dsl/validations_spec.rb +2 -0
  180. data/spec/grape/endpoint_spec.rb +21 -6
  181. data/spec/grape/entity_spec.rb +2 -0
  182. data/spec/grape/exceptions/base_spec.rb +3 -1
  183. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  184. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  185. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  186. data/spec/grape/exceptions/invalid_response_spec.rb +2 -0
  187. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  188. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  189. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  190. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  191. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  192. data/spec/grape/exceptions/validation_errors_spec.rb +4 -2
  193. data/spec/grape/exceptions/validation_spec.rb +3 -1
  194. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  195. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  196. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  197. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  198. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  199. data/spec/grape/integration/rack_spec.rb +3 -1
  200. data/spec/grape/loading_spec.rb +2 -0
  201. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  202. data/spec/grape/middleware/auth/dsl_spec.rb +2 -0
  203. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  204. data/spec/grape/middleware/base_spec.rb +2 -0
  205. data/spec/grape/middleware/error_spec.rb +2 -0
  206. data/spec/grape/middleware/exception_spec.rb +3 -1
  207. data/spec/grape/middleware/formatter_spec.rb +19 -12
  208. data/spec/grape/middleware/globals_spec.rb +2 -0
  209. data/spec/grape/middleware/stack_spec.rb +11 -0
  210. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  211. data/spec/grape/middleware/versioner/header_spec.rb +3 -1
  212. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  213. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  214. data/spec/grape/middleware/versioner_spec.rb +2 -0
  215. data/spec/grape/named_api_spec.rb +2 -0
  216. data/spec/grape/parser_spec.rb +7 -5
  217. data/spec/grape/path_spec.rb +6 -4
  218. data/spec/grape/presenters/presenter_spec.rb +2 -0
  219. data/spec/grape/request_spec.rb +2 -0
  220. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  221. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  222. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  223. data/spec/grape/util/stackable_values_spec.rb +3 -1
  224. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  225. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  226. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  227. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +2 -0
  228. data/spec/grape/validations/params_scope_spec.rb +3 -1
  229. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -4
  230. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  231. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  232. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  233. data/spec/grape/validations/types_spec.rb +9 -36
  234. data/spec/grape/validations/validators/all_or_none_spec.rb +2 -0
  235. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  236. data/spec/grape/validations/validators/at_least_one_of_spec.rb +2 -0
  237. data/spec/grape/validations/validators/coerce_spec.rb +341 -136
  238. data/spec/grape/validations/validators/default_spec.rb +123 -0
  239. data/spec/grape/validations/validators/exactly_one_of_spec.rb +14 -12
  240. data/spec/grape/validations/validators/except_values_spec.rb +3 -1
  241. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +2 -0
  242. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  243. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  244. data/spec/grape/validations/validators/same_as_spec.rb +2 -0
  245. data/spec/grape/validations/validators/values_spec.rb +30 -5
  246. data/spec/grape/validations_spec.rb +91 -33
  247. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  248. data/spec/integration/multi_json/json_spec.rb +2 -0
  249. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  250. data/spec/shared/versioning_examples.rb +2 -0
  251. data/spec/spec_helper.rb +18 -0
  252. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  253. data/spec/support/content_type_helpers.rb +2 -0
  254. data/spec/support/eager_load.rb +19 -0
  255. data/spec/support/endpoint_faker.rb +2 -0
  256. data/spec/support/file_streamer.rb +2 -0
  257. data/spec/support/integer_helpers.rb +2 -0
  258. data/spec/support/versioned_helpers.rb +4 -2
  259. metadata +48 -28
  260. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  261. data/lib/grape/util/content_types.rb +0 -26
  262. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dry_type_coercer'
4
+
5
+ module Grape
6
+ module Validations
7
+ module Types
8
+ # Coerces the given value to a type defined via a +type+ argument during
9
+ # initialization. When +strict+ is true, it doesn't coerce a value but check
10
+ # that it has the proper type.
11
+ class PrimitiveCoercer < DryTypeCoercer
12
+ MAPPING = {
13
+ Grape::API::Boolean => DryTypes::Params::Bool,
14
+ BigDecimal => DryTypes::Params::Decimal,
15
+
16
+ # unfortunately, a +Params+ scope doesn't contain String
17
+ String => DryTypes::Coercible::String
18
+ }.freeze
19
+
20
+ STRICT_MAPPING = {
21
+ Grape::API::Boolean => DryTypes::Strict::Bool,
22
+ BigDecimal => DryTypes::Strict::Decimal
23
+ }.freeze
24
+
25
+ def initialize(type, strict = false)
26
+ super
27
+
28
+ @type = type
29
+
30
+ @coercer = if strict
31
+ STRICT_MAPPING.fetch(type) { scope.const_get(type.name) }
32
+ else
33
+ MAPPING.fetch(type) { scope.const_get(type.name) }
34
+ end
35
+ end
36
+
37
+ def call(val)
38
+ return InvalidValue.new if reject?(val)
39
+ return nil if val.nil? || treat_as_nil?(val)
40
+
41
+ super
42
+ end
43
+
44
+ protected
45
+
46
+ attr_reader :type
47
+
48
+ # This method maintains logic which was defined by Virtus. For example,
49
+ # dry-types is ok to convert an array or a hash to a string, it is supported,
50
+ # but Virtus wouldn't accept it. So, this method only exists to not introduce
51
+ # breaking changes.
52
+ def reject?(val)
53
+ (val.is_a?(Array) && type == String) ||
54
+ (val.is_a?(String) && type == Hash) ||
55
+ (val.is_a?(Hash) && type == String)
56
+ end
57
+
58
+ # Dry-Types treats an empty string as invalid. However, Grape considers an empty string as
59
+ # absence of a value and coerces it into nil. See a discussion there
60
+ # https://github.com/ruby-grape/grape/pull/2045
61
+ def treat_as_nil?(val)
62
+ val == '' && type != String
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require_relative 'array_coercer'
5
+
6
+ module Grape
7
+ module Validations
8
+ module Types
9
+ # Takes the given array and converts it to a set. Every element of the set
10
+ # is also coerced.
11
+ class SetCoercer < ArrayCoercer
12
+ register_collection Set
13
+
14
+ def initialize(type, strict = false)
15
+ super
16
+
17
+ @coercer = nil
18
+ end
19
+
20
+ def call(value)
21
+ return InvalidValue.new unless value.is_a?(Array)
22
+
23
+ coerce_elements(value)
24
+ end
25
+
26
+ protected
27
+
28
+ def coerce_elements(collection)
29
+ collection.each_with_object(Set.new) do |elem, memo|
30
+ coerced_elem = elem_coercer.call(elem)
31
+
32
+ return coerced_elem if coerced_elem.is_a?(InvalidValue)
33
+
34
+ memo.add(coerced_elem)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  module Types
4
6
  # This class wraps {MultipleTypeCoercer}, for use with collections
5
7
  # that allow members of more than one type.
6
- class VariantCollectionCoercer < Virtus::Attribute
8
+ class VariantCollectionCoercer
7
9
  # Construct a new coercer that will attempt to coerce
8
10
  # a list of values such that all members are of one of
9
11
  # the given types. The container may also optionally be
@@ -30,8 +32,8 @@ module Grape
30
32
  # @return [Array<Object>,Set<Object>,InvalidValue]
31
33
  # the coerced result, or an instance
32
34
  # of {InvalidValue} if the value could not be coerced.
33
- def coerce(value)
34
- return InvalidValue.new unless value.is_a? Array
35
+ def call(value)
36
+ return unless value.is_a? Array
35
37
 
36
38
  value =
37
39
  if @method
@@ -43,16 +45,6 @@ module Grape
43
45
 
44
46
  value
45
47
  end
46
-
47
- # Assert that the value has been coerced successfully.
48
- #
49
- # @param value [Object] a coerced result returned from {#coerce}
50
- # @return [true,false] whether or not the coerced value
51
- # satisfies type requirements.
52
- def value_coerced?(value)
53
- value.is_a?(@types.class) &&
54
- value.all? { |v| @member_coercer.success?(@types, v) }
55
- end
56
48
  end
57
49
  end
58
50
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class ValidatorFactory
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/validations/validators/multiple_params_base'
2
4
 
3
5
  module Grape
@@ -6,7 +8,7 @@ module Grape
6
8
  def validate_params!(params)
7
9
  keys = keys_in_common(params)
8
10
  return if keys.empty? || keys.length == all_keys.length
9
- raise Grape::Exceptions::Validation, params: all_keys, message: message(:all_or_none)
11
+ raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:all_or_none))
10
12
  end
11
13
  end
12
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class AllowBlankValidator < Base
@@ -9,7 +11,7 @@ module Grape
9
11
 
10
12
  return if value == false || value.present?
11
13
 
12
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:blank)
14
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:blank))
13
15
  end
14
16
  end
15
17
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class AsValidator < Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/validations/validators/multiple_params_base'
2
4
 
3
5
  module Grape
@@ -5,7 +7,7 @@ module Grape
5
7
  class AtLeastOneOfValidator < MultipleParamsBase
6
8
  def validate_params!(params)
7
9
  return unless keys_in_common(params).empty?
8
- raise Grape::Exceptions::Validation, params: all_keys, message: message(:at_least_one)
10
+ raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:at_least_one))
9
11
  end
10
12
  end
11
13
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class Base
@@ -17,6 +19,7 @@ module Grape
17
19
  @required = required
18
20
  @scope = scope
19
21
  @fail_fast = opts[:fail_fast] || false
22
+ @allow_blank = opts[:allow_blank] || false
20
23
  end
21
24
 
22
25
  # Validates a given request.
@@ -40,12 +43,12 @@ module Grape
40
43
  # there may be more than one error per field
41
44
  array_errors = []
42
45
 
43
- attributes.each do |resource_params, attr_name|
44
- next if !@scope.required? && resource_params.empty?
45
- next unless @scope.meets_dependency?(resource_params, params)
46
+ attributes.each do |val, attr_name, empty_val|
47
+ next if !@scope.required? && empty_val
48
+ next unless @scope.meets_dependency?(val, params)
46
49
  begin
47
- if @required || resource_params.respond_to?(:key?) && resource_params.key?(attr_name)
48
- validate_param!(attr_name, resource_params)
50
+ if @required || val.respond_to?(:key?) && val.key?(attr_name)
51
+ validate_param!(attr_name, val)
49
52
  end
50
53
  rescue Grape::Exceptions::Validation => e
51
54
  array_errors << e
@@ -1,9 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  class API
3
- Boolean = Virtus::Attribute::Boolean
5
+ class Boolean
6
+ def self.build(val)
7
+ return nil if val != true && val != false
8
+
9
+ new
10
+ end
11
+ end
4
12
 
5
13
  class Instance
6
- Boolean = Virtus::Attribute::Boolean
14
+ Boolean = Grape::API::Boolean
7
15
  end
8
16
  end
9
17
 
@@ -11,7 +19,12 @@ module Grape
11
19
  class CoerceValidator < Base
12
20
  def initialize(*_args)
13
21
  super
14
- @converter = Types.build_coercer(type, @option[:method])
22
+
23
+ @converter = if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
24
+ type
25
+ else
26
+ Types.build_coercer(type, method: @option[:method])
27
+ end
15
28
  end
16
29
 
17
30
  def validate(request)
@@ -19,10 +32,23 @@ module Grape
19
32
  end
20
33
 
21
34
  def validate_param!(attr_name, params)
22
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless params.is_a? Hash
23
- return unless requires_coercion?(params[attr_name])
35
+ raise validation_exception(attr_name) unless params.is_a? Hash
36
+
24
37
  new_value = coerce_value(params[attr_name])
25
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless valid_type?(new_value)
38
+
39
+ raise validation_exception(attr_name) unless valid_type?(new_value)
40
+
41
+ # Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
42
+ # which looses wrappers for hashes and arrays after reassigning values
43
+ #
44
+ # h = Hashie::Mash.new(list: [1, 2, 3, 4])
45
+ # => #<Hashie::Mash list=#<Hashie::Array [1, 2, 3, 4]>>
46
+ # list = h.list
47
+ # h[:list] = list
48
+ # h
49
+ # => #<Hashie::Mash list=[1, 2, 3, 4]>
50
+ return if params[attr_name].class == new_value.class && params[attr_name] == new_value
51
+
26
52
  params[attr_name] = new_value
27
53
  end
28
54
 
@@ -33,32 +59,17 @@ module Grape
33
59
  #
34
60
  # See {Types.build_coercer}
35
61
  #
36
- # @return [Virtus::Attribute]
62
+ # @return [Object]
37
63
  attr_reader :converter
38
64
 
39
65
  def valid_type?(val)
40
- # Special value to denote coercion failure
41
- return false if val.instance_of?(Types::InvalidValue)
42
-
43
- # Allow nil, to ignore when a parameter is absent
44
- return true if val.nil?
45
-
46
- converter.value_coerced? val
66
+ !val.is_a?(Types::InvalidValue)
47
67
  end
48
68
 
49
69
  def coerce_value(val)
50
- # Don't coerce things other than nil to Arrays or Hashes
51
- unless (@option[:method] && !val.nil?) || type.is_a?(Virtus::Attribute)
52
- return val || [] if type == Array
53
- return val || Set.new if type == Set
54
- return val || {} if type == Hash
55
- end
56
-
57
- converter.coerce(val)
58
-
59
- # not the prettiest but some invalid coercion can currently trigger
60
- # errors in Virtus (see coerce_spec.rb:75)
61
- rescue
70
+ converter.call(val)
71
+ # Some custom types might fail, so it should be treated as an invalid value
72
+ rescue StandardError
62
73
  Types::InvalidValue.new
63
74
  end
64
75
 
@@ -69,9 +80,8 @@ module Grape
69
80
  @option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
70
81
  end
71
82
 
72
- def requires_coercion?(value)
73
- # JSON types do not require coercion if value is valid
74
- !valid_type?(value) || converter.coercer.respond_to?(:method) && !converter.is_a?(Grape::Validations::Types::Json)
83
+ def validation_exception(attr_name)
84
+ Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:coerce))
75
85
  end
76
86
  end
77
87
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class DefaultValidator < Base
@@ -7,7 +9,6 @@ module Grape
7
9
  end
8
10
 
9
11
  def validate_param!(attr_name, params)
10
- return if params.key? attr_name
11
12
  params[attr_name] = if @default.is_a? Proc
12
13
  @default.call
13
14
  elsif @default.frozen? || !duplicatable?(@default)
@@ -1,11 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/validations/validators/multiple_params_base'
2
4
 
3
5
  module Grape
4
6
  module Validations
5
7
  class ExactlyOneOfValidator < MultipleParamsBase
6
8
  def validate_params!(params)
7
- return if keys_in_common(params).length == 1
8
- raise Grape::Exceptions::Validation, params: all_keys, message: message(:exactly_one)
9
+ keys = keys_in_common(params)
10
+ return if keys.length == 1
11
+ raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.length.zero?
12
+ raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
9
13
  end
10
14
  end
11
15
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class ExceptValuesValidator < Base
@@ -13,7 +15,7 @@ module Grape
13
15
  return if excepts.nil?
14
16
 
15
17
  param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
16
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:except_values) if param_array.any? { |param| excepts.include?(param) }
18
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:except_values)) if param_array.any? { |param| excepts.include?(param) }
17
19
  end
18
20
  end
19
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class MultipleParamsBase < Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/validations/validators/multiple_params_base'
2
4
 
3
5
  module Grape
@@ -6,7 +8,7 @@ module Grape
6
8
  def validate_params!(params)
7
9
  keys = keys_in_common(params)
8
10
  return if keys.length <= 1
9
- raise Grape::Exceptions::Validation, params: keys, message: message(:mutual_exclusion)
11
+ raise Grape::Exceptions::Validation.new(params: keys, message: message(:mutual_exclusion))
10
12
  end
11
13
  end
12
14
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class PresenceValidator < Base
4
6
  def validate_param!(attr_name, params)
5
7
  return if params.respond_to?(:key?) && params.key?(attr_name)
6
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:presence)
8
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
7
9
  end
8
10
  end
9
11
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class RegexpValidator < Base
4
6
  def validate_param!(attr_name, params)
5
7
  return unless params.respond_to?(:key?) && params.key?(attr_name)
6
- return if Array.wrap(params[attr_name]).all? { |param| param.nil? || (param.to_s =~ (options_key?(:value) ? @option[:value] : @option)) }
7
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:regexp)
8
+ return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.match?((options_key?(:value) ? @option[:value] : @option)) }
9
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
8
10
  end
9
11
  end
10
12
  end