grape 1.2.5 → 1.3.1

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 (258) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +7 -6
  4. data/UPGRADING.md +43 -0
  5. data/grape.gemspec +10 -1
  6. data/lib/grape/api/helpers.rb +2 -0
  7. data/lib/grape/api/instance.rb +8 -6
  8. data/lib/grape/api.rb +4 -2
  9. data/lib/grape/config.rb +2 -0
  10. data/lib/grape/content_types.rb +34 -0
  11. data/lib/grape/cookies.rb +2 -0
  12. data/lib/grape/dsl/api.rb +2 -0
  13. data/lib/grape/dsl/callbacks.rb +2 -0
  14. data/lib/grape/dsl/configuration.rb +2 -0
  15. data/lib/grape/dsl/desc.rb +2 -0
  16. data/lib/grape/dsl/headers.rb +2 -0
  17. data/lib/grape/dsl/helpers.rb +4 -2
  18. data/lib/grape/dsl/inside_route.rb +15 -11
  19. data/lib/grape/dsl/logger.rb +2 -0
  20. data/lib/grape/dsl/middleware.rb +2 -0
  21. data/lib/grape/dsl/parameters.rb +8 -6
  22. data/lib/grape/dsl/request_response.rb +4 -2
  23. data/lib/grape/dsl/routing.rb +9 -5
  24. data/lib/grape/dsl/settings.rb +7 -1
  25. data/lib/grape/dsl/validations.rb +2 -0
  26. data/lib/grape/eager_load.rb +2 -0
  27. data/lib/grape/endpoint.rb +13 -7
  28. data/lib/grape/error_formatter/base.rb +2 -0
  29. data/lib/grape/error_formatter/json.rb +2 -0
  30. data/lib/grape/error_formatter/txt.rb +2 -0
  31. data/lib/grape/error_formatter/xml.rb +2 -0
  32. data/lib/grape/error_formatter.rb +3 -1
  33. data/lib/grape/exceptions/base.rb +11 -13
  34. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  35. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  36. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  37. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  38. data/lib/grape/exceptions/invalid_response.rb +2 -0
  39. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  40. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  41. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  42. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  43. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  44. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  45. data/lib/grape/exceptions/missing_option.rb +2 -0
  46. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  47. data/lib/grape/exceptions/unknown_options.rb +2 -0
  48. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  49. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  50. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  51. data/lib/grape/exceptions/validation.rb +3 -1
  52. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  53. data/lib/grape/exceptions/validation_errors.rb +13 -12
  54. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  55. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  56. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  57. data/lib/grape/extensions/hash.rb +2 -0
  58. data/lib/grape/extensions/hashie/mash.rb +2 -0
  59. data/lib/grape/formatter/json.rb +2 -0
  60. data/lib/grape/formatter/serializable_hash.rb +2 -0
  61. data/lib/grape/formatter/txt.rb +2 -0
  62. data/lib/grape/formatter/xml.rb +2 -0
  63. data/lib/grape/formatter.rb +5 -3
  64. data/lib/grape/http/headers.rb +49 -18
  65. data/lib/grape/middleware/auth/base.rb +2 -0
  66. data/lib/grape/middleware/auth/dsl.rb +2 -0
  67. data/lib/grape/middleware/auth/strategies.rb +2 -0
  68. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  69. data/lib/grape/middleware/base.rb +5 -5
  70. data/lib/grape/middleware/error.rb +3 -1
  71. data/lib/grape/middleware/filter.rb +2 -0
  72. data/lib/grape/middleware/formatter.rb +5 -3
  73. data/lib/grape/middleware/globals.rb +2 -0
  74. data/lib/grape/middleware/helpers.rb +2 -0
  75. data/lib/grape/middleware/stack.rb +4 -1
  76. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  77. data/lib/grape/middleware/versioner/header.rb +5 -3
  78. data/lib/grape/middleware/versioner/param.rb +3 -1
  79. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -0
  80. data/lib/grape/middleware/versioner/path.rb +3 -1
  81. data/lib/grape/middleware/versioner.rb +2 -0
  82. data/lib/grape/namespace.rb +14 -2
  83. data/lib/grape/parser/json.rb +2 -0
  84. data/lib/grape/parser/xml.rb +2 -0
  85. data/lib/grape/parser.rb +3 -1
  86. data/lib/grape/path.rb +13 -1
  87. data/lib/grape/presenters/presenter.rb +2 -0
  88. data/lib/grape/request.rb +15 -8
  89. data/lib/grape/router/attribute_translator.rb +18 -8
  90. data/lib/grape/router/pattern.rb +20 -16
  91. data/lib/grape/router/route.rb +9 -4
  92. data/lib/grape/router.rb +26 -12
  93. data/lib/grape/serve_file/file_body.rb +2 -0
  94. data/lib/grape/serve_file/file_response.rb +2 -0
  95. data/lib/grape/serve_file/sendfile_response.rb +2 -0
  96. data/lib/grape/util/base_inheritable.rb +6 -0
  97. data/lib/grape/util/cache.rb +20 -0
  98. data/lib/grape/util/endpoint_configuration.rb +2 -0
  99. data/lib/grape/util/env.rb +19 -17
  100. data/lib/grape/util/inheritable_setting.rb +2 -0
  101. data/lib/grape/util/inheritable_values.rb +2 -0
  102. data/lib/grape/util/json.rb +2 -0
  103. data/lib/grape/util/lazy_block.rb +2 -0
  104. data/lib/grape/util/lazy_object.rb +43 -0
  105. data/lib/grape/util/lazy_value.rb +2 -0
  106. data/lib/grape/util/registrable.rb +2 -0
  107. data/lib/grape/util/reverse_stackable_values.rb +3 -1
  108. data/lib/grape/util/stackable_values.rb +9 -21
  109. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  110. data/lib/grape/util/xml.rb +2 -0
  111. data/lib/grape/validations/attributes_iterator.rb +3 -3
  112. data/lib/grape/validations/multiple_attributes_iterator.rb +2 -0
  113. data/lib/grape/validations/params_scope.rb +24 -11
  114. data/lib/grape/validations/single_attribute_iterator.rb +13 -2
  115. data/lib/grape/validations/types/array_coercer.rb +56 -0
  116. data/lib/grape/validations/types/build_coercer.rb +49 -48
  117. data/lib/grape/validations/types/custom_type_coercer.rb +15 -49
  118. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  119. data/lib/grape/validations/types/dry_type_coercer.rb +41 -0
  120. data/lib/grape/validations/types/file.rb +11 -9
  121. data/lib/grape/validations/types/json.rb +11 -8
  122. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  123. data/lib/grape/validations/types/primitive_coercer.rb +61 -0
  124. data/lib/grape/validations/types/set_coercer.rb +38 -0
  125. data/lib/grape/validations/types/variant_collection_coercer.rb +4 -12
  126. data/lib/grape/validations/types.rb +7 -30
  127. data/lib/grape/validations/validator_factory.rb +2 -0
  128. data/lib/grape/validations/validators/all_or_none.rb +3 -1
  129. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  130. data/lib/grape/validations/validators/as.rb +2 -0
  131. data/lib/grape/validations/validators/at_least_one_of.rb +3 -1
  132. data/lib/grape/validations/validators/base.rb +8 -5
  133. data/lib/grape/validations/validators/coerce.rb +44 -27
  134. data/lib/grape/validations/validators/default.rb +2 -0
  135. data/lib/grape/validations/validators/exactly_one_of.rb +6 -2
  136. data/lib/grape/validations/validators/except_values.rb +3 -1
  137. data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
  138. data/lib/grape/validations/validators/mutual_exclusion.rb +3 -1
  139. data/lib/grape/validations/validators/presence.rb +3 -1
  140. data/lib/grape/validations/validators/regexp.rb +3 -1
  141. data/lib/grape/validations/validators/same_as.rb +6 -3
  142. data/lib/grape/validations/validators/values.rb +17 -5
  143. data/lib/grape/validations.rb +2 -0
  144. data/lib/grape/version.rb +3 -1
  145. data/lib/grape.rb +4 -5
  146. data/spec/grape/api/custom_validations_spec.rb +5 -3
  147. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  148. data/spec/grape/api/defines_boolean_in_params_spec.rb +5 -3
  149. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  150. data/spec/grape/api/instance_spec.rb +54 -0
  151. data/spec/grape/api/invalid_format_spec.rb +2 -0
  152. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  153. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  154. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  155. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  156. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  157. data/spec/grape/api/recognize_path_spec.rb +2 -0
  158. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  160. data/spec/grape/api/routes_with_requirements_spec.rb +2 -0
  161. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  162. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  163. data/spec/grape/api_remount_spec.rb +2 -0
  164. data/spec/grape/api_spec.rb +34 -11
  165. data/spec/grape/config_spec.rb +2 -0
  166. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  167. data/spec/grape/dsl/configuration_spec.rb +2 -0
  168. data/spec/grape/dsl/desc_spec.rb +2 -0
  169. data/spec/grape/dsl/headers_spec.rb +2 -0
  170. data/spec/grape/dsl/helpers_spec.rb +4 -2
  171. data/spec/grape/dsl/inside_route_spec.rb +2 -0
  172. data/spec/grape/dsl/logger_spec.rb +2 -0
  173. data/spec/grape/dsl/middleware_spec.rb +2 -0
  174. data/spec/grape/dsl/parameters_spec.rb +2 -0
  175. data/spec/grape/dsl/request_response_spec.rb +2 -0
  176. data/spec/grape/dsl/routing_spec.rb +2 -0
  177. data/spec/grape/dsl/settings_spec.rb +2 -0
  178. data/spec/grape/dsl/validations_spec.rb +2 -0
  179. data/spec/grape/endpoint_spec.rb +3 -1
  180. data/spec/grape/entity_spec.rb +2 -0
  181. data/spec/grape/exceptions/base_spec.rb +3 -1
  182. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  183. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  184. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  185. data/spec/grape/exceptions/invalid_response_spec.rb +2 -0
  186. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  187. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  188. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  189. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  190. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  191. data/spec/grape/exceptions/validation_errors_spec.rb +4 -2
  192. data/spec/grape/exceptions/validation_spec.rb +3 -1
  193. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  194. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  195. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  196. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  197. data/spec/grape/integration/rack_sendfile_spec.rb +2 -0
  198. data/spec/grape/integration/rack_spec.rb +3 -1
  199. data/spec/grape/loading_spec.rb +2 -0
  200. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  201. data/spec/grape/middleware/auth/dsl_spec.rb +2 -0
  202. data/spec/grape/middleware/auth/strategies_spec.rb +2 -0
  203. data/spec/grape/middleware/base_spec.rb +2 -0
  204. data/spec/grape/middleware/error_spec.rb +2 -0
  205. data/spec/grape/middleware/exception_spec.rb +3 -1
  206. data/spec/grape/middleware/formatter_spec.rb +19 -12
  207. data/spec/grape/middleware/globals_spec.rb +2 -0
  208. data/spec/grape/middleware/stack_spec.rb +11 -0
  209. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  210. data/spec/grape/middleware/versioner/header_spec.rb +3 -1
  211. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  212. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  213. data/spec/grape/middleware/versioner_spec.rb +2 -0
  214. data/spec/grape/named_api_spec.rb +2 -0
  215. data/spec/grape/parser_spec.rb +7 -5
  216. data/spec/grape/path_spec.rb +2 -0
  217. data/spec/grape/presenters/presenter_spec.rb +2 -0
  218. data/spec/grape/request_spec.rb +2 -0
  219. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  220. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  221. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  222. data/spec/grape/util/stackable_values_spec.rb +3 -1
  223. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  224. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  225. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  226. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +2 -0
  227. data/spec/grape/validations/params_scope_spec.rb +3 -1
  228. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -4
  229. data/spec/grape/validations/types/primitive_coercer_spec.rb +75 -0
  230. data/spec/grape/validations/types_spec.rb +8 -35
  231. data/spec/grape/validations/validators/all_or_none_spec.rb +2 -0
  232. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  233. data/spec/grape/validations/validators/at_least_one_of_spec.rb +2 -0
  234. data/spec/grape/validations/validators/coerce_spec.rb +51 -110
  235. data/spec/grape/validations/validators/default_spec.rb +2 -0
  236. data/spec/grape/validations/validators/exactly_one_of_spec.rb +14 -12
  237. data/spec/grape/validations/validators/except_values_spec.rb +3 -1
  238. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +2 -0
  239. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  240. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  241. data/spec/grape/validations/validators/same_as_spec.rb +2 -0
  242. data/spec/grape/validations/validators/values_spec.rb +29 -4
  243. data/spec/grape/validations_spec.rb +69 -15
  244. data/spec/integration/multi_json/json_spec.rb +2 -0
  245. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  246. data/spec/shared/versioning_examples.rb +2 -0
  247. data/spec/spec_helper.rb +18 -0
  248. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  249. data/spec/support/content_type_helpers.rb +2 -0
  250. data/spec/support/eager_load.rb +19 -0
  251. data/spec/support/endpoint_faker.rb +2 -0
  252. data/spec/support/file_streamer.rb +2 -0
  253. data/spec/support/integer_helpers.rb +2 -0
  254. data/spec/support/versioned_helpers.rb +4 -2
  255. metadata +126 -112
  256. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  257. data/lib/grape/util/content_types.rb +0 -26
  258. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
@@ -1,21 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
 
3
5
  module Grape
4
6
  module Validations
5
7
  module Types
6
- # +Virtus::Attribute+ implementation that handles coercion
7
- # and type checking for parameters that are complex types
8
- # given as JSON-encoded strings. It accepts both JSON objects
8
+ # Handles coercion and type checking for parameters that are complex
9
+ # types given as JSON-encoded strings. It accepts both JSON objects
9
10
  # and arrays of objects, and will coerce the input to a +Hash+
10
11
  # or +Array+ object respectively. In either case the Grape
11
12
  # validation system will apply nested validation rules to
12
13
  # all returned objects.
13
- class Json < Virtus::Attribute
14
+ class Json
14
15
  # Coerce the input into a JSON-like data structure.
15
16
  #
16
17
  # @param input [String] a JSON-encoded parameter value
17
18
  # @return [Hash,Array<Hash>,nil]
18
- def coerce(input)
19
+ def call(input)
20
+ return input if coerced?(input)
21
+
19
22
  # Allow nulls and blank strings
20
23
  return if input.nil? || input =~ /^\s*$/
21
24
  JSON.parse(input, symbolize_names: true)
@@ -26,7 +29,7 @@ module Grape
26
29
  #
27
30
  # @param value [Object] result of {#coerce}
28
31
  # @return [true,false]
29
- def value_coerced?(value)
32
+ def coerced?(value)
30
33
  value.is_a?(::Hash) || coerced_collection?(value)
31
34
  end
32
35
 
@@ -50,13 +53,13 @@ module Grape
50
53
  #
51
54
  # @param input [String] JSON-encoded parameter value
52
55
  # @return [Array<Hash>]
53
- def coerce(input)
56
+ def call(input)
54
57
  json = super
55
58
  Array.wrap(json) unless json.nil?
56
59
  end
57
60
 
58
61
  # See {Json#coerced_collection?}
59
- def value_coerced?(value)
62
+ def coerced?(value)
60
63
  coerced_collection? value
61
64
  end
62
65
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  module Types
@@ -22,53 +24,32 @@ module Grape
22
24
 
23
25
  @type_coercers = types.map do |type|
24
26
  if Types.multiple? type
25
- VariantCollectionCoercer.new type
27
+ VariantCollectionCoercer.new type, @method
26
28
  else
27
- Types.build_coercer type
29
+ Types.build_coercer type, strict: !@method.nil?
28
30
  end
29
31
  end
30
32
  end
31
33
 
32
- # This method is called from somewhere within
33
- # +Virtus::Attribute::coerce+ in order to coerce
34
- # the given value.
34
+ # Coerces the given value.
35
35
  #
36
- # @param value [String] value to be coerced, in grape
36
+ # @param val [String] value to be coerced, in grape
37
37
  # this should always be a string.
38
38
  # @return [Object,InvalidValue] the coerced result, or an instance
39
39
  # of {InvalidValue} if the value could not be coerced.
40
- def call(value)
41
- return @method.call(value) if @method
40
+ def call(val)
41
+ # once the value is coerced by the custom method, its type should be checked
42
+ val = @method.call(val) if @method
43
+
44
+ coerced_val = InvalidValue.new
42
45
 
43
46
  @type_coercers.each do |coercer|
44
- coerced = coercer.coerce(value)
47
+ coerced_val = coercer.call(val)
45
48
 
46
- return coerced if coercer.value_coerced? coerced
49
+ return coerced_val unless coerced_val.is_a?(InvalidValue)
47
50
  end
48
51
 
49
- # Declare that we couldn't coerce the value in such a way
50
- # that Grape won't ask us again if the value is valid
51
- InvalidValue.new
52
- end
53
-
54
- # This method is called from somewhere within
55
- # +Virtus::Attribute::value_coerced?+ in order to
56
- # assert that the value has been coerced successfully.
57
- # Due to Grape's design this will in fact only be called
58
- # if a custom coercion method is being used, since {#call}
59
- # returns an {InvalidValue} object if the value could not
60
- # be coerced.
61
- #
62
- # @param _primitive [Axiom::Types::Type] primitive type
63
- # for the coercion as detected by axiom-types' inference
64
- # system. For custom types this is typically not much use
65
- # (i.e. it is +Axiom::Types::Object+) unless special
66
- # inference rules have been declared for the type.
67
- # @param value [Object] a coerced result returned from {#call}
68
- # @return [true,false] whether or not the coerced value
69
- # satisfies type requirements.
70
- def success?(_primitive, value)
71
- @type_coercers.any? { |coercer| coercer.value_coerced? value }
52
+ coerced_val
72
53
  end
73
54
  end
74
55
  end
@@ -0,0 +1,61 @@
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
+
15
+ # unfortunately, a +Params+ scope doesn't contain String
16
+ String => DryTypes::Coercible::String,
17
+ BigDecimal => DryTypes::Coercible::Decimal
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?
40
+ return '' if val == ''
41
+
42
+ super
43
+ end
44
+
45
+ protected
46
+
47
+ attr_reader :type
48
+
49
+ # This method maintaine logic which was defined by Virtus. For example,
50
+ # dry-types is ok to convert an array or a hash to a string, it is supported,
51
+ # but Virtus wouldn't accept it. So, this method only exists to not introduce
52
+ # breaking changes.
53
+ def reject?(val)
54
+ (val.is_a?(Array) && type == String) ||
55
+ (val.is_a?(String) && type == Hash) ||
56
+ (val.is_a?(Hash) && type == String)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require_relative 'dry_type_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 < DryTypeCoercer
12
+ def initialize(type, strict = false)
13
+ super
14
+
15
+ @elem_coercer = PrimitiveCoercer.new(type.first, strict)
16
+ end
17
+
18
+ def call(value)
19
+ return InvalidValue.new unless value.is_a?(Array)
20
+
21
+ coerce_elements(value)
22
+ end
23
+
24
+ protected
25
+
26
+ def coerce_elements(collection)
27
+ collection.each_with_object(Set.new) do |elem, memo|
28
+ coerced_elem = @elem_coercer.call(elem)
29
+
30
+ return coerced_elem if coerced_elem.is_a?(InvalidValue)
31
+
32
+ memo.add(coerced_elem)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ 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,7 +32,7 @@ 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)
35
+ def call(value)
34
36
  return InvalidValue.new unless value.is_a? Array
35
37
 
36
38
  value =
@@ -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
  require_relative 'types/build_coercer'
2
4
  require_relative 'types/custom_type_coercer'
3
5
  require_relative 'types/custom_type_collection_coercer'
@@ -6,10 +8,6 @@ require_relative 'types/variant_collection_coercer'
6
8
  require_relative 'types/json'
7
9
  require_relative 'types/file'
8
10
 
9
- # Patch for Virtus::Attribute::Collection
10
- # See the file for more details
11
- require_relative 'types/virtus_collection_patch'
12
-
13
11
  module Grape
14
12
  module Validations
15
13
  # Module for code related to grape's system for
@@ -27,8 +25,7 @@ module Grape
27
25
  # a parameter value could not be coerced.
28
26
  class InvalidValue; end
29
27
 
30
- # Types representing a single value, which are coerced through Virtus
31
- # or special logic in Grape.
28
+ # Types representing a single value, which are coerced.
32
29
  PRIMITIVES = [
33
30
  # Numerical
34
31
  Integer,
@@ -42,10 +39,12 @@ module Grape
42
39
  Time,
43
40
 
44
41
  # Misc
45
- Virtus::Attribute::Boolean,
42
+ Grape::API::Boolean,
46
43
  String,
47
44
  Symbol,
48
- Rack::Multipart::UploadedFile
45
+ Rack::Multipart::UploadedFile,
46
+ TrueClass,
47
+ FalseClass
49
48
  ].freeze
50
49
 
51
50
  # Types representing data structures.
@@ -86,8 +85,6 @@ module Grape
86
85
  # @param type [Class] type to check
87
86
  # @return [Boolean] whether or not the type is known by Grape as a valid
88
87
  # data structure type
89
- # @note This method does not yet consider 'complex types', which inherit
90
- # Virtus.model.
91
88
  def self.structure?(type)
92
89
  STRUCTURES.include?(type)
93
90
  end
@@ -104,25 +101,6 @@ module Grape
104
101
  (type.is_a?(Array) || type.is_a?(Set)) && type.size > 1
105
102
  end
106
103
 
107
- # Does the given class implement a type system that Grape
108
- # (i.e. the underlying virtus attribute system) supports
109
- # out-of-the-box? Currently supported are +axiom-types+
110
- # and +virtus+.
111
- #
112
- # The type will be passed to +Virtus::Attribute.build+,
113
- # and the resulting attribute object will be expected to
114
- # respond correctly to +coerce+ and +value_coerced?+.
115
- #
116
- # @param type [Class] type to check
117
- # @return [Boolean] +true+ where the type is recognized
118
- def self.recognized?(type)
119
- return false if type.is_a?(Array) || type.is_a?(Set)
120
-
121
- type.is_a?(Virtus::Attribute) ||
122
- type.ancestors.include?(Axiom::Types::Type) ||
123
- type.include?(Virtus::Model::Core)
124
- end
125
-
126
104
  # Does Grape provide special coercion and validation
127
105
  # routines for the given class? This does not include
128
106
  # automatic handling for primitives, structures and
@@ -152,7 +130,6 @@ module Grape
152
130
  !primitive?(type) &&
153
131
  !structure?(type) &&
154
132
  !multiple?(type) &&
155
- !recognized?(type) &&
156
133
  !special?(type) &&
157
134
  type.respond_to?(:parse) &&
158
135
  type.method(:parse).arity == 1
@@ -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,11 +32,22 @@ 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)
26
- params[attr_name] = 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
+ params[attr_name] = new_value unless params[attr_name] == new_value
27
51
  end
28
52
 
29
53
  private
@@ -33,32 +57,26 @@ module Grape
33
57
  #
34
58
  # See {Types.build_coercer}
35
59
  #
36
- # @return [Virtus::Attribute]
60
+ # @return [Object]
37
61
  attr_reader :converter
38
62
 
39
63
  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
64
+ !val.is_a?(Types::InvalidValue)
47
65
  end
48
66
 
49
67
  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
68
+ # define default values for structures, the dry-types lib which is used
69
+ # for coercion doesn't accept nil as a value, so it would fail
70
+ if val.nil?
71
+ return [] if type == Array || type.is_a?(Array)
72
+ return Set.new if type == Set
73
+ return {} if type == Hash
55
74
  end
56
75
 
57
- converter.coerce(val)
76
+ converter.call(val)
58
77
 
59
- # not the prettiest but some invalid coercion can currently trigger
60
- # errors in Virtus (see coerce_spec.rb:75)
61
- rescue
78
+ # Some custom types might fail, so it should be treated as an invalid value
79
+ rescue StandardError
62
80
  Types::InvalidValue.new
63
81
  end
64
82
 
@@ -69,9 +87,8 @@ module Grape
69
87
  @option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
70
88
  end
71
89
 
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)
90
+ def validation_exception(attr_name)
91
+ Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:coerce))
75
92
  end
76
93
  end
77
94
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class DefaultValidator < Base
@@ -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
8
  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)
9
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
8
10
  end
9
11
  end
10
12
  end
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Validations
3
5
  class SameAsValidator < Base
4
6
  def validate_param!(attr_name, params)
5
7
  confirmation = options_key?(:value) ? @option[:value] : @option
6
8
  return if params[attr_name] == params[confirmation]
7
- raise Grape::Exceptions::Validation,
8
- params: [@scope.full_name(attr_name)],
9
- message: build_message
9
+ raise Grape::Exceptions::Validation.new(
10
+ params: [@scope.full_name(attr_name)],
11
+ message: build_message
12
+ )
10
13
  end
11
14
 
12
15
  private