grape 1.8.0 → 4.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +517 -1
- data/CONTRIBUTING.md +19 -11
- data/README.md +841 -545
- data/UPGRADING.md +1553 -7
- data/grape.gemspec +11 -14
- data/lib/grape/api/instance.rb +77 -160
- data/lib/grape/api.rb +75 -107
- data/lib/grape/content_types.rb +67 -9
- data/lib/grape/cookies.rb +28 -25
- data/lib/grape/declared_params_handler.rb +116 -0
- data/lib/grape/dry_types.rb +48 -6
- data/lib/grape/dsl/callbacks.rb +8 -58
- data/lib/grape/dsl/declared.rb +35 -0
- data/lib/grape/dsl/desc.rb +25 -63
- data/lib/grape/dsl/entity.rb +106 -0
- data/lib/grape/dsl/headers.rb +2 -2
- data/lib/grape/dsl/helpers.rb +83 -64
- data/lib/grape/dsl/inside_route.rb +69 -296
- data/lib/grape/dsl/logger.rb +4 -9
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +88 -94
- data/lib/grape/dsl/request_response.rb +143 -147
- data/lib/grape/dsl/rescue_options.rb +25 -0
- data/lib/grape/dsl/routing.rb +333 -191
- data/lib/grape/dsl/settings.rb +29 -135
- data/lib/grape/dsl/validations.rb +39 -32
- data/lib/grape/dsl/version_options.rb +23 -0
- data/lib/grape/endpoint/options.rb +25 -0
- data/lib/grape/endpoint.rb +319 -269
- data/lib/grape/{util/env.rb → env.rb} +8 -8
- data/lib/grape/error_formatter/base.rb +57 -21
- data/lib/grape/error_formatter/json.rb +13 -15
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +12 -18
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +9 -25
- data/lib/grape/exceptions/base.rb +22 -58
- data/lib/grape/exceptions/error_response.rb +48 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/internal_server_error.rb +16 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_message_body.rb +1 -1
- data/lib/grape/exceptions/invalid_version_header.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/method_not_allowed.rb +1 -1
- data/lib/grape/exceptions/missing_group_type.rb +0 -2
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/request_error.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_error_formatter.rb +11 -0
- data/lib/grape/exceptions/unknown_parameter.rb +1 -1
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
- data/lib/grape/exceptions/validation.rb +28 -11
- data/lib/grape/exceptions/validation_array_errors.rb +5 -0
- data/lib/grape/exceptions/validation_errors.rb +22 -26
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +5 -6
- data/lib/grape/formatter/serializable_hash.rb +7 -10
- data/lib/grape/formatter/txt.rb +3 -5
- data/lib/grape/formatter/xml.rb +4 -6
- data/lib/grape/formatter.rb +7 -25
- data/lib/grape/json.rb +46 -0
- data/lib/grape/locale/en.yml +44 -42
- data/lib/grape/middleware/auth/base.rb +11 -33
- data/lib/grape/middleware/auth/dsl.rb +12 -34
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/base.rb +56 -33
- data/lib/grape/middleware/error.rb +290 -91
- data/lib/grape/middleware/formatter.rb +164 -106
- data/lib/grape/middleware/precomputed_content_types.rb +51 -0
- data/lib/grape/middleware/stack.rb +38 -40
- data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
- data/lib/grape/middleware/versioner/base.rb +66 -0
- data/lib/grape/middleware/versioner/header.rb +44 -129
- data/lib/grape/middleware/versioner/param.rb +4 -25
- data/lib/grape/middleware/versioner/path.rb +48 -25
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/mountable.rb +22 -0
- data/lib/grape/namespace.rb +21 -14
- data/lib/grape/params_builder/base.rb +20 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +15 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -23
- data/lib/grape/path.rb +8 -94
- data/lib/grape/precompiled_json.rb +50 -0
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +200 -26
- data/lib/grape/router/base_route.rb +85 -0
- data/lib/grape/router/greedy_route.rb +30 -0
- data/lib/grape/router/mustermann_pattern.rb +44 -0
- data/lib/grape/router/pattern/path.rb +78 -0
- data/lib/grape/router/pattern.rb +77 -35
- data/lib/grape/router/route.rb +79 -60
- data/lib/grape/router.rb +128 -104
- data/lib/grape/serve_stream/file_body.rb +7 -0
- data/lib/grape/serve_stream/sendfile_response.rb +3 -5
- data/lib/grape/serve_stream/stream_response.rb +7 -0
- data/lib/grape/testing.rb +33 -0
- data/lib/grape/util/api_description.rb +67 -0
- data/lib/grape/util/cache.rb +22 -5
- data/lib/grape/util/deep_freeze.rb +34 -0
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/freeze_on_new.rb +20 -0
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_setting.rb +777 -39
- data/lib/grape/util/lazy/base.rb +16 -0
- data/lib/grape/util/lazy/block.rb +22 -0
- data/lib/grape/util/lazy/value.rb +31 -0
- data/lib/grape/util/lazy/value_array.rb +21 -0
- data/lib/grape/util/lazy/value_enumerable.rb +31 -0
- data/lib/grape/util/lazy/value_hash.rb +21 -0
- data/lib/grape/util/media_type.rb +74 -0
- data/lib/grape/util/path_normalizer.rb +37 -0
- data/lib/grape/util/registry.rb +37 -0
- data/lib/grape/util/shadowed_rescue_handlers.rb +49 -0
- data/lib/grape/util/stackable_values.rb +40 -16
- data/lib/grape/util/translation.rb +42 -0
- data/lib/grape/validations/attributes_iterator.rb +61 -28
- data/lib/grape/validations/coerce_options.rb +21 -0
- data/lib/grape/validations/contract_scope.rb +29 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/oneof_collector.rb +35 -0
- data/lib/grape/validations/param_scope_tracker.rb +62 -0
- data/lib/grape/validations/params_documentation.rb +52 -0
- data/lib/grape/validations/params_scope.rb +223 -306
- data/lib/grape/validations/shared_options.rb +19 -0
- data/lib/grape/validations/single_attribute_iterator.rb +6 -4
- data/lib/grape/validations/types/array_coercer.rb +7 -12
- data/lib/grape/validations/types/custom_type_coercer.rb +47 -85
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
- data/lib/grape/validations/types/dry_type_coercer.rb +17 -28
- data/lib/grape/validations/types/json.rb +1 -5
- data/lib/grape/validations/types/multiple_type_coercer.rb +5 -3
- data/lib/grape/validations/types/primitive_coercer.rb +14 -35
- data/lib/grape/validations/types/set_coercer.rb +1 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +16 -3
- data/lib/grape/validations/types.rb +29 -54
- data/lib/grape/validations/validations_spec.rb +164 -0
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
- data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
- data/lib/grape/validations/validators/base.rb +118 -37
- data/lib/grape/validations/validators/coerce_validator.rb +26 -38
- data/lib/grape/validations/validators/contract_scope_validator.rb +46 -0
- data/lib/grape/validations/validators/default_validator.rb +13 -16
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
- data/lib/grape/validations/validators/except_values_validator.rb +15 -5
- data/lib/grape/validations/validators/length_validator.rb +50 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +12 -9
- data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
- data/lib/grape/validations/validators/oneof_validator.rb +51 -0
- data/lib/grape/validations/validators/presence_validator.rb +4 -2
- data/lib/grape/validations/validators/regexp_validator.rb +11 -3
- data/lib/grape/validations/validators/same_as_validator.rb +7 -15
- data/lib/grape/validations/validators/values_validator.rb +36 -65
- data/lib/grape/validations.rb +8 -21
- data/lib/grape/version.rb +1 -2
- data/lib/grape/xml.rb +17 -0
- data/lib/grape.rb +96 -288
- metadata +83 -294
- data/lib/grape/api/helpers.rb +0 -9
- data/lib/grape/dsl/api.rb +0 -19
- data/lib/grape/dsl/configuration.rb +0 -15
- data/lib/grape/eager_load.rb +0 -20
- data/lib/grape/exceptions/empty_message_body.rb +0 -11
- data/lib/grape/exceptions/missing_option.rb +0 -11
- data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
- data/lib/grape/exceptions/unknown_options.rb +0 -11
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -27
- data/lib/grape/extensions/hash.rb +0 -22
- data/lib/grape/extensions/hashie/mash.rb +0 -26
- data/lib/grape/http/headers.rb +0 -61
- data/lib/grape/middleware/globals.rb +0 -16
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
- data/lib/grape/router/attribute_translator.rb +0 -63
- data/lib/grape/types/invalid_value.rb +0 -8
- data/lib/grape/util/base_inheritable.rb +0 -43
- data/lib/grape/util/inheritable_values.rb +0 -31
- data/lib/grape/util/json.rb +0 -12
- data/lib/grape/util/lazy_block.rb +0 -27
- data/lib/grape/util/lazy_object.rb +0 -43
- data/lib/grape/util/lazy_value.rb +0 -91
- data/lib/grape/util/registrable.rb +0 -15
- data/lib/grape/util/reverse_stackable_values.rb +0 -20
- data/lib/grape/util/strict_hash_configuration.rb +0 -108
- data/lib/grape/util/xml.rb +0 -10
- data/lib/grape/validations/attributes_doc.rb +0 -58
- data/lib/grape/validations/types/build_coercer.rb +0 -94
- data/lib/grape/validations/validator_factory.rb +0 -15
- data/spec/grape/api/custom_validations_spec.rb +0 -213
- data/spec/grape/api/deeply_included_options_spec.rb +0 -56
- data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
- data/spec/grape/api/documentation_spec.rb +0 -59
- data/spec/grape/api/inherited_helpers_spec.rb +0 -114
- data/spec/grape/api/instance_spec.rb +0 -103
- data/spec/grape/api/invalid_format_spec.rb +0 -45
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
- data/spec/grape/api/nested_helpers_spec.rb +0 -50
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
- data/spec/grape/api/parameters_modification_spec.rb +0 -41
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
- data/spec/grape/api/recognize_path_spec.rb +0 -21
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
- data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
- data/spec/grape/api/shared_helpers_spec.rb +0 -36
- data/spec/grape/api_remount_spec.rb +0 -509
- data/spec/grape/api_spec.rb +0 -4356
- data/spec/grape/dsl/callbacks_spec.rb +0 -45
- data/spec/grape/dsl/desc_spec.rb +0 -98
- data/spec/grape/dsl/headers_spec.rb +0 -62
- data/spec/grape/dsl/helpers_spec.rb +0 -100
- data/spec/grape/dsl/inside_route_spec.rb +0 -531
- data/spec/grape/dsl/logger_spec.rb +0 -24
- data/spec/grape/dsl/middleware_spec.rb +0 -60
- data/spec/grape/dsl/parameters_spec.rb +0 -180
- data/spec/grape/dsl/request_response_spec.rb +0 -225
- data/spec/grape/dsl/routing_spec.rb +0 -275
- data/spec/grape/dsl/settings_spec.rb +0 -261
- data/spec/grape/dsl/validations_spec.rb +0 -55
- data/spec/grape/endpoint/declared_spec.rb +0 -846
- data/spec/grape/endpoint_spec.rb +0 -1085
- data/spec/grape/entity_spec.rb +0 -336
- data/spec/grape/exceptions/base_spec.rb +0 -81
- data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
- data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_option_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
- data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
- data/spec/grape/exceptions/validation_spec.rb +0 -19
- data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
- data/spec/grape/grape_spec.rb +0 -9
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
- data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
- data/spec/grape/integration/rack_spec.rb +0 -51
- data/spec/grape/loading_spec.rb +0 -44
- data/spec/grape/middleware/auth/base_spec.rb +0 -31
- data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
- data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
- data/spec/grape/middleware/base_spec.rb +0 -221
- data/spec/grape/middleware/error_spec.rb +0 -85
- data/spec/grape/middleware/exception_spec.rb +0 -294
- data/spec/grape/middleware/formatter_spec.rb +0 -461
- data/spec/grape/middleware/globals_spec.rb +0 -30
- data/spec/grape/middleware/stack_spec.rb +0 -155
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
- data/spec/grape/middleware/versioner/header_spec.rb +0 -345
- data/spec/grape/middleware/versioner/param_spec.rb +0 -171
- data/spec/grape/middleware/versioner/path_spec.rb +0 -62
- data/spec/grape/middleware/versioner_spec.rb +0 -21
- data/spec/grape/named_api_spec.rb +0 -19
- data/spec/grape/parser_spec.rb +0 -86
- data/spec/grape/path_spec.rb +0 -252
- data/spec/grape/presenters/presenter_spec.rb +0 -71
- data/spec/grape/request_spec.rb +0 -126
- data/spec/grape/util/inheritable_setting_spec.rb +0 -242
- data/spec/grape/util/inheritable_values_spec.rb +0 -79
- data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
- data/spec/grape/util/stackable_values_spec.rb +0 -128
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
- data/spec/grape/validations/attributes_doc_spec.rb +0 -153
- data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
- data/spec/grape/validations/params_scope_spec.rb +0 -1420
- data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
- data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
- data/spec/grape/validations/types_spec.rb +0 -111
- data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
- data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
- data/spec/grape/validations/validators/base_spec.rb +0 -38
- data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
- data/spec/grape/validations/validators/default_spec.rb +0 -463
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
- data/spec/grape/validations/validators/except_values_spec.rb +0 -192
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
- data/spec/grape/validations/validators/presence_spec.rb +0 -315
- data/spec/grape/validations/validators/regexp_spec.rb +0 -161
- data/spec/grape/validations/validators/same_as_spec.rb +0 -57
- data/spec/grape/validations/validators/values_spec.rb +0 -733
- data/spec/grape/validations/validators/zh-CN.yml +0 -10
- data/spec/grape/validations_spec.rb +0 -2030
- data/spec/integration/eager_load/eager_load_spec.rb +0 -15
- data/spec/integration/multi_json/json_spec.rb +0 -7
- data/spec/integration/multi_xml/xml_spec.rb +0 -7
- data/spec/shared/deprecated_class_examples.rb +0 -16
- data/spec/shared/versioning_examples.rb +0 -215
- data/spec/spec_helper.rb +0 -52
- data/spec/support/basic_auth_encode_helpers.rb +0 -11
- data/spec/support/chunks.rb +0 -14
- data/spec/support/content_type_helpers.rb +0 -15
- data/spec/support/endpoint_faker.rb +0 -25
- data/spec/support/file_streamer.rb +0 -13
- data/spec/support/integer_helpers.rb +0 -13
- data/spec/support/versioned_helpers.rb +0 -55
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'grape/validations/types/json'
|
|
4
|
-
require 'grape/validations/types/file'
|
|
5
|
-
|
|
6
3
|
module Grape
|
|
7
4
|
module Validations
|
|
8
5
|
# Module for code related to grape's system for
|
|
@@ -78,7 +75,7 @@ module Grape
|
|
|
78
75
|
# @return [Boolean] +true+ if the given value will be treated as
|
|
79
76
|
# a list of types.
|
|
80
77
|
def multiple?(type)
|
|
81
|
-
|
|
78
|
+
array_or_set?(type) && type.size > 1
|
|
82
79
|
end
|
|
83
80
|
|
|
84
81
|
# Does Grape provide special coercion and validation
|
|
@@ -101,17 +98,18 @@ module Grape
|
|
|
101
98
|
GROUPS.include? type
|
|
102
99
|
end
|
|
103
100
|
|
|
101
|
+
def array_or_set?(type)
|
|
102
|
+
type.is_a?(Array) || type.is_a?(Set)
|
|
103
|
+
end
|
|
104
|
+
|
|
104
105
|
# A valid custom type must implement a class-level `parse` method, taking
|
|
105
106
|
# one String argument and returning the parsed value in its correct type.
|
|
106
107
|
#
|
|
107
108
|
# @param type [Class] type to check
|
|
108
109
|
# @return [Boolean] whether or not the type can be used as a custom type
|
|
109
110
|
def custom?(type)
|
|
110
|
-
!primitive?(type) &&
|
|
111
|
-
|
|
112
|
-
!multiple?(type) &&
|
|
113
|
-
type.respond_to?(:parse) &&
|
|
114
|
-
type.method(:parse).arity == 1
|
|
111
|
+
!(primitive?(type) || structure?(type) || multiple?(type)) &&
|
|
112
|
+
type.respond_to?(:parse) && type.method(:parse).arity == 1
|
|
115
113
|
end
|
|
116
114
|
|
|
117
115
|
# Is the declared type an +Array+ or +Set+ of a {#custom?} type?
|
|
@@ -120,9 +118,7 @@ module Grape
|
|
|
120
118
|
# @return [Boolean] true if +type+ is a collection of a type that implements
|
|
121
119
|
# its own +#parse+ method.
|
|
122
120
|
def collection_of_custom?(type)
|
|
123
|
-
(type.
|
|
124
|
-
type.length == 1 &&
|
|
125
|
-
(custom?(type.first) || special?(type.first))
|
|
121
|
+
array_or_set?(type) && type.length == 1 && (custom?(type.first) || special?(type.first))
|
|
126
122
|
end
|
|
127
123
|
|
|
128
124
|
def map_special(type)
|
|
@@ -158,59 +154,38 @@ module Grape
|
|
|
158
154
|
# @return [Object] object to be used
|
|
159
155
|
# for coercion and type validation
|
|
160
156
|
def build_coercer(type, method: nil, strict: false)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
end
|
|
164
|
-
end
|
|
157
|
+
# no cache since unique
|
|
158
|
+
return create_coercer_instance(type, method, strict) if method.respond_to?(:call)
|
|
165
159
|
|
|
166
|
-
|
|
167
|
-
# Maps a custom type provided by Grape, it doesn't map types wrapped by collections!!!
|
|
168
|
-
type = Types.map_special(type)
|
|
169
|
-
|
|
170
|
-
# Use a special coercer for multiply-typed parameters.
|
|
171
|
-
if Types.multiple?(type)
|
|
172
|
-
MultipleTypeCoercer.new(type, method)
|
|
173
|
-
|
|
174
|
-
# Use a special coercer for custom types and coercion methods.
|
|
175
|
-
elsif method || Types.custom?(type)
|
|
176
|
-
CustomTypeCoercer.new(type, method)
|
|
177
|
-
|
|
178
|
-
# Special coercer for collections of types that implement a parse method.
|
|
179
|
-
# CustomTypeCoercer (above) already handles such types when an explicit coercion
|
|
180
|
-
# method is supplied.
|
|
181
|
-
elsif Types.collection_of_custom?(type)
|
|
182
|
-
Types::CustomTypeCollectionCoercer.new(
|
|
183
|
-
Types.map_special(type.first), type.is_a?(Set)
|
|
184
|
-
)
|
|
185
|
-
else
|
|
186
|
-
DryTypeCoercer.coercer_instance_for(type, strict)
|
|
187
|
-
end
|
|
160
|
+
CoercerCache[[type, method, strict]]
|
|
188
161
|
end
|
|
189
162
|
|
|
190
|
-
def
|
|
191
|
-
|
|
163
|
+
def create_coercer_instance(type, method, strict)
|
|
164
|
+
# map_special doesn't recurse into collections — applied only to the top-level type here.
|
|
165
|
+
mapped = map_special(type)
|
|
192
166
|
|
|
193
|
-
|
|
167
|
+
# Multiply-typed parameters, e.g. types: [Integer, String].
|
|
168
|
+
return MultipleTypeCoercer.new(mapped, method) if multiple?(mapped)
|
|
194
169
|
|
|
195
|
-
|
|
170
|
+
# User-supplied coercion method, or a custom type with its own #parse.
|
|
171
|
+
return CustomTypeCoercer.new(mapped, method) if method || custom?(mapped)
|
|
196
172
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
173
|
+
# Array/Set of a custom type — CustomTypeCoercer already handles single
|
|
174
|
+
# custom types when an explicit coercion method is supplied.
|
|
175
|
+
return CustomTypeCollectionCoercer.new(map_special(mapped.first), set: mapped.is_a?(Set)) if collection_of_custom?(mapped)
|
|
200
176
|
|
|
201
|
-
|
|
177
|
+
# Fallback: let dry-types handle primitives, structures, and known specials.
|
|
178
|
+
DryTypeCoercer.coercer_instance_for(mapped, strict:)
|
|
202
179
|
end
|
|
203
180
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
181
|
+
class CoercerCache < Grape::Util::Cache
|
|
182
|
+
def initialize
|
|
183
|
+
super
|
|
184
|
+
@cache = Hash.new do |h, (type, method, strict)|
|
|
185
|
+
h[[type, method, strict]] = Grape::Validations::Types.create_coercer_instance(type, method, strict)
|
|
186
|
+
end
|
|
209
187
|
end
|
|
210
188
|
end
|
|
211
|
-
|
|
212
|
-
instance_variable_set(:@__cache, {})
|
|
213
|
-
instance_variable_set(:@__cache_write_lock, Mutex.new)
|
|
214
189
|
end
|
|
215
190
|
end
|
|
216
191
|
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
# Frozen value object holding everything {ParamsScope#validates} needs to
|
|
6
|
+
# know about a single +requires+/+optional+ declaration. Built once from
|
|
7
|
+
# the raw validations hash supplied by the DSL; the raw hash is never
|
|
8
|
+
# mutated.
|
|
9
|
+
#
|
|
10
|
+
# Splits the raw entries into three logical buckets:
|
|
11
|
+
#
|
|
12
|
+
# * Spec-consumed keys (type/types/coerce*, presence/message,
|
|
13
|
+
# default/fail_fast, doc keys) — exposed via named accessors and never
|
|
14
|
+
# handed to validator dispatch.
|
|
15
|
+
# * Shared opts (allow_blank, fail_fast) — read by every validator at
|
|
16
|
+
# construction time via {#shared_opts}.
|
|
17
|
+
# * Validator entries (everything else, e.g. +regexp+, +length+, +values+,
|
|
18
|
+
# +allow_blank+, custom validators) — exposed via {#validator_entries}
|
|
19
|
+
# for the dispatch loop.
|
|
20
|
+
#
|
|
21
|
+
# Same key can land in more than one bucket (e.g. +allow_blank+ is both a
|
|
22
|
+
# shared opt and a validator entry; +length+ is both a doc source and a
|
|
23
|
+
# validator entry).
|
|
24
|
+
class ValidationsSpec
|
|
25
|
+
# Keys consumed by the spec itself; must NOT be dispatched as validators
|
|
26
|
+
# by the caller. Documentation-only keys are filtered through a separate
|
|
27
|
+
# set so that dual-purpose keys (length, default, values, except_values)
|
|
28
|
+
# aren't accidentally swallowed.
|
|
29
|
+
SPEC_CONSUMED_KEYS = %i[
|
|
30
|
+
type types coerce coerce_with coerce_message
|
|
31
|
+
presence message
|
|
32
|
+
fail_fast
|
|
33
|
+
desc description documentation
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
attr_reader :raw, :coerce_type, :coerce_method, :coerce_message, :presence_options, :values, :except_values, :default, :allow_blank, :fail_fast, :shared_opts, :validator_entries
|
|
37
|
+
|
|
38
|
+
def self.from(validations)
|
|
39
|
+
new(validations)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(raw)
|
|
43
|
+
raise ArgumentError, ':type may not be supplied with :types' if raw.key?(:type) && raw.key?(:types)
|
|
44
|
+
|
|
45
|
+
@raw = raw
|
|
46
|
+
@coerce_type, @coerce_message, @coerce_method = parse_coerce(raw)
|
|
47
|
+
@values = resolve_value(raw[:values])
|
|
48
|
+
@except_values = resolve_value(raw[:except_values])
|
|
49
|
+
@default = raw[:default]
|
|
50
|
+
@presence_options = raw[:presence]
|
|
51
|
+
@allow_blank = resolve_value(raw[:allow_blank])
|
|
52
|
+
@fail_fast = raw[:fail_fast] || false
|
|
53
|
+
|
|
54
|
+
@guessed_coerce_type = guess_coerce_type(declared_coerce_type, @values, @except_values)
|
|
55
|
+
@shared_opts = { allow_blank: @allow_blank, fail_fast: @fail_fast }.freeze
|
|
56
|
+
@validator_entries = build_validator_entries(raw)
|
|
57
|
+
|
|
58
|
+
validate!
|
|
59
|
+
|
|
60
|
+
freeze
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def required?
|
|
64
|
+
!@presence_options.nil? && @presence_options != false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def coerce_options
|
|
68
|
+
CoerceOptions.new(type: @coerce_type, coerce_method: @coerce_method, message: @coerce_message)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# Cross-field consistency checks on the parsed declaration. Run at
|
|
74
|
+
# construction so an incoherent spec (e.g. a +default+ outside +values+,
|
|
75
|
+
# or +values+ whose elements don't match +type+) can never exist —
|
|
76
|
+
# callers no longer have to remember to invoke these separately.
|
|
77
|
+
#
|
|
78
|
+
# NB. +@guessed_coerce_type+ is used only for this check, never for
|
|
79
|
+
# +coerce_options+ — a bare +type: Array+ combined with scalar
|
|
80
|
+
# +values:+ must still coerce to +Array+ at runtime (only the elements
|
|
81
|
+
# are scalar), so the real +@coerce_type+ is never overwritten by it.
|
|
82
|
+
def validate!
|
|
83
|
+
check_incompatible_option_values(@default, @values, @except_values)
|
|
84
|
+
validate_value_coercion(@guessed_coerce_type, @values, @except_values)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def check_incompatible_option_values(default, values, except_values)
|
|
88
|
+
return if default.nil? || default.is_a?(Proc)
|
|
89
|
+
|
|
90
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) if values && !values.is_a?(Proc) && Array(default).any? { |def_val| !values.include?(def_val) }
|
|
91
|
+
|
|
92
|
+
return unless except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }
|
|
93
|
+
|
|
94
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def validate_value_coercion(coerce_type, *values_list)
|
|
98
|
+
return unless coerce_type
|
|
99
|
+
|
|
100
|
+
element_type = coerce_type.is_a?(Enumerable) ? coerce_type.first : coerce_type
|
|
101
|
+
values_list.each do |values|
|
|
102
|
+
next if !values || values.is_a?(Proc)
|
|
103
|
+
|
|
104
|
+
value_types = values.is_a?(Range) ? [values.begin, values.end].compact : values
|
|
105
|
+
value_types = value_types.map { |type| Grape::API::Boolean.build(type) } if element_type == Grape::API::Boolean
|
|
106
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, element_type, :values, values) unless value_types.all?(element_type)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def build_validator_entries(raw)
|
|
111
|
+
raw.reject do |k, _|
|
|
112
|
+
SPEC_CONSUMED_KEYS.include?(k) || ParamsScope::RESERVED_DOCUMENTATION_KEYWORDS.include?(k)
|
|
113
|
+
end.freeze
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def parse_coerce(raw)
|
|
117
|
+
if raw.key?(:type)
|
|
118
|
+
coerce, coerce_message = extract_value_and_message(raw[:type])
|
|
119
|
+
coerce_with = raw[:coerce_with]
|
|
120
|
+
return [Types::VariantCollectionCoercer.new(coerce, coerce_with), coerce_message, nil] if Types.multiple?(coerce)
|
|
121
|
+
elsif raw.key?(:types)
|
|
122
|
+
coerce, coerce_message = extract_value_and_message(raw[:types])
|
|
123
|
+
coerce_with = raw[:coerce_with]
|
|
124
|
+
else
|
|
125
|
+
coerce = raw[:coerce]
|
|
126
|
+
coerce_message = raw[:coerce_message]
|
|
127
|
+
coerce_with = raw[:coerce_with]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
[coerce, coerce_message, coerce_with]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def extract_value_and_message(opt)
|
|
134
|
+
return [opt, nil] unless opt.is_a?(Hash)
|
|
135
|
+
|
|
136
|
+
[opt[:value], opt[:message]]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def resolve_value(opt)
|
|
140
|
+
opt.is_a?(Hash) ? opt[:value] : opt
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# The type as written in the DSL. Identical to +@coerce_type+ except for
|
|
144
|
+
# a multiple-type declaration (+type: [Integer, String]+), where
|
|
145
|
+
# {#parse_coerce} wraps the declared list in a
|
|
146
|
+
# {Types::VariantCollectionCoercer} for runtime coercion — the
|
|
147
|
+
# definition-time coherence checks need the list back, since the wrapper
|
|
148
|
+
# matches nothing under +===+.
|
|
149
|
+
def declared_coerce_type
|
|
150
|
+
@coerce_type.is_a?(Types::VariantCollectionCoercer) ? @coerce_type.types : @coerce_type
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def guess_coerce_type(coerce_type, *values_list)
|
|
154
|
+
return coerce_type unless coerce_type == Array
|
|
155
|
+
|
|
156
|
+
values_list.each do |values|
|
|
157
|
+
next if !values || values.is_a?(Proc)
|
|
158
|
+
return values.first.class if values.is_a?(Range) || !values.empty?
|
|
159
|
+
end
|
|
160
|
+
coerce_type
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -4,11 +4,14 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class AllOrNoneOfValidator < MultipleParamsBase
|
|
7
|
+
default_message_key :all_or_none
|
|
8
|
+
|
|
7
9
|
def validate_params!(params)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
known_keys = all_keys
|
|
11
|
+
keys = keys_in_common(params, known_keys)
|
|
12
|
+
return if keys.empty? || keys.length == attrs.length
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
validation_error!(known_keys)
|
|
12
15
|
end
|
|
13
16
|
end
|
|
14
17
|
end
|
|
@@ -4,15 +4,20 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class AllowBlankValidator < Base
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
default_message_key :blank
|
|
8
|
+
|
|
9
|
+
def initialize(attrs, options, required, scope, opts)
|
|
10
|
+
super
|
|
11
|
+
@value = option_value
|
|
12
|
+
end
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
def validate_param!(attr_name, params)
|
|
15
|
+
return if @value || !hash_like?(params)
|
|
12
16
|
|
|
17
|
+
value = scrub(params[attr_name])
|
|
13
18
|
return if value == false || value.present?
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
validation_error!(attr_name)
|
|
16
21
|
end
|
|
17
22
|
end
|
|
18
23
|
end
|
|
@@ -4,10 +4,13 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class AtLeastOneOfValidator < MultipleParamsBase
|
|
7
|
+
default_message_key :at_least_one
|
|
8
|
+
|
|
7
9
|
def validate_params!(params)
|
|
8
|
-
|
|
10
|
+
known_keys = all_keys
|
|
11
|
+
return if hash_like?(params) && known_keys.intersect?(params.keys.map { |attr| scope.full_name(attr) })
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
validation_error!(known_keys)
|
|
11
14
|
end
|
|
12
15
|
end
|
|
13
16
|
end
|
|
@@ -3,25 +3,72 @@
|
|
|
3
3
|
module Grape
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
|
+
# Base class for all parameter validators.
|
|
7
|
+
#
|
|
8
|
+
# == Freeze contract
|
|
9
|
+
# Validator instances are shared across requests and are frozen after
|
|
10
|
+
# initialization (via +.new+). All inputs (+options+, +opts+, +attrs+)
|
|
11
|
+
# arrive pre-frozen from the DSL boundary, so subclass ivars derived
|
|
12
|
+
# from them are frozen by construction. Lazy ivar assignment
|
|
13
|
+
# (e.g. +memoize+, <tt>||=</tt>) will raise +FrozenError+ at request time.
|
|
6
14
|
class Base
|
|
15
|
+
extend Forwardable
|
|
16
|
+
extend Grape::Util::FreezeOnNew
|
|
17
|
+
include Grape::Util::Translation
|
|
18
|
+
|
|
7
19
|
attr_reader :attrs
|
|
8
20
|
|
|
21
|
+
# +allow_blank+ / +fail_fast+ are read straight off the internal
|
|
22
|
+
# {Grape::Validations::SharedOptions} value object; no per-validator
|
|
23
|
+
# ivars. The object is built from the +opts+ Hash in #initialize, so
|
|
24
|
+
# the public 5th-argument contract stays a plain Hash.
|
|
25
|
+
def_delegators :@opts, :allow_blank, :fail_fast
|
|
26
|
+
|
|
27
|
+
alias fail_fast? fail_fast
|
|
28
|
+
alias allow_blank? allow_blank
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# Declares the default I18n message key used by +validation_error!+.
|
|
32
|
+
# Subclasses that only need a single fixed error message can declare it
|
|
33
|
+
# at the class level instead of overriding +initialize+:
|
|
34
|
+
#
|
|
35
|
+
# class MyValidator < Grape::Validations::Validators::Base
|
|
36
|
+
# default_message_key :my_error
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# The key is resolved through +message+, so a per-option +:message+
|
|
40
|
+
# override still takes precedence.
|
|
41
|
+
def default_message_key(key = nil)
|
|
42
|
+
if key
|
|
43
|
+
@default_message_key = key
|
|
44
|
+
else
|
|
45
|
+
@default_message_key || (superclass.respond_to?(:default_message_key) ? superclass.default_message_key : nil)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def inherited(klass)
|
|
50
|
+
super
|
|
51
|
+
Validations.register(klass)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
9
55
|
# Creates a new Validator from options specified
|
|
10
56
|
# by a +requires+ or +optional+ directive during
|
|
11
57
|
# parameter definition.
|
|
12
58
|
# @param attrs [Array] names of attributes to which the Validator applies
|
|
13
|
-
# @param options [Object] implementation-dependent Validator options
|
|
59
|
+
# @param options [Object] implementation-dependent Validator options; deep-frozen on assignment
|
|
14
60
|
# @param required [Boolean] attribute(s) are required or optional
|
|
15
61
|
# @param scope [ParamsScope] parent scope for this Validator
|
|
16
|
-
# @param opts [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@
|
|
62
|
+
# @param opts [Hash] shared validator options; only +:allow_blank+ and
|
|
63
|
+
# +:fail_fast+ are consulted (other keys ignored, as before)
|
|
64
|
+
def initialize(attrs, options, required, scope, opts)
|
|
65
|
+
@attrs = Array(attrs).freeze
|
|
66
|
+
@options = Grape::Util::DeepFreeze.deep_freeze(options)
|
|
20
67
|
@required = required
|
|
21
68
|
@scope = scope
|
|
22
|
-
opts =
|
|
23
|
-
@
|
|
24
|
-
@
|
|
69
|
+
@opts = SharedOptions.new(**opts.slice(:allow_blank, :fail_fast))
|
|
70
|
+
@exception_message = message(self.class.default_message_key) if self.class.default_message_key
|
|
71
|
+
@iterator = iterator_class.new(@attrs, @scope).freeze
|
|
25
72
|
end
|
|
26
73
|
|
|
27
74
|
# Validates a given request.
|
|
@@ -30,62 +77,96 @@ module Grape
|
|
|
30
77
|
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
31
78
|
# @return [void]
|
|
32
79
|
def validate(request)
|
|
33
|
-
|
|
80
|
+
params = request.params
|
|
81
|
+
return unless scope.should_validate?(params)
|
|
34
82
|
|
|
35
|
-
validate!(
|
|
83
|
+
validate!(params)
|
|
36
84
|
end
|
|
37
85
|
|
|
38
86
|
# Validates a given parameter hash.
|
|
39
|
-
# @note Override #
|
|
87
|
+
# @note Override #validate_param! for per-parameter validation,
|
|
88
|
+
# or #validate if you need access to the entire request.
|
|
40
89
|
# @param params [Hash] parameters to validate
|
|
41
90
|
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
42
91
|
# @return [void]
|
|
43
92
|
def validate!(params)
|
|
44
|
-
attributes = SingleAttributeIterator.new(self, @scope, params)
|
|
45
93
|
# we collect errors inside array because
|
|
46
94
|
# there may be more than one error per field
|
|
47
|
-
array_errors =
|
|
95
|
+
array_errors = nil
|
|
48
96
|
|
|
49
|
-
|
|
50
|
-
next if
|
|
51
|
-
next unless
|
|
97
|
+
@iterator.each(params) do |val, attr_name, empty_val|
|
|
98
|
+
next if !scope.required? && empty_val
|
|
99
|
+
next unless scope.meets_dependency?(val, params)
|
|
52
100
|
|
|
53
|
-
validate_param!(attr_name, val) if
|
|
101
|
+
validate_param!(attr_name, val) if required? || (hash_like?(val) && val.key?(attr_name))
|
|
54
102
|
rescue Grape::Exceptions::Validation => e
|
|
55
|
-
array_errors << e
|
|
103
|
+
(array_errors ||= []) << e
|
|
56
104
|
end
|
|
57
105
|
|
|
58
|
-
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors
|
|
106
|
+
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
protected
|
|
110
|
+
|
|
111
|
+
# Validates a single attribute. Override in subclasses.
|
|
112
|
+
# @param attr_name [Symbol, String] the attribute name
|
|
113
|
+
# @param params [Hash] the parameter hash containing the attribute
|
|
114
|
+
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
115
|
+
# @return [void]
|
|
116
|
+
def validate_param!(attr_name, params)
|
|
117
|
+
raise NotImplementedError
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
attr_reader :options, :scope, :required, :exception_message
|
|
123
|
+
|
|
124
|
+
alias required? required
|
|
125
|
+
|
|
126
|
+
# The AttributesIterator subclass used to walk this validator's
|
|
127
|
+
# attributes. Built once in #initialize and reused across requests.
|
|
128
|
+
def iterator_class
|
|
129
|
+
SingleAttributeIterator
|
|
59
130
|
end
|
|
60
131
|
|
|
61
|
-
def
|
|
62
|
-
|
|
132
|
+
def validation_error!(attr_name_or_params, message = exception_message)
|
|
133
|
+
params = attr_name_or_params.is_a?(Array) ? attr_name_or_params : scope.full_name(attr_name_or_params)
|
|
134
|
+
raise Grape::Exceptions::Validation.new(params:, message:)
|
|
135
|
+
end
|
|
63
136
|
|
|
64
|
-
|
|
65
|
-
|
|
137
|
+
def hash_like?(obj)
|
|
138
|
+
obj.respond_to?(:key?)
|
|
66
139
|
end
|
|
67
140
|
|
|
141
|
+
def options_key?(key, given_options = nil)
|
|
142
|
+
current_options = given_options || options
|
|
143
|
+
hash_like?(current_options) && current_options.key?(key) && !current_options[key].nil?
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Returns the effective message for a validation error.
|
|
147
|
+
# Prefers an explicit +:message+ option, then +default_key+.
|
|
148
|
+
# If both are nil, the block (if given) is called to compute a fallback —
|
|
149
|
+
# useful for validators that build a message Hash for deferred i18n interpolation.
|
|
150
|
+
# @example
|
|
151
|
+
# @exception_message = message(:presence) # symbol key or custom message
|
|
152
|
+
# @exception_message = message { build_hash_message } # computed fallback
|
|
68
153
|
def message(default_key = nil)
|
|
69
|
-
|
|
70
|
-
|
|
154
|
+
key = options_key?(:message) ? options[:message] : default_key
|
|
155
|
+
return key unless key.nil?
|
|
156
|
+
|
|
157
|
+
yield if block_given?
|
|
71
158
|
end
|
|
72
159
|
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
|
|
160
|
+
def option_value
|
|
161
|
+
options_key?(:value) ? options[:value] : options
|
|
76
162
|
end
|
|
77
163
|
|
|
78
|
-
def
|
|
79
|
-
|
|
164
|
+
def scrub(value)
|
|
165
|
+
return value if !value.respond_to?(:valid_encoding?) || value.valid_encoding?
|
|
166
|
+
|
|
167
|
+
value.scrub
|
|
80
168
|
end
|
|
81
169
|
end
|
|
82
170
|
end
|
|
83
171
|
end
|
|
84
172
|
end
|
|
85
|
-
|
|
86
|
-
Grape::Validations::Base = Class.new(Grape::Validations::Validators::Base) do
|
|
87
|
-
def self.inherited(*)
|
|
88
|
-
ActiveSupport::Deprecation.warn 'Grape::Validations::Base is deprecated! Use Grape::Validations::Validators::Base instead.'
|
|
89
|
-
super
|
|
90
|
-
end
|
|
91
|
-
end
|
|
@@ -4,22 +4,35 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class CoerceValidator < Base
|
|
7
|
-
|
|
7
|
+
default_message_key :coerce
|
|
8
|
+
|
|
9
|
+
def initialize(attrs, options, required, scope, opts)
|
|
8
10
|
super
|
|
9
11
|
|
|
10
|
-
@
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
@exception_message = options.message if options.message
|
|
13
|
+
|
|
14
|
+
raw_type = options.type
|
|
15
|
+
type = hash_like?(raw_type) ? raw_type[:value] : raw_type
|
|
16
|
+
@converter =
|
|
17
|
+
if type.is_a?(Grape::Validations::Types::VariantCollectionCoercer)
|
|
18
|
+
type
|
|
19
|
+
else
|
|
20
|
+
Types.build_coercer(type, method: options.coerce_method)
|
|
21
|
+
end
|
|
15
22
|
end
|
|
16
23
|
|
|
24
|
+
# The attribute is read once and held: +coerce_value+ only ever sees the
|
|
25
|
+
# value, never the Hash, so the two further reads the identity check
|
|
26
|
+
# used to make came back with the same object -- through a
|
|
27
|
+
# HashWithIndifferentAccess key conversion each time, for every coerced
|
|
28
|
+
# attribute of every request.
|
|
17
29
|
def validate_param!(attr_name, params)
|
|
18
|
-
|
|
30
|
+
validation_error!(attr_name) unless hash_like?(params)
|
|
19
31
|
|
|
20
|
-
|
|
32
|
+
value = params[attr_name]
|
|
33
|
+
new_value = coerce_value(value)
|
|
21
34
|
|
|
22
|
-
|
|
35
|
+
validation_error!(attr_name, new_value.message || exception_message) if new_value.is_a?(Types::InvalidValue)
|
|
23
36
|
|
|
24
37
|
# Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
|
|
25
38
|
# which looses wrappers for hashes and arrays after reassigning values
|
|
@@ -30,45 +43,20 @@ module Grape
|
|
|
30
43
|
# h[:list] = list
|
|
31
44
|
# h
|
|
32
45
|
# => #<Hashie::Mash list=[1, 2, 3, 4]>
|
|
33
|
-
return if
|
|
46
|
+
return if value.instance_of?(new_value.class) && value == new_value
|
|
34
47
|
|
|
35
48
|
params[attr_name] = new_value
|
|
36
49
|
end
|
|
37
50
|
|
|
38
51
|
private
|
|
39
52
|
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
# See {Types.build_coercer}
|
|
44
|
-
#
|
|
45
|
-
# @return [Object]
|
|
46
|
-
attr_reader :converter
|
|
47
|
-
|
|
48
|
-
def valid_type?(val)
|
|
49
|
-
!val.is_a?(Types::InvalidValue)
|
|
50
|
-
end
|
|
51
|
-
|
|
53
|
+
# Calls the converter built at definition time.
|
|
54
|
+
# Custom coercers may raise; any StandardError is treated as an invalid value.
|
|
52
55
|
def coerce_value(val)
|
|
53
|
-
converter.call(val)
|
|
54
|
-
# Some custom types might fail, so it should be treated as an invalid value
|
|
56
|
+
@converter.call(val)
|
|
55
57
|
rescue StandardError
|
|
56
58
|
Types::InvalidValue.new
|
|
57
59
|
end
|
|
58
|
-
|
|
59
|
-
# Type to which the parameter will be coerced.
|
|
60
|
-
#
|
|
61
|
-
# @return [Class]
|
|
62
|
-
def type
|
|
63
|
-
@option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def validation_exception(attr_name, custom_msg = nil)
|
|
67
|
-
Grape::Exceptions::Validation.new(
|
|
68
|
-
params: [@scope.full_name(attr_name)],
|
|
69
|
-
message: custom_msg || message(:coerce)
|
|
70
|
-
)
|
|
71
|
-
end
|
|
72
60
|
end
|
|
73
61
|
end
|
|
74
62
|
end
|