grape 1.8.0 → 3.3.5
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 +391 -1
- data/CONTRIBUTING.md +3 -11
- data/README.md +648 -552
- data/UPGRADING.md +803 -7
- data/grape.gemspec +11 -14
- data/lib/grape/api/instance.rb +68 -158
- data/lib/grape/api.rb +71 -107
- data/lib/grape/content_types.rb +23 -11
- 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 +14 -58
- data/lib/grape/dsl/declared.rb +35 -0
- data/lib/grape/dsl/desc.rb +15 -63
- data/lib/grape/dsl/entity.rb +88 -0
- data/lib/grape/dsl/headers.rb +2 -2
- data/lib/grape/dsl/helpers.rb +83 -64
- data/lib/grape/dsl/inside_route.rb +56 -295
- data/lib/grape/dsl/logger.rb +4 -9
- data/lib/grape/dsl/middleware.rb +22 -40
- data/lib/grape/dsl/parameters.rb +50 -82
- data/lib/grape/dsl/request_response.rb +144 -146
- data/lib/grape/dsl/rescue_options.rb +24 -0
- data/lib/grape/dsl/routing.rb +261 -186
- 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 +19 -0
- data/lib/grape/endpoint.rb +277 -250
- data/lib/grape/{util/env.rb → env.rb} +1 -8
- data/lib/grape/error_formatter/base.rb +56 -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 +5 -25
- data/lib/grape/exceptions/base.rb +22 -58
- data/lib/grape/exceptions/error_response.rb +45 -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_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 +25 -9
- 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 +4 -6
- data/lib/grape/formatter/serializable_hash.rb +6 -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 +43 -42
- data/lib/grape/middleware/auth/base.rb +11 -33
- data/lib/grape/middleware/auth/dsl.rb +37 -31
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/base.rb +56 -33
- data/lib/grape/middleware/deprecated_options_hash_access.rb +19 -0
- data/lib/grape/middleware/error.rb +183 -91
- data/lib/grape/middleware/formatter.rb +94 -105
- data/lib/grape/middleware/precomputed_content_types.rb +46 -0
- data/lib/grape/middleware/stack.rb +27 -38
- data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
- data/lib/grape/middleware/versioner/base.rb +72 -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 +13 -32
- data/lib/grape/middleware/versioner.rb +7 -14
- data/lib/grape/mountable.rb +22 -0
- data/lib/grape/namespace.rb +18 -12
- 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 +38 -63
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +168 -26
- data/lib/grape/router/base_route.rb +48 -0
- data/lib/grape/router/greedy_route.rb +26 -0
- data/lib/grape/router/mustermann_pattern.rb +44 -0
- data/lib/grape/router/pattern.rb +30 -34
- data/lib/grape/router/route.rb +28 -61
- data/lib/grape/router.rb +71 -86
- data/lib/grape/serve_stream/file_body.rb +1 -0
- data/lib/grape/serve_stream/sendfile_response.rb +3 -5
- data/lib/grape/serve_stream/stream_response.rb +1 -0
- data/lib/grape/testing.rb +33 -0
- data/lib/grape/util/api_description.rb +58 -0
- data/lib/grape/util/base_inheritable.rb +14 -14
- data/lib/grape/util/cache.rb +1 -3
- data/lib/grape/util/deep_freeze.rb +35 -0
- data/lib/grape/util/endpoint_configuration.rb +1 -1
- data/lib/grape/util/header.rb +13 -0
- data/lib/grape/util/inheritable_setting.rb +51 -27
- data/lib/grape/util/inheritable_values.rb +7 -5
- 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 +67 -0
- data/lib/grape/util/path_normalizer.rb +34 -0
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/util/reverse_stackable_values.rb +1 -6
- data/lib/grape/util/stackable_values.rb +12 -14
- data/lib/grape/util/translation.rb +42 -0
- data/lib/grape/validations/attributes_iterator.rb +43 -27
- data/lib/grape/validations/coerce_options.rb +21 -0
- data/lib/grape/validations/contract_scope.rb +28 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/oneof_collector.rb +39 -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 +180 -303
- data/lib/grape/validations/shared_options.rb +19 -0
- data/lib/grape/validations/single_attribute_iterator.rb +2 -2
- data/lib/grape/validations/types/array_coercer.rb +4 -7
- data/lib/grape/validations/types/custom_type_coercer.rb +41 -85
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
- data/lib/grape/validations/types/dry_type_coercer.rb +15 -28
- data/lib/grape/validations/types/json.rb +0 -2
- data/lib/grape/validations/types/primitive_coercer.rb +11 -35
- data/lib/grape/validations/types/set_coercer.rb +1 -4
- data/lib/grape/validations/types/variant_collection_coercer.rb +11 -0
- 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 +120 -36
- data/lib/grape/validations/validators/coerce_validator.rb +18 -36
- data/lib/grape/validations/validators/contract_scope_validator.rb +45 -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 +48 -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 +49 -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 +6 -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 +75 -288
- metadata +79 -291
- 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/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/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
|
@@ -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
|
+
coerce_type = coerce_type.first if coerce_type.is_a?(Enumerable)
|
|
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 coerce_type == Grape::API::Boolean
|
|
106
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values) unless value_types.all?(coerce_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,76 @@
|
|
|
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
|
+
include Grape::Util::Translation
|
|
17
|
+
|
|
7
18
|
attr_reader :attrs
|
|
8
19
|
|
|
20
|
+
# +allow_blank+ / +fail_fast+ are read straight off the internal
|
|
21
|
+
# {Grape::Validations::SharedOptions} value object; no per-validator
|
|
22
|
+
# ivars. The object is built from the +opts+ Hash in #initialize, so
|
|
23
|
+
# the public 5th-argument contract stays a plain Hash.
|
|
24
|
+
def_delegators :@opts, :allow_blank, :fail_fast
|
|
25
|
+
|
|
26
|
+
alias fail_fast? fail_fast
|
|
27
|
+
alias allow_blank? allow_blank
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
# Declares the default I18n message key used by +validation_error!+.
|
|
31
|
+
# Subclasses that only need a single fixed error message can declare it
|
|
32
|
+
# at the class level instead of overriding +initialize+:
|
|
33
|
+
#
|
|
34
|
+
# class MyValidator < Grape::Validations::Validators::Base
|
|
35
|
+
# default_message_key :my_error
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# The key is resolved through +message+, so a per-option +:message+
|
|
39
|
+
# override still takes precedence.
|
|
40
|
+
def default_message_key(key = nil)
|
|
41
|
+
if key
|
|
42
|
+
@default_message_key = key
|
|
43
|
+
else
|
|
44
|
+
@default_message_key || (superclass.respond_to?(:default_message_key) ? superclass.default_message_key : nil)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def new(...)
|
|
49
|
+
super.freeze
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def inherited(klass)
|
|
53
|
+
super
|
|
54
|
+
Validations.register(klass)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
9
58
|
# Creates a new Validator from options specified
|
|
10
59
|
# by a +requires+ or +optional+ directive during
|
|
11
60
|
# parameter definition.
|
|
12
61
|
# @param attrs [Array] names of attributes to which the Validator applies
|
|
13
|
-
# @param options [Object] implementation-dependent Validator options
|
|
62
|
+
# @param options [Object] implementation-dependent Validator options; deep-frozen on assignment
|
|
14
63
|
# @param required [Boolean] attribute(s) are required or optional
|
|
15
64
|
# @param scope [ParamsScope] parent scope for this Validator
|
|
16
|
-
# @param opts [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@
|
|
65
|
+
# @param opts [Hash] shared validator options; only +:allow_blank+ and
|
|
66
|
+
# +:fail_fast+ are consulted (other keys ignored, as before)
|
|
67
|
+
def initialize(attrs, options, required, scope, opts)
|
|
68
|
+
@attrs = Array(attrs).freeze
|
|
69
|
+
@options = Grape::Util::DeepFreeze.deep_freeze(options)
|
|
70
|
+
@option = @options # TODO: remove in next major release
|
|
20
71
|
@required = required
|
|
21
72
|
@scope = scope
|
|
22
|
-
opts =
|
|
23
|
-
@
|
|
24
|
-
@
|
|
73
|
+
@opts = SharedOptions.new(**opts.slice(:allow_blank, :fail_fast))
|
|
74
|
+
@exception_message = message(self.class.default_message_key) if self.class.default_message_key
|
|
75
|
+
@iterator = iterator_class.new(@attrs, @scope).freeze
|
|
25
76
|
end
|
|
26
77
|
|
|
27
78
|
# Validates a given request.
|
|
@@ -30,62 +81,95 @@ module Grape
|
|
|
30
81
|
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
31
82
|
# @return [void]
|
|
32
83
|
def validate(request)
|
|
33
|
-
return unless
|
|
84
|
+
return unless scope.should_validate?(request.params)
|
|
34
85
|
|
|
35
86
|
validate!(request.params)
|
|
36
87
|
end
|
|
37
88
|
|
|
38
89
|
# Validates a given parameter hash.
|
|
39
|
-
# @note Override #
|
|
90
|
+
# @note Override #validate_param! for per-parameter validation,
|
|
91
|
+
# or #validate if you need access to the entire request.
|
|
40
92
|
# @param params [Hash] parameters to validate
|
|
41
93
|
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
42
94
|
# @return [void]
|
|
43
95
|
def validate!(params)
|
|
44
|
-
attributes = SingleAttributeIterator.new(self, @scope, params)
|
|
45
96
|
# we collect errors inside array because
|
|
46
97
|
# there may be more than one error per field
|
|
47
|
-
array_errors =
|
|
98
|
+
array_errors = nil
|
|
48
99
|
|
|
49
|
-
|
|
50
|
-
next if
|
|
51
|
-
next unless
|
|
100
|
+
@iterator.each(params) do |val, attr_name, empty_val|
|
|
101
|
+
next if !scope.required? && empty_val
|
|
102
|
+
next unless scope.meets_dependency?(val, params)
|
|
52
103
|
|
|
53
|
-
validate_param!(attr_name, val) if
|
|
104
|
+
validate_param!(attr_name, val) if required? || (hash_like?(val) && val.key?(attr_name))
|
|
54
105
|
rescue Grape::Exceptions::Validation => e
|
|
55
|
-
array_errors << e
|
|
106
|
+
(array_errors ||= []) << e
|
|
56
107
|
end
|
|
57
108
|
|
|
58
|
-
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors
|
|
109
|
+
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
protected
|
|
113
|
+
|
|
114
|
+
# Validates a single attribute. Override in subclasses.
|
|
115
|
+
# @param attr_name [Symbol, String] the attribute name
|
|
116
|
+
# @param params [Hash] the parameter hash containing the attribute
|
|
117
|
+
# @raise [Grape::Exceptions::Validation] if validation failed
|
|
118
|
+
# @return [void]
|
|
119
|
+
def validate_param!(attr_name, params)
|
|
120
|
+
raise NotImplementedError
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
|
|
125
|
+
attr_reader :options, :scope, :required, :exception_message
|
|
126
|
+
|
|
127
|
+
alias required? required
|
|
128
|
+
|
|
129
|
+
# The AttributesIterator subclass used to walk this validator's
|
|
130
|
+
# attributes. Built once in #initialize and reused across requests.
|
|
131
|
+
def iterator_class
|
|
132
|
+
SingleAttributeIterator
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def validation_error!(attr_name_or_params, message = exception_message)
|
|
136
|
+
params = attr_name_or_params.is_a?(Array) ? attr_name_or_params : scope.full_name(attr_name_or_params)
|
|
137
|
+
raise Grape::Exceptions::Validation.new(params:, message:)
|
|
59
138
|
end
|
|
60
139
|
|
|
61
|
-
def
|
|
62
|
-
|
|
140
|
+
def hash_like?(obj)
|
|
141
|
+
obj.respond_to?(:key?)
|
|
142
|
+
end
|
|
63
143
|
|
|
64
|
-
|
|
65
|
-
|
|
144
|
+
def options_key?(key, given_options = nil)
|
|
145
|
+
current_options = given_options || options
|
|
146
|
+
hash_like?(current_options) && current_options.key?(key) && !current_options[key].nil?
|
|
66
147
|
end
|
|
67
148
|
|
|
149
|
+
# Returns the effective message for a validation error.
|
|
150
|
+
# Prefers an explicit +:message+ option, then +default_key+.
|
|
151
|
+
# If both are nil, the block (if given) is called to compute a fallback —
|
|
152
|
+
# useful for validators that build a message Hash for deferred i18n interpolation.
|
|
153
|
+
# @example
|
|
154
|
+
# @exception_message = message(:presence) # symbol key or custom message
|
|
155
|
+
# @exception_message = message { build_hash_message } # computed fallback
|
|
68
156
|
def message(default_key = nil)
|
|
69
|
-
|
|
70
|
-
|
|
157
|
+
key = options_key?(:message) ? options[:message] : default_key
|
|
158
|
+
return key unless key.nil?
|
|
159
|
+
|
|
160
|
+
yield if block_given?
|
|
71
161
|
end
|
|
72
162
|
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
options.respond_to?(:key?) && options.key?(key) && !options[key].nil?
|
|
163
|
+
def option_value
|
|
164
|
+
options_key?(:value) ? options[:value] : options
|
|
76
165
|
end
|
|
77
166
|
|
|
78
|
-
def
|
|
79
|
-
|
|
167
|
+
def scrub(value)
|
|
168
|
+
return value if !value.respond_to?(:valid_encoding?) || value.valid_encoding?
|
|
169
|
+
|
|
170
|
+
value.scrub
|
|
80
171
|
end
|
|
81
172
|
end
|
|
82
173
|
end
|
|
83
174
|
end
|
|
84
175
|
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,29 @@ 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
|
|
|
17
24
|
def validate_param!(attr_name, params)
|
|
18
|
-
|
|
25
|
+
validation_error!(attr_name) unless hash_like?(params)
|
|
19
26
|
|
|
20
27
|
new_value = coerce_value(params[attr_name])
|
|
21
28
|
|
|
22
|
-
|
|
29
|
+
validation_error!(attr_name, new_value.message || exception_message) if new_value.is_a?(Types::InvalidValue)
|
|
23
30
|
|
|
24
31
|
# Don't assign a value if it is identical. It fixes a problem with Hashie::Mash
|
|
25
32
|
# which looses wrappers for hashes and arrays after reassigning values
|
|
@@ -37,38 +44,13 @@ module Grape
|
|
|
37
44
|
|
|
38
45
|
private
|
|
39
46
|
|
|
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
|
-
|
|
47
|
+
# Calls the converter built at definition time.
|
|
48
|
+
# Custom coercers may raise; any StandardError is treated as an invalid value.
|
|
52
49
|
def coerce_value(val)
|
|
53
|
-
converter.call(val)
|
|
54
|
-
# Some custom types might fail, so it should be treated as an invalid value
|
|
50
|
+
@converter.call(val)
|
|
55
51
|
rescue StandardError
|
|
56
52
|
Types::InvalidValue.new
|
|
57
53
|
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
54
|
end
|
|
73
55
|
end
|
|
74
56
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grape
|
|
4
|
+
module Validations
|
|
5
|
+
module Validators
|
|
6
|
+
class ContractScopeValidator
|
|
7
|
+
attr_reader :schema
|
|
8
|
+
|
|
9
|
+
def initialize(schema:)
|
|
10
|
+
@schema = schema
|
|
11
|
+
freeze
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Validates a given request.
|
|
15
|
+
# @param request [Grape::Request] the request currently being handled
|
|
16
|
+
# @raise [Grape::Exceptions::ValidationArrayErrors] if validation failed
|
|
17
|
+
# @return [void]
|
|
18
|
+
def validate(request)
|
|
19
|
+
res = schema.call(request.params)
|
|
20
|
+
|
|
21
|
+
if res.success?
|
|
22
|
+
request.params.deep_merge!(res.to_h)
|
|
23
|
+
return
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
raise Grape::Exceptions::ValidationArrayErrors.new(build_errors_from_messages(res.errors.messages))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def fail_fast?
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def build_errors_from_messages(messages)
|
|
36
|
+
messages.map do |message|
|
|
37
|
+
full_name = message.path.first.to_s
|
|
38
|
+
full_name << "[#{message.path[1..].join('][')}]" if message.path.size > 1
|
|
39
|
+
Grape::Exceptions::Validation.new(params: full_name, message: message.text)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -4,27 +4,24 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class DefaultValidator < Base
|
|
7
|
-
def initialize(attrs, options, required, scope,
|
|
8
|
-
@default = options
|
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
|
9
8
|
super
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
end
|
|
9
|
+
# !important, lazy call at runtime
|
|
10
|
+
@default_call =
|
|
11
|
+
if options.is_a?(Proc)
|
|
12
|
+
options.arity.zero? ? proc { options.call } : options
|
|
13
|
+
elsif options.duplicable?
|
|
14
|
+
proc { options.dup }
|
|
15
|
+
else
|
|
16
|
+
proc { options }
|
|
17
|
+
end
|
|
20
18
|
end
|
|
21
19
|
|
|
22
20
|
def validate!(params)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
next unless @scope.meets_dependency?(resource_params, params)
|
|
21
|
+
@iterator.each(params) do |resource_params, attr_name|
|
|
22
|
+
next unless scope.meets_dependency?(resource_params, params)
|
|
26
23
|
|
|
27
|
-
|
|
24
|
+
resource_params[attr_name] = @default_call.call(resource_params) if hash_like?(resource_params) && resource_params[attr_name].nil?
|
|
28
25
|
end
|
|
29
26
|
end
|
|
30
27
|
end
|
|
@@ -4,12 +4,19 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class ExactlyOneOfValidator < MultipleParamsBase
|
|
7
|
+
def initialize(attrs, options, required, scope, opts)
|
|
8
|
+
super
|
|
9
|
+
@exactly_one_exception_message = message(:exactly_one)
|
|
10
|
+
@mutual_exclusion_exception_message = message(:mutual_exclusion)
|
|
11
|
+
end
|
|
12
|
+
|
|
7
13
|
def validate_params!(params)
|
|
8
|
-
|
|
14
|
+
known_keys = all_keys
|
|
15
|
+
keys = keys_in_common(params, known_keys)
|
|
9
16
|
return if keys.length == 1
|
|
10
|
-
raise Grape::Exceptions::Validation.new(params: all_keys, message: message(:exactly_one)) if keys.length.zero?
|
|
11
17
|
|
|
12
|
-
|
|
18
|
+
validation_error!(known_keys, @exactly_one_exception_message) if keys.empty?
|
|
19
|
+
validation_error!(keys, @mutual_exclusion_exception_message)
|
|
13
20
|
end
|
|
14
21
|
end
|
|
15
22
|
end
|
|
@@ -4,19 +4,29 @@ module Grape
|
|
|
4
4
|
module Validations
|
|
5
5
|
module Validators
|
|
6
6
|
class ExceptValuesValidator < Base
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
default_message_key :except_values
|
|
8
|
+
|
|
9
|
+
def initialize(attrs, options, required, scope, opts)
|
|
9
10
|
super
|
|
11
|
+
except = option_value
|
|
12
|
+
except_proc = except.is_a?(Proc)
|
|
13
|
+
raise ArgumentError, 'except_values Proc must have arity of zero (use values: with a one-arity predicate for per-element checks)' if except_proc && !except.arity.zero?
|
|
14
|
+
|
|
15
|
+
# Zero-arity procs (e.g. -> { User.pluck(:role) }) must be called per-request,
|
|
16
|
+
# not at definition time, so they are wrapped in a lambda to defer execution.
|
|
17
|
+
@excepts_call = except_proc ? except : -> { except }
|
|
10
18
|
end
|
|
11
19
|
|
|
12
20
|
def validate_param!(attr_name, params)
|
|
13
|
-
return unless
|
|
21
|
+
return unless hash_like?(params) && params.key?(attr_name)
|
|
14
22
|
|
|
15
|
-
excepts = @
|
|
23
|
+
excepts = @excepts_call.call
|
|
16
24
|
return if excepts.nil?
|
|
17
25
|
|
|
18
26
|
param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
|
|
19
|
-
|
|
27
|
+
return if param_array.none? { |param| excepts.include?(param) }
|
|
28
|
+
|
|
29
|
+
validation_error!(attr_name)
|
|
20
30
|
end
|
|
21
31
|
end
|
|
22
32
|
end
|