grape 1.2.5 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -0
- data/LICENSE +1 -1
- data/README.md +53 -16
- data/UPGRADING.md +231 -23
- data/grape.gemspec +10 -1
- data/lib/grape.rb +6 -7
- data/lib/grape/api.rb +4 -2
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +36 -33
- data/lib/grape/config.rb +2 -0
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/api.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +2 -0
- data/lib/grape/dsl/configuration.rb +2 -0
- data/lib/grape/dsl/desc.rb +2 -0
- data/lib/grape/dsl/headers.rb +2 -0
- data/lib/grape/dsl/helpers.rb +4 -2
- data/lib/grape/dsl/inside_route.rb +83 -34
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +2 -0
- data/lib/grape/dsl/parameters.rb +8 -6
- data/lib/grape/dsl/request_response.rb +4 -2
- data/lib/grape/dsl/routing.rb +9 -5
- data/lib/grape/dsl/settings.rb +7 -1
- data/lib/grape/dsl/validations.rb +20 -1
- data/lib/grape/eager_load.rb +3 -1
- data/lib/grape/endpoint.rb +21 -13
- data/lib/grape/error_formatter.rb +3 -1
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/error_formatter/json.rb +2 -0
- data/lib/grape/error_formatter/txt.rb +2 -0
- data/lib/grape/error_formatter/xml.rb +2 -0
- data/lib/grape/exceptions/base.rb +11 -13
- data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
- data/lib/grape/exceptions/invalid_formatter.rb +2 -0
- data/lib/grape/exceptions/invalid_message_body.rb +2 -0
- data/lib/grape/exceptions/invalid_response.rb +2 -0
- data/lib/grape/exceptions/invalid_version_header.rb +2 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
- data/lib/grape/exceptions/method_not_allowed.rb +2 -0
- data/lib/grape/exceptions/missing_group_type.rb +2 -0
- data/lib/grape/exceptions/missing_mime_type.rb +2 -0
- data/lib/grape/exceptions/missing_option.rb +2 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
- data/lib/grape/exceptions/unknown_options.rb +2 -0
- data/lib/grape/exceptions/unknown_parameter.rb +2 -0
- data/lib/grape/exceptions/unknown_validator.rb +2 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
- data/lib/grape/exceptions/validation.rb +3 -1
- data/lib/grape/exceptions/validation_array_errors.rb +2 -0
- data/lib/grape/exceptions/validation_errors.rb +13 -12
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
- data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
- data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
- data/lib/grape/extensions/hash.rb +2 -0
- data/lib/grape/extensions/hashie/mash.rb +2 -0
- data/lib/grape/formatter.rb +5 -3
- data/lib/grape/formatter/json.rb +2 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -0
- data/lib/grape/formatter/txt.rb +2 -0
- data/lib/grape/formatter/xml.rb +2 -0
- data/lib/grape/http/headers.rb +50 -18
- data/lib/grape/middleware/auth/base.rb +2 -0
- data/lib/grape/middleware/auth/dsl.rb +2 -0
- data/lib/grape/middleware/auth/strategies.rb +2 -0
- data/lib/grape/middleware/auth/strategy_info.rb +2 -0
- data/lib/grape/middleware/base.rb +7 -7
- data/lib/grape/middleware/error.rb +3 -1
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +8 -6
- data/lib/grape/middleware/globals.rb +2 -0
- data/lib/grape/middleware/helpers.rb +2 -0
- data/lib/grape/middleware/stack.rb +4 -1
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +3 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
- data/lib/grape/middleware/versioner/path.rb +3 -1
- data/lib/grape/namespace.rb +14 -2
- data/lib/grape/parser.rb +3 -1
- data/lib/grape/parser/json.rb +2 -0
- data/lib/grape/parser/xml.rb +2 -0
- data/lib/grape/path.rb +15 -3
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +15 -8
- data/lib/grape/router.rb +30 -29
- data/lib/grape/router/attribute_translator.rb +39 -8
- data/lib/grape/router/pattern.rb +20 -16
- data/lib/grape/router/route.rb +12 -26
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
- data/lib/grape/util/base_inheritable.rb +15 -6
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/endpoint_configuration.rb +2 -0
- data/lib/grape/util/env.rb +19 -17
- data/lib/grape/util/inheritable_setting.rb +2 -0
- data/lib/grape/util/inheritable_values.rb +2 -0
- data/lib/grape/util/json.rb +2 -0
- data/lib/grape/util/lazy_block.rb +2 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +2 -0
- data/lib/grape/util/registrable.rb +2 -0
- data/lib/grape/util/reverse_stackable_values.rb +4 -0
- data/lib/grape/util/stackable_values.rb +10 -20
- data/lib/grape/util/strict_hash_configuration.rb +2 -0
- data/lib/grape/util/xml.rb +2 -0
- data/lib/grape/validations.rb +2 -0
- data/lib/grape/validations/attributes_iterator.rb +3 -3
- data/lib/grape/validations/multiple_attributes_iterator.rb +2 -0
- data/lib/grape/validations/params_scope.rb +27 -14
- data/lib/grape/validations/single_attribute_iterator.rb +13 -2
- data/lib/grape/validations/types.rb +12 -34
- data/lib/grape/validations/types/array_coercer.rb +65 -0
- data/lib/grape/validations/types/build_coercer.rb +47 -49
- data/lib/grape/validations/types/custom_type_coercer.rb +15 -49
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
- data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
- data/lib/grape/validations/types/file.rb +22 -18
- data/lib/grape/validations/types/json.rb +46 -39
- data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
- data/lib/grape/validations/types/primitive_coercer.rb +67 -0
- data/lib/grape/validations/types/set_coercer.rb +40 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
- data/lib/grape/validations/validator_factory.rb +2 -0
- data/lib/grape/validations/validators/all_or_none.rb +3 -1
- data/lib/grape/validations/validators/allow_blank.rb +3 -1
- data/lib/grape/validations/validators/as.rb +2 -0
- data/lib/grape/validations/validators/at_least_one_of.rb +3 -1
- data/lib/grape/validations/validators/base.rb +8 -5
- data/lib/grape/validations/validators/coerce.rb +39 -29
- data/lib/grape/validations/validators/default.rb +2 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +6 -2
- data/lib/grape/validations/validators/except_values.rb +3 -1
- data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
- data/lib/grape/validations/validators/mutual_exclusion.rb +3 -1
- data/lib/grape/validations/validators/presence.rb +3 -1
- data/lib/grape/validations/validators/regexp.rb +4 -2
- data/lib/grape/validations/validators/same_as.rb +6 -3
- data/lib/grape/validations/validators/values.rb +17 -5
- data/lib/grape/version.rb +3 -1
- data/spec/grape/api/custom_validations_spec.rb +5 -3
- data/spec/grape/api/deeply_included_options_spec.rb +2 -0
- data/spec/grape/api/defines_boolean_in_params_spec.rb +5 -3
- data/spec/grape/api/inherited_helpers_spec.rb +2 -0
- data/spec/grape/api/instance_spec.rb +104 -0
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/nested_helpers_spec.rb +2 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/parameters_modification_spec.rb +3 -1
- data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +2 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +2 -0
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
- data/spec/grape/api/shared_helpers_spec.rb +2 -0
- data/spec/grape/api_remount_spec.rb +2 -0
- data/spec/grape/api_spec.rb +99 -11
- data/spec/grape/config_spec.rb +2 -0
- data/spec/grape/dsl/callbacks_spec.rb +2 -0
- data/spec/grape/dsl/configuration_spec.rb +2 -0
- data/spec/grape/dsl/desc_spec.rb +2 -0
- data/spec/grape/dsl/headers_spec.rb +2 -0
- data/spec/grape/dsl/helpers_spec.rb +4 -2
- data/spec/grape/dsl/inside_route_spec.rb +177 -33
- data/spec/grape/dsl/logger_spec.rb +2 -0
- data/spec/grape/dsl/middleware_spec.rb +2 -0
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +2 -0
- data/spec/grape/dsl/routing_spec.rb +2 -0
- data/spec/grape/dsl/settings_spec.rb +2 -0
- data/spec/grape/dsl/validations_spec.rb +2 -0
- data/spec/grape/endpoint_spec.rb +21 -6
- data/spec/grape/entity_spec.rb +2 -0
- data/spec/grape/exceptions/base_spec.rb +3 -1
- data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_response_spec.rb +2 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
- data/spec/grape/exceptions/missing_option_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
- data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +4 -2
- data/spec/grape/exceptions/validation_spec.rb +3 -1
- data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
- data/spec/grape/integration/rack_spec.rb +3 -1
- data/spec/grape/loading_spec.rb +2 -0
- data/spec/grape/middleware/auth/base_spec.rb +2 -0
- data/spec/grape/middleware/auth/dsl_spec.rb +2 -0
- data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
- data/spec/grape/middleware/base_spec.rb +2 -0
- data/spec/grape/middleware/error_spec.rb +2 -0
- data/spec/grape/middleware/exception_spec.rb +3 -1
- data/spec/grape/middleware/formatter_spec.rb +19 -12
- data/spec/grape/middleware/globals_spec.rb +2 -0
- data/spec/grape/middleware/stack_spec.rb +11 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
- data/spec/grape/middleware/versioner/header_spec.rb +3 -1
- data/spec/grape/middleware/versioner/param_spec.rb +3 -1
- data/spec/grape/middleware/versioner/path_spec.rb +3 -1
- data/spec/grape/middleware/versioner_spec.rb +2 -0
- data/spec/grape/named_api_spec.rb +2 -0
- data/spec/grape/parser_spec.rb +7 -5
- data/spec/grape/path_spec.rb +6 -4
- data/spec/grape/presenters/presenter_spec.rb +2 -0
- data/spec/grape/request_spec.rb +2 -0
- data/spec/grape/util/inheritable_setting_spec.rb +2 -0
- data/spec/grape/util/inheritable_values_spec.rb +2 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
- data/spec/grape/util/stackable_values_spec.rb +3 -1
- data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
- data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +2 -0
- data/spec/grape/validations/params_scope_spec.rb +3 -1
- data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -4
- data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
- data/spec/grape/validations/types_spec.rb +9 -36
- data/spec/grape/validations/validators/all_or_none_spec.rb +2 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +2 -0
- data/spec/grape/validations/validators/coerce_spec.rb +341 -136
- data/spec/grape/validations/validators/default_spec.rb +123 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +14 -12
- data/spec/grape/validations/validators/except_values_spec.rb +3 -1
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +2 -0
- data/spec/grape/validations/validators/presence_spec.rb +30 -0
- data/spec/grape/validations/validators/regexp_spec.rb +2 -0
- data/spec/grape/validations/validators/same_as_spec.rb +2 -0
- data/spec/grape/validations/validators/values_spec.rb +30 -5
- data/spec/grape/validations_spec.rb +91 -33
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +2 -0
- data/spec/integration/multi_xml/xml_spec.rb +2 -0
- data/spec/shared/versioning_examples.rb +2 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/basic_auth_encode_helpers.rb +2 -0
- data/spec/support/content_type_helpers.rb +2 -0
- data/spec/support/eager_load.rb +19 -0
- data/spec/support/endpoint_faker.rb +2 -0
- data/spec/support/file_streamer.rb +2 -0
- data/spec/support/integer_helpers.rb +2 -0
- data/spec/support/versioned_helpers.rb +4 -2
- metadata +48 -28
- data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
- data/lib/grape/util/content_types.rb +0 -26
- data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
@@ -1,21 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
module Types
|
4
|
-
# Instances of this class may be passed to
|
5
|
-
# +Virtus::Attribute.build+ as the +:coercer+
|
6
|
-
# option for custom types that do not otherwise
|
7
|
-
# satisfy the requirements for +Virtus::Attribute::coerce+
|
8
|
-
# and +Virtus::Attribute::value_coerced?+ to work
|
9
|
-
# as expected.
|
10
|
-
#
|
11
|
-
# Subclasses of +Virtus::Attribute+ or +Axiom::Types::Type+
|
12
|
-
# (or for which an axiom type can be inferred, i.e. the
|
13
|
-
# primitives, +Date+, +Time+, etc.) do not need any such
|
14
|
-
# coercer to be passed with them.
|
15
|
-
#
|
16
|
-
# Coercion
|
17
|
-
# --------
|
18
|
-
#
|
19
6
|
# This class will detect type classes that implement
|
20
7
|
# a class-level +parse+ method. The method should accept one
|
21
8
|
# +String+ argument and should return the value coerced to
|
@@ -30,14 +17,14 @@ module Grape
|
|
30
17
|
# Type Checking
|
31
18
|
# -------------
|
32
19
|
#
|
33
|
-
# Calls to +
|
20
|
+
# Calls to +coerced?+ will consult this class to check
|
34
21
|
# that the coerced value produced above is in fact of the
|
35
22
|
# expected type. By default this class performs a basic check
|
36
23
|
# against the type supplied, but this behaviour will be
|
37
24
|
# overridden if the class implements a class-level
|
38
25
|
# +coerced?+ or +parsed?+ method. This method
|
39
26
|
# will receive a single parameter that is the coerced value
|
40
|
-
# and should return +true+
|
27
|
+
# and should return +true+ if the value meets type expectations.
|
41
28
|
# Arbitrary assertions may be made here but the grape validation
|
42
29
|
# system should be preferred.
|
43
30
|
#
|
@@ -46,15 +33,6 @@ module Grape
|
|
46
33
|
# contract as +coerced?+, and must be supplied with a coercion
|
47
34
|
# +method+.
|
48
35
|
class CustomTypeCoercer
|
49
|
-
# Uses +Virtus::Attribute.build+ to build a new
|
50
|
-
# attribute that makes use of this class for
|
51
|
-
# coercion and type validation logic.
|
52
|
-
#
|
53
|
-
# @return [Virtus::Attribute]
|
54
|
-
def self.build(type, method = nil)
|
55
|
-
Virtus::Attribute.build(type, coercer: new(type, method))
|
56
|
-
end
|
57
|
-
|
58
36
|
# A new coercer for the given type specification
|
59
37
|
# and coercion method.
|
60
38
|
#
|
@@ -64,37 +42,25 @@ module Grape
|
|
64
42
|
# optional coercion method. See class docs.
|
65
43
|
def initialize(type, method = nil)
|
66
44
|
coercion_method = infer_coercion_method type, method
|
67
|
-
|
68
45
|
@method = enforce_symbolized_keys type, coercion_method
|
69
|
-
|
70
46
|
@type_check = infer_type_check(type)
|
71
47
|
end
|
72
48
|
|
73
|
-
#
|
74
|
-
# +Virtus::Attribute::coerce+ in order to coerce
|
75
|
-
# the given value.
|
49
|
+
# Coerces the given value.
|
76
50
|
#
|
77
51
|
# @param value [String] value to be coerced, in grape
|
78
52
|
# this should always be a string.
|
79
53
|
# @return [Object] the coerced result
|
80
|
-
def call(
|
81
|
-
|
54
|
+
def call(val)
|
55
|
+
return if val.nil?
|
56
|
+
|
57
|
+
coerced_val = @method.call(val)
|
58
|
+
return InvalidValue.new unless coerced?(coerced_val)
|
59
|
+
coerced_val
|
82
60
|
end
|
83
61
|
|
84
|
-
|
85
|
-
|
86
|
-
# assert that the value has been coerced successfully.
|
87
|
-
#
|
88
|
-
# @param _primitive [Axiom::Types::Type] primitive type
|
89
|
-
# for the coercion as detected by axiom-types' inference
|
90
|
-
# system. For custom types this is typically not much use
|
91
|
-
# (i.e. it is +Axiom::Types::Object+) unless special
|
92
|
-
# inference rules have been declared for the type.
|
93
|
-
# @param value [Object] a coerced result returned from {#call}
|
94
|
-
# @return [true,false] whether or not the coerced value
|
95
|
-
# satisfies type requirements.
|
96
|
-
def success?(_primitive, value)
|
97
|
-
@type_check.call value
|
62
|
+
def coerced?(val)
|
63
|
+
val.nil? || @type_check.call(val)
|
98
64
|
end
|
99
65
|
|
100
66
|
private
|
@@ -160,8 +126,8 @@ module Grape
|
|
160
126
|
# Collections have all values processed individually
|
161
127
|
if [Array, Set].include?(type)
|
162
128
|
lambda do |val|
|
163
|
-
method.call(val).tap do |
|
164
|
-
|
129
|
+
method.call(val).tap do |new_val|
|
130
|
+
new_val.map do |item|
|
165
131
|
item.is_a?(Hash) ? symbolize_keys(item) : item
|
166
132
|
end
|
167
133
|
end
|
@@ -1,12 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
module Types
|
4
|
-
# Instances of this class may be passed to
|
5
|
-
# +Virtus::Attribute.build+ as the +:coercer+
|
6
|
-
# option, to handle collections of types that
|
7
|
-
# provide their own parsing (and optionally,
|
8
|
-
# type-checking) functionality.
|
9
|
-
#
|
10
6
|
# See {CustomTypeCoercer} for details on types
|
11
7
|
# that will be supported by this by this coercer.
|
12
8
|
# This coercer works in the same way as +CustomTypeCoercer+
|
@@ -38,32 +34,21 @@ module Grape
|
|
38
34
|
@set = set
|
39
35
|
end
|
40
36
|
|
41
|
-
#
|
42
|
-
# +Virtus::Attribute::coerce+ in order to coerce
|
43
|
-
# the given value.
|
37
|
+
# Coerces the given value.
|
44
38
|
#
|
45
39
|
# @param value [Array<String>] an array of values to be coerced
|
46
40
|
# @return [Array,Set] the coerced result. May be an +Array+ or a
|
47
41
|
# +Set+ depending on the setting given to the constructor
|
48
42
|
def call(value)
|
49
|
-
coerced = value.map
|
43
|
+
coerced = value.map do |item|
|
44
|
+
coerced_item = super(item)
|
50
45
|
|
51
|
-
|
52
|
-
end
|
46
|
+
return coerced_item if coerced_item.is_a?(InvalidValue)
|
53
47
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
#
|
59
|
-
# @param primitive [Axiom::Types::Type] primitive type for
|
60
|
-
# the coercion as deteced by axiom-types' inference system.
|
61
|
-
# @param value [Enumerable] a coerced result returned from {#call}
|
62
|
-
# @return [true,false] whether or not all of the coerced values in
|
63
|
-
# the collection satisfy type requirements.
|
64
|
-
def success?(primitive, value)
|
65
|
-
value.is_a?(@set ? Set : Array) &&
|
66
|
-
value.all? { |item| super(primitive, item) }
|
48
|
+
coerced_item
|
49
|
+
end
|
50
|
+
|
51
|
+
@set ? Set.new(coerced) : coerced
|
67
52
|
end
|
68
53
|
end
|
69
54
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-types'
|
4
|
+
|
5
|
+
module DryTypes
|
6
|
+
# Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
|
7
|
+
# a container in this case. Check documentation for more information
|
8
|
+
# https://dry-rb.org/gems/dry-types/1.2/getting-started/
|
9
|
+
include Dry.Types()
|
10
|
+
end
|
11
|
+
|
12
|
+
module Grape
|
13
|
+
module Validations
|
14
|
+
module Types
|
15
|
+
# A base class for classes which must identify a coercer to be used.
|
16
|
+
# If the +strict+ argument is true, it won't coerce the given value
|
17
|
+
# but check its type. More information there
|
18
|
+
# https://dry-rb.org/gems/dry-types/1.2/built-in-types/
|
19
|
+
class DryTypeCoercer
|
20
|
+
class << self
|
21
|
+
# Registers a collection coercer which could be found by a type,
|
22
|
+
# see +collection_coercer_for+ method below. This method is meant for inheritors.
|
23
|
+
def register_collection(type)
|
24
|
+
DryTypeCoercer.collection_coercers[type] = self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a collection coercer which corresponds to a given type.
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# collection_coercer_for(Array)
|
31
|
+
# #=> Grape::Validations::Types::ArrayCoercer
|
32
|
+
def collection_coercer_for(type)
|
33
|
+
collection_coercers[type]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns an instance of a coercer for a given type
|
37
|
+
def coercer_instance_for(type, strict = false)
|
38
|
+
return PrimitiveCoercer.new(type, strict) if type.class == Class
|
39
|
+
|
40
|
+
# in case of a collection (Array[Integer]) the type is an instance of a collection,
|
41
|
+
# so we need to figure out the actual type
|
42
|
+
collection_coercer_for(type.class).new(type, strict)
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def collection_coercers
|
48
|
+
@collection_coercers ||= {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(type, strict = false)
|
53
|
+
@type = type
|
54
|
+
@strict = strict
|
55
|
+
@scope = strict ? DryTypes::Strict : DryTypes::Params
|
56
|
+
end
|
57
|
+
|
58
|
+
# Coerces the given value to a type which was specified during
|
59
|
+
# initialization as a type argument.
|
60
|
+
#
|
61
|
+
# @param val [Object]
|
62
|
+
def call(val)
|
63
|
+
return if val.nil?
|
64
|
+
|
65
|
+
@coercer[val]
|
66
|
+
rescue Dry::Types::CoercionError => _e
|
67
|
+
InvalidValue.new
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
attr_reader :scope, :type, :strict
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -1,25 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Validations
|
3
5
|
module Types
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
6
|
+
# Implementation for parameters that are multipart file objects.
|
7
|
+
# Actual handling of these objects is provided by +Rack::Request+;
|
8
|
+
# this class is here only to assert that rack's handling has succeeded.
|
9
|
+
class File
|
10
|
+
class << self
|
11
|
+
def parse(input)
|
12
|
+
return if input.nil?
|
13
|
+
return InvalidValue.new unless parsed?(input)
|
14
|
+
|
15
|
+
# Processing of multipart file objects
|
16
|
+
# is already taken care of by Rack::Request.
|
17
|
+
# Nothing to do here.
|
18
|
+
input
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def parsed?(value)
|
22
|
+
# Rack::Request creates a Hash with filename,
|
23
|
+
# content type and an IO object. Do a bit of basic
|
24
|
+
# duck-typing.
|
25
|
+
value.is_a?(::Hash) && value.key?(:tempfile) && value[:tempfile].is_a?(Tempfile)
|
26
|
+
end
|
23
27
|
end
|
24
28
|
end
|
25
29
|
end
|
@@ -1,43 +1,48 @@
|
|
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
|
-
#
|
7
|
-
#
|
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
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
JSON.parse(input, symbolize_names: true)
|
22
|
-
end
|
14
|
+
class Json
|
15
|
+
class << self
|
16
|
+
# Coerce the input into a JSON-like data structure.
|
17
|
+
#
|
18
|
+
# @param input [String] a JSON-encoded parameter value
|
19
|
+
# @return [Hash,Array<Hash>,nil]
|
20
|
+
def parse(input)
|
21
|
+
return input if parsed?(input)
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# @return [true,false]
|
29
|
-
def value_coerced?(value)
|
30
|
-
value.is_a?(::Hash) || coerced_collection?(value)
|
31
|
-
end
|
23
|
+
# Allow nulls and blank strings
|
24
|
+
return if input.nil? || input.match?(/^\s*$/)
|
25
|
+
JSON.parse(input, symbolize_names: true)
|
26
|
+
end
|
32
27
|
|
33
|
-
|
28
|
+
# Checks that the input was parsed successfully
|
29
|
+
# and isn't something odd such as an array of primitives.
|
30
|
+
#
|
31
|
+
# @param value [Object] result of {#parse}
|
32
|
+
# @return [true,false]
|
33
|
+
def parsed?(value)
|
34
|
+
value.is_a?(::Hash) || coerced_collection?(value)
|
35
|
+
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
protected
|
38
|
+
|
39
|
+
# Is the value an array of JSON-like objects?
|
40
|
+
#
|
41
|
+
# @param value [Object] result of {#parse}
|
42
|
+
# @return [true,false]
|
43
|
+
def coerced_collection?(value)
|
44
|
+
value.is_a?(::Array) && value.all? { |i| i.is_a? ::Hash }
|
45
|
+
end
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
@@ -46,18 +51,20 @@ module Grape
|
|
46
51
|
# objects and arrays of objects, but wraps single objects
|
47
52
|
# in an Array.
|
48
53
|
class JsonArray < Json
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
class << self
|
55
|
+
# See {Json#parse}. Wraps single objects in an array.
|
56
|
+
#
|
57
|
+
# @param input [String] JSON-encoded parameter value
|
58
|
+
# @return [Array<Hash>]
|
59
|
+
def parse(input)
|
60
|
+
json = super
|
61
|
+
Array.wrap(json) unless json.nil?
|
62
|
+
end
|
57
63
|
|
58
|
-
|
59
|
-
|
60
|
-
|
64
|
+
# See {Json#coerced_collection?}
|
65
|
+
def parsed?(value)
|
66
|
+
coerced_collection? value
|
67
|
+
end
|
61
68
|
end
|
62
69
|
end
|
63
70
|
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
|
-
#
|
33
|
-
# +Virtus::Attribute::coerce+ in order to coerce
|
34
|
-
# the given value.
|
34
|
+
# Coerces the given value.
|
35
35
|
#
|
36
|
-
# @param
|
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(
|
41
|
-
|
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
|
-
|
47
|
+
coerced_val = coercer.call(val)
|
45
48
|
|
46
|
-
return
|
49
|
+
return coerced_val unless coerced_val.is_a?(InvalidValue)
|
47
50
|
end
|
48
51
|
|
49
|
-
|
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
|