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
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'attributes_doc'
|
|
4
|
-
|
|
5
3
|
module Grape
|
|
6
4
|
module Validations
|
|
7
5
|
class ParamsScope
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
attr_reader :parent, :type, :nearest_array_ancestor, :full_path
|
|
7
|
+
|
|
8
|
+
def qualifying_params
|
|
9
|
+
ParamScopeTracker.current&.qualifying_params(self)
|
|
10
|
+
end
|
|
10
11
|
|
|
11
12
|
include Grape::DSL::Parameters
|
|
13
|
+
include Grape::Validations::ParamsDocumentation
|
|
12
14
|
|
|
13
15
|
# There are a number of documentation options on entities that don't have
|
|
14
16
|
# corresponding validators. Since there is nowhere that enumerates them all,
|
|
15
17
|
# we maintain a list of them here and skip looking up validators for them.
|
|
16
18
|
RESERVED_DOCUMENTATION_KEYWORDS = %i[as required param_type is_array format example].freeze
|
|
17
19
|
|
|
20
|
+
SPECIAL_JSON = [JSON, Array[JSON]].freeze
|
|
21
|
+
|
|
18
22
|
class Attr
|
|
19
|
-
|
|
23
|
+
attr_reader :key, :scope
|
|
20
24
|
|
|
21
25
|
# Open up a new ParamsScope::Attr
|
|
22
26
|
# @param key [Hash, Symbol] key of attr
|
|
@@ -34,9 +38,10 @@ module Grape
|
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def self.attr_key(declared_param_attr)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
case declared_param_attr
|
|
42
|
+
when self
|
|
43
|
+
attr_key(declared_param_attr.key)
|
|
44
|
+
when Hash
|
|
40
45
|
declared_param_attr.transform_values { |value| attrs_keys(value) }
|
|
41
46
|
else
|
|
42
47
|
declared_param_attr
|
|
@@ -46,39 +51,44 @@ module Grape
|
|
|
46
51
|
|
|
47
52
|
# Open up a new ParamsScope, allowing parameter definitions per
|
|
48
53
|
# Grape::DSL::Params.
|
|
49
|
-
# @param
|
|
50
|
-
# @
|
|
51
|
-
# this to be relevant,
|
|
52
|
-
# @
|
|
54
|
+
# @param api [API] the API endpoint to modify
|
|
55
|
+
# @param element [Symbol] the element that contains this scope; for
|
|
56
|
+
# this to be relevant, parent must be set
|
|
57
|
+
# @param element_renamed [Symbol, nil] whenever this scope should
|
|
53
58
|
# be renamed and to what, given +nil+ no renaming is done
|
|
54
|
-
# @
|
|
55
|
-
# @
|
|
56
|
-
# @option opts :optional [Boolean] whether or not this scope needs to have
|
|
59
|
+
# @param parent [ParamsScope] the scope containing this scope
|
|
60
|
+
# @param optional [Boolean] whether or not this scope needs to have
|
|
57
61
|
# any parameters set or not
|
|
58
|
-
# @
|
|
59
|
-
# @
|
|
60
|
-
# @
|
|
62
|
+
# @param type [Class] a type meant to govern this scope (deprecated)
|
|
63
|
+
# @param type [Hash] group options for this scope
|
|
64
|
+
# @param dependent_on [Symbol] if present, this scope should only
|
|
61
65
|
# validate if this param is present in the parent scope
|
|
62
66
|
# @yield the instance context, open for parameter definitions
|
|
63
|
-
def initialize(
|
|
64
|
-
@element =
|
|
65
|
-
@element_renamed =
|
|
66
|
-
@parent =
|
|
67
|
-
@api =
|
|
68
|
-
@optional =
|
|
69
|
-
@type =
|
|
70
|
-
@group =
|
|
71
|
-
@dependent_on =
|
|
67
|
+
def initialize(api:, element: nil, element_renamed: nil, parent: nil, optional: false, type: nil, group: nil, dependent_on: nil, &block)
|
|
68
|
+
@element = element
|
|
69
|
+
@element_renamed = element_renamed
|
|
70
|
+
@parent = parent
|
|
71
|
+
@api = api
|
|
72
|
+
@optional = optional
|
|
73
|
+
@type = type
|
|
74
|
+
@group = group
|
|
75
|
+
@dependent_on = dependent_on
|
|
76
|
+
# Must be an ivar: push_declared_params is dispatched on self during
|
|
77
|
+
# instance_eval, so local variables from initialize are unreachable.
|
|
78
|
+
# configure_declared_params consumes it and clears @declared_params to nil.
|
|
72
79
|
@declared_params = []
|
|
73
|
-
@
|
|
80
|
+
@full_path = build_full_path
|
|
74
81
|
|
|
75
82
|
instance_eval(&block) if block
|
|
76
83
|
|
|
77
84
|
configure_declared_params
|
|
85
|
+
@nearest_array_ancestor = find_nearest_array_ancestor
|
|
86
|
+
freeze
|
|
78
87
|
end
|
|
79
88
|
|
|
80
89
|
def configuration
|
|
81
|
-
|
|
90
|
+
config = @api.configuration
|
|
91
|
+
config.is_a?(Grape::Util::Lazy::Base) ? config.evaluate : config
|
|
82
92
|
end
|
|
83
93
|
|
|
84
94
|
# @return [Boolean] whether or not this entire scope needs to be
|
|
@@ -88,24 +98,26 @@ module Grape
|
|
|
88
98
|
|
|
89
99
|
return false if @optional && (scoped_params.blank? || all_element_blank?(scoped_params))
|
|
90
100
|
return false unless meets_dependency?(scoped_params, parameters)
|
|
91
|
-
return true if parent.nil?
|
|
101
|
+
return true if @parent.nil?
|
|
92
102
|
|
|
93
|
-
parent.should_validate?(parameters)
|
|
103
|
+
@parent.should_validate?(parameters)
|
|
94
104
|
end
|
|
95
105
|
|
|
96
106
|
def meets_dependency?(params, request_params)
|
|
97
107
|
return true unless @dependent_on
|
|
98
|
-
|
|
99
108
|
return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
|
|
100
109
|
|
|
101
|
-
|
|
110
|
+
if params.is_a?(Array)
|
|
111
|
+
filtered = params.flatten.filter { |param| meets_dependency?(param, request_params) }
|
|
112
|
+
ParamScopeTracker.current&.store_qualifying_params(self, filtered)
|
|
113
|
+
return filtered.present?
|
|
114
|
+
end
|
|
102
115
|
|
|
103
116
|
meets_hash_dependency?(params)
|
|
104
117
|
end
|
|
105
118
|
|
|
106
119
|
def attr_meets_dependency?(params)
|
|
107
120
|
return true unless @dependent_on
|
|
108
|
-
|
|
109
121
|
return false if @parent.present? && !@parent.attr_meets_dependency?(params)
|
|
110
122
|
|
|
111
123
|
meets_hash_dependency?(params)
|
|
@@ -115,28 +127,27 @@ module Grape
|
|
|
115
127
|
# params might be anything what looks like a hash, so it must implement a `key?` method
|
|
116
128
|
return false unless params.respond_to?(:key?)
|
|
117
129
|
|
|
118
|
-
@dependent_on.
|
|
130
|
+
@dependent_on.all? do |dependency|
|
|
119
131
|
if dependency.is_a?(Hash)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return false
|
|
132
|
+
key, callable = dependency.first
|
|
133
|
+
callable.call(params[key])
|
|
134
|
+
else
|
|
135
|
+
params[dependency].present?
|
|
125
136
|
end
|
|
126
137
|
end
|
|
127
|
-
|
|
128
|
-
true
|
|
129
138
|
end
|
|
130
139
|
|
|
131
140
|
# @return [String] the proper attribute name, with nesting considered.
|
|
132
141
|
def full_name(name, index: nil)
|
|
142
|
+
tracker = ParamScopeTracker.current
|
|
133
143
|
if nested?
|
|
134
144
|
# Find our containing element's name, and append ours.
|
|
135
|
-
|
|
145
|
+
resolved_index = index || tracker&.index_for(self)
|
|
146
|
+
"#{@parent.full_name(@element)}#{brackets(resolved_index)}#{brackets(name)}"
|
|
136
147
|
elsif lateral?
|
|
137
148
|
# Find the name of the element as if it was at the same nesting level
|
|
138
149
|
# as our parent. We need to forward our index upward to achieve this.
|
|
139
|
-
@parent.full_name(name, index:
|
|
150
|
+
@parent.full_name(name, index: tracker&.index_for(self))
|
|
140
151
|
else
|
|
141
152
|
# We must be the root scope, so no prefix needed.
|
|
142
153
|
name.to_s
|
|
@@ -176,26 +187,22 @@ module Grape
|
|
|
176
187
|
# Adds a parameter declaration to our list of validations.
|
|
177
188
|
# @param attrs [Array] (see Grape::DSL::Parameters#requires)
|
|
178
189
|
def push_declared_params(attrs, **opts)
|
|
179
|
-
opts =
|
|
180
|
-
if lateral?
|
|
181
|
-
@parent.push_declared_params(attrs, **opts)
|
|
182
|
-
else
|
|
183
|
-
push_renamed_param(full_path + [attrs.first], opts[:as]) \
|
|
184
|
-
if opts && opts[:as]
|
|
190
|
+
opts[:declared_params_scope] = self unless opts.key?(:declared_params_scope)
|
|
191
|
+
return @parent.push_declared_params(attrs, **opts) if lateral?
|
|
185
192
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# Get the full path of the parameter scope in the hierarchy.
|
|
191
|
-
#
|
|
192
|
-
# @return [Array<Symbol>] the nesting/path of the current parameter scope
|
|
193
|
-
def full_path
|
|
194
|
-
nested? ? @parent.full_path + [@element] : []
|
|
193
|
+
push_renamed_param(full_path + [attrs.first], opts[:as]) if opts[:as]
|
|
194
|
+
@declared_params.concat(attrs.map { |attr| ::Grape::Validations::ParamsScope::Attr.new(attr, opts[:declared_params_scope]) })
|
|
195
195
|
end
|
|
196
196
|
|
|
197
197
|
private
|
|
198
198
|
|
|
199
|
+
def build_full_path
|
|
200
|
+
return @parent.full_path + [@element] if nested?
|
|
201
|
+
return @parent.full_path if lateral?
|
|
202
|
+
|
|
203
|
+
[]
|
|
204
|
+
end
|
|
205
|
+
|
|
199
206
|
# Add a new parameter which should be renamed when using the +#declared+
|
|
200
207
|
# method.
|
|
201
208
|
#
|
|
@@ -204,331 +211,201 @@ module Grape
|
|
|
204
211
|
# @param new_name [String, Symbol] the new name of the parameter (the
|
|
205
212
|
# renamed name, with the +as: ...+ semantic)
|
|
206
213
|
def push_renamed_param(path, new_name)
|
|
207
|
-
|
|
214
|
+
api_route_setting = @api.inheritable_setting.route
|
|
215
|
+
base = api_route_setting[:renamed_params] || {}
|
|
208
216
|
base[Array(path).map(&:to_s)] = new_name.to_s
|
|
209
|
-
|
|
217
|
+
api_route_setting[:renamed_params] = base
|
|
210
218
|
end
|
|
211
219
|
|
|
212
|
-
def require_required_and_optional_fields(context,
|
|
220
|
+
def require_required_and_optional_fields(context, using:, except: nil)
|
|
221
|
+
except_fields = Array.wrap(except)
|
|
222
|
+
using_fields = using.keys.delete_if { |f| except_fields.include?(f) }
|
|
223
|
+
|
|
213
224
|
if context == :all
|
|
214
|
-
optional_fields =
|
|
215
|
-
required_fields =
|
|
225
|
+
optional_fields = except_fields
|
|
226
|
+
required_fields = using_fields
|
|
216
227
|
else # context == :none
|
|
217
|
-
required_fields =
|
|
218
|
-
optional_fields =
|
|
228
|
+
required_fields = except_fields
|
|
229
|
+
optional_fields = using_fields
|
|
219
230
|
end
|
|
220
231
|
required_fields.each do |field|
|
|
221
|
-
field_opts =
|
|
232
|
+
field_opts = using[field]
|
|
222
233
|
raise ArgumentError, "required field not exist: #{field}" unless field_opts
|
|
223
234
|
|
|
224
|
-
requires(field, field_opts)
|
|
235
|
+
requires(field, **field_opts)
|
|
225
236
|
end
|
|
226
237
|
optional_fields.each do |field|
|
|
227
|
-
field_opts =
|
|
228
|
-
optional(field, field_opts) if field_opts
|
|
238
|
+
field_opts = using[field]
|
|
239
|
+
optional(field, **field_opts) if field_opts
|
|
229
240
|
end
|
|
230
241
|
end
|
|
231
242
|
|
|
232
|
-
def require_optional_fields(context,
|
|
233
|
-
optional_fields =
|
|
234
|
-
|
|
243
|
+
def require_optional_fields(context, using:, except: nil)
|
|
244
|
+
optional_fields = using.keys
|
|
245
|
+
unless context == :all
|
|
246
|
+
except_fields = Array.wrap(except)
|
|
247
|
+
optional_fields.delete_if { |f| except_fields.include?(f) }
|
|
248
|
+
end
|
|
235
249
|
optional_fields.each do |field|
|
|
236
|
-
field_opts =
|
|
237
|
-
optional(field, field_opts) if field_opts
|
|
250
|
+
field_opts = using[field]
|
|
251
|
+
optional(field, **field_opts) if field_opts
|
|
238
252
|
end
|
|
239
253
|
end
|
|
240
254
|
|
|
241
|
-
def validate_attributes(attrs, opts, &block)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
validates(attrs, validations)
|
|
255
|
+
def validate_attributes(attrs, **opts, &block)
|
|
256
|
+
opts[:type] ||= Array if block
|
|
257
|
+
validates(attrs, opts)
|
|
245
258
|
end
|
|
246
259
|
|
|
247
260
|
# Returns a new parameter scope, subordinate to the current one and nested
|
|
248
|
-
# under the
|
|
249
|
-
# @param
|
|
250
|
-
#
|
|
251
|
-
# @param
|
|
261
|
+
# under the given element.
|
|
262
|
+
# @param element [Symbol] the parameter name under which this scope is nested
|
|
263
|
+
# @param type [Class] the type governing this scope
|
|
264
|
+
# @param as [Symbol, nil] optional renamed name for the element
|
|
265
|
+
# @param optional [Boolean] whether the parameter this scope is nested under
|
|
252
266
|
# is optional or not (and hence, whether this block's params will be).
|
|
253
267
|
# @yield parameter scope
|
|
254
|
-
def new_scope(
|
|
268
|
+
def new_scope(element, type:, as:, optional: false, &)
|
|
255
269
|
# if required params are grouped and no type or unsupported type is provided, raise an error
|
|
256
|
-
|
|
257
|
-
if attrs.first && !optional
|
|
270
|
+
if element && !optional
|
|
258
271
|
raise Grape::Exceptions::MissingGroupType if type.nil?
|
|
259
272
|
raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
|
|
260
273
|
end
|
|
261
274
|
|
|
262
275
|
self.class.new(
|
|
263
276
|
api: @api,
|
|
264
|
-
element
|
|
265
|
-
element_renamed:
|
|
277
|
+
element:,
|
|
278
|
+
element_renamed: as,
|
|
266
279
|
parent: self,
|
|
267
|
-
optional
|
|
280
|
+
optional:,
|
|
268
281
|
type: type || Array,
|
|
269
|
-
|
|
282
|
+
group: @group,
|
|
283
|
+
&
|
|
270
284
|
)
|
|
271
285
|
end
|
|
272
286
|
|
|
273
287
|
# Returns a new parameter scope, not nested under any current-level param
|
|
274
288
|
# but instead at the same level as the current scope.
|
|
275
|
-
# @param
|
|
276
|
-
#
|
|
277
|
-
# scope should only validate if this parameter from the above scope is
|
|
278
|
-
# present
|
|
289
|
+
# @param dependent_on [Symbol] if given, specifies that this scope should
|
|
290
|
+
# only validate if this parameter from the above scope is present
|
|
279
291
|
# @yield parameter scope
|
|
280
|
-
def new_lateral_scope(
|
|
292
|
+
def new_lateral_scope(dependent_on:, &)
|
|
281
293
|
self.class.new(
|
|
282
294
|
api: @api,
|
|
283
|
-
element: nil,
|
|
284
295
|
parent: self,
|
|
285
|
-
|
|
296
|
+
optional: @optional,
|
|
286
297
|
type: type == Array ? Array : Hash,
|
|
287
|
-
dependent_on
|
|
288
|
-
&
|
|
298
|
+
dependent_on:,
|
|
299
|
+
&
|
|
289
300
|
)
|
|
290
301
|
end
|
|
291
302
|
|
|
292
|
-
# Returns a new parameter scope, subordinate to the current one
|
|
293
|
-
#
|
|
294
|
-
# @param
|
|
295
|
-
# `optional` invocation that opened this scope.
|
|
303
|
+
# Returns a new parameter scope, subordinate to the current one, sharing
|
|
304
|
+
# the given group options with all parameters defined within.
|
|
305
|
+
# @param group [Hash] common options to merge into each parameter in the scope
|
|
296
306
|
# @yield parameter scope
|
|
297
|
-
def new_group_scope(
|
|
298
|
-
self.class.new(
|
|
299
|
-
api: @api,
|
|
300
|
-
parent: self,
|
|
301
|
-
group: attrs.first,
|
|
302
|
-
&block
|
|
303
|
-
)
|
|
307
|
+
def new_group_scope(group, &)
|
|
308
|
+
self.class.new(api: @api, parent: self, group:, &)
|
|
304
309
|
end
|
|
305
310
|
|
|
306
|
-
# Pushes declared params to parent or settings
|
|
311
|
+
# Pushes declared params to parent or settings, then clears @declared_params.
|
|
312
|
+
# Clearing here (rather than in initialize) keeps the lifecycle ownership in
|
|
313
|
+
# one place: this method both consumes and invalidates the ivar so that
|
|
314
|
+
# push_declared_params cannot be called on the frozen scope later.
|
|
307
315
|
def configure_declared_params
|
|
308
316
|
push_renamed_param(full_path, @element_renamed) if @element_renamed
|
|
317
|
+
return @parent.push_declared_params [{ @element => @declared_params }] if nested?
|
|
309
318
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
else
|
|
313
|
-
@api.namespace_stackable(:declared_params, @declared_params)
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
# params were stored in settings, it can be cleaned from the params scope
|
|
319
|
+
@api.inheritable_setting.namespace_stackable[:declared_params] = @declared_params
|
|
320
|
+
ensure
|
|
317
321
|
@declared_params = nil
|
|
318
322
|
end
|
|
319
323
|
|
|
320
|
-
def
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
doc.type = coerce_type
|
|
327
|
-
|
|
328
|
-
default = validations[:default]
|
|
329
|
-
|
|
330
|
-
if (values_hash = validations[:values]).is_a? Hash
|
|
331
|
-
values = values_hash[:value]
|
|
332
|
-
# NB: excepts is deprecated
|
|
333
|
-
excepts = values_hash[:except]
|
|
334
|
-
else
|
|
335
|
-
values = validations[:values]
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
doc.values = values
|
|
339
|
-
|
|
340
|
-
except_values = options_key?(:except_values, :value, validations) ? validations[:except_values][:value] : validations[:except_values]
|
|
341
|
-
|
|
342
|
-
# NB. values and excepts should be nil, Proc, Array, or Range.
|
|
343
|
-
# Specifically, values should NOT be a Hash
|
|
344
|
-
|
|
345
|
-
# use values or excepts to guess coerce type when stated type is Array
|
|
346
|
-
coerce_type = guess_coerce_type(coerce_type, values, except_values, excepts)
|
|
347
|
-
|
|
348
|
-
# default value should be present in values array, if both exist and are not procs
|
|
349
|
-
check_incompatible_option_values(default, values, except_values, excepts)
|
|
350
|
-
|
|
351
|
-
# type should be compatible with values array, if both exist
|
|
352
|
-
validate_value_coercion(coerce_type, values, except_values, excepts)
|
|
353
|
-
|
|
354
|
-
doc.document attrs
|
|
324
|
+
def find_nearest_array_ancestor
|
|
325
|
+
scope = @parent
|
|
326
|
+
scope = scope.parent while scope && scope.type != Array
|
|
327
|
+
scope
|
|
328
|
+
end
|
|
355
329
|
|
|
356
|
-
|
|
330
|
+
def validates(attrs, validations)
|
|
331
|
+
process_oneof!(validations) if validations.key?(:oneof)
|
|
332
|
+
spec = ValidationsSpec.from(validations)
|
|
357
333
|
|
|
358
|
-
|
|
359
|
-
validates_presence(validations, attrs, doc, opts)
|
|
334
|
+
document_params(attrs, spec)
|
|
360
335
|
|
|
361
|
-
#
|
|
362
|
-
#
|
|
363
|
-
|
|
364
|
-
coerce_type validations, attrs, doc, opts
|
|
336
|
+
# Presence runs first — `required` is forwarded to every subsequent
|
|
337
|
+
# validator (some short-circuit on it).
|
|
338
|
+
validate_presence(spec, attrs)
|
|
365
339
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
next if RESERVED_DOCUMENTATION_KEYWORDS.include?(type)
|
|
340
|
+
# Coerce runs second — later validators see the typed value.
|
|
341
|
+
validate_coerce(spec, attrs)
|
|
369
342
|
|
|
370
|
-
|
|
343
|
+
spec.validator_entries.each do |type, options|
|
|
344
|
+
validate(type, options, attrs, spec.required?, spec.shared_opts)
|
|
371
345
|
end
|
|
372
346
|
end
|
|
373
347
|
|
|
374
|
-
#
|
|
375
|
-
#
|
|
376
|
-
#
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
# options.
|
|
382
|
-
#
|
|
383
|
-
# @param validations [Hash] list of validations supplied to the
|
|
384
|
-
# parameter declaration
|
|
385
|
-
# @return [class-like] type to which the parameter will be coerced
|
|
386
|
-
# @raise [ArgumentError] if the given type options are invalid
|
|
387
|
-
def infer_coercion(validations)
|
|
388
|
-
raise ArgumentError, ':type may not be supplied with :types' if validations.key?(:type) && validations.key?(:types)
|
|
389
|
-
|
|
390
|
-
validations[:coerce] = (options_key?(:type, :value, validations) ? validations[:type][:value] : validations[:type]) if validations.key?(:type)
|
|
391
|
-
validations[:coerce_message] = (options_key?(:type, :message, validations) ? validations[:type][:message] : nil) if validations.key?(:type)
|
|
392
|
-
validations[:coerce] = (options_key?(:types, :value, validations) ? validations[:types][:value] : validations[:types]) if validations.key?(:types)
|
|
393
|
-
validations[:coerce_message] = (options_key?(:types, :message, validations) ? validations[:types][:message] : nil) if validations.key?(:types)
|
|
394
|
-
|
|
395
|
-
validations.delete(:types) if validations.key?(:types)
|
|
396
|
-
|
|
397
|
-
coerce_type = validations[:coerce]
|
|
398
|
-
|
|
399
|
-
# Special case - when the argument is a single type that is a
|
|
400
|
-
# variant-type collection.
|
|
401
|
-
if Types.multiple?(coerce_type) && validations.key?(:type)
|
|
402
|
-
validations[:coerce] = Types::VariantCollectionCoercer.new(
|
|
403
|
-
coerce_type,
|
|
404
|
-
validations.delete(:coerce_with)
|
|
405
|
-
)
|
|
406
|
-
end
|
|
407
|
-
validations.delete(:type)
|
|
408
|
-
|
|
409
|
-
coerce_type
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
# Enforce correct usage of :coerce_with parameter.
|
|
413
|
-
# We do not allow coercion without a type, nor with
|
|
414
|
-
# +JSON+ as a type since this defines its own coercion
|
|
415
|
-
# method.
|
|
416
|
-
def check_coerce_with(validations)
|
|
417
|
-
return unless validations.key?(:coerce_with)
|
|
418
|
-
# type must be supplied for coerce_with..
|
|
419
|
-
raise ArgumentError, 'must supply type for coerce_with' unless validations.key?(:coerce)
|
|
420
|
-
|
|
421
|
-
# but not special JSON types, which
|
|
422
|
-
# already imply coercion method
|
|
423
|
-
return if [JSON, Array[JSON]].exclude? validations[:coerce]
|
|
348
|
+
# Enforce correct usage of :coerce_with on a CoerceOptions.
|
|
349
|
+
# We do not allow coercion without a type, nor with +JSON+ as a type
|
|
350
|
+
# since that defines its own coercion method.
|
|
351
|
+
def check_coerce_with(coerce_options)
|
|
352
|
+
return unless coerce_options.coerce_method
|
|
353
|
+
raise ArgumentError, 'must supply type for coerce_with' unless coerce_options.type
|
|
354
|
+
return unless SPECIAL_JSON.include?(coerce_options.type)
|
|
424
355
|
|
|
425
356
|
raise ArgumentError, 'coerce_with disallowed for type: JSON'
|
|
426
357
|
end
|
|
427
358
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
# This validation has special handling since it is
|
|
431
|
-
# composited from more than one +requires+/+optional+
|
|
432
|
-
# parameter, and needs to be run before most other
|
|
433
|
-
# validations.
|
|
434
|
-
def coerce_type(validations, attrs, doc, opts)
|
|
435
|
-
check_coerce_with(validations)
|
|
359
|
+
def validate_presence(spec, attrs)
|
|
360
|
+
return unless spec.required?
|
|
436
361
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
coerce_options = {
|
|
440
|
-
type: validations[:coerce],
|
|
441
|
-
method: validations[:coerce_with],
|
|
442
|
-
message: validations[:coerce_message]
|
|
443
|
-
}
|
|
444
|
-
validate('coerce', coerce_options, attrs, doc, opts)
|
|
445
|
-
validations.delete(:coerce_with)
|
|
446
|
-
validations.delete(:coerce)
|
|
447
|
-
validations.delete(:coerce_message)
|
|
362
|
+
validate('presence', spec.presence_options, attrs, true, spec.shared_opts)
|
|
448
363
|
end
|
|
449
364
|
|
|
450
|
-
def
|
|
451
|
-
|
|
365
|
+
def validate_coerce(spec, attrs)
|
|
366
|
+
coerce_options = spec.coerce_options
|
|
367
|
+
check_coerce_with(coerce_options)
|
|
368
|
+
# Falsy check is intentional: when a remountable API is first evaluated
|
|
369
|
+
# on its base instance (no configuration supplied yet),
|
|
370
|
+
# configuration[:some_type] evaluates to nil. Skipping instantiation
|
|
371
|
+
# here is correct — the real mounted instance will replay this step
|
|
372
|
+
# with the actual type value.
|
|
373
|
+
return unless coerce_options.type
|
|
452
374
|
|
|
453
|
-
|
|
454
|
-
next if !values || values.is_a?(Proc)
|
|
455
|
-
return values.first.class if values.is_a?(Range) || !values.empty?
|
|
456
|
-
end
|
|
457
|
-
coerce_type
|
|
375
|
+
validate('coerce', coerce_options, attrs, spec.required?, spec.shared_opts)
|
|
458
376
|
end
|
|
459
377
|
|
|
460
|
-
|
|
461
|
-
|
|
378
|
+
# Translate a `oneof: [proc, proc, ...]` declaration into a list of
|
|
379
|
+
# captured validator arrays — one array per variant. Each variant's
|
|
380
|
+
# block is evaluated in its own +ParamsScope+ backed by an
|
|
381
|
+
# {OneofCollector} so the full params DSL is available inside variants
|
|
382
|
+
# and the resulting validators are kept out of the real API's
|
|
383
|
+
# registration list.
|
|
384
|
+
def process_oneof!(validations)
|
|
385
|
+
raise ArgumentError, 'oneof: requires type: Hash' unless validations[:type] == Hash
|
|
462
386
|
|
|
463
|
-
|
|
387
|
+
variants = validations[:oneof]
|
|
388
|
+
raise ArgumentError, 'oneof: must be a non-empty Array of blocks' unless variants.is_a?(Array) && variants.any?
|
|
389
|
+
raise ArgumentError, 'oneof: each variant must be a Proc' unless variants.all?(Proc)
|
|
464
390
|
|
|
465
|
-
|
|
466
|
-
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values) \
|
|
467
|
-
|
|
468
|
-
end
|
|
469
|
-
|
|
470
|
-
return unless excepts && !excepts.is_a?(Proc)
|
|
471
|
-
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, excepts) \
|
|
472
|
-
unless Array(default).none? { |def_val| excepts.include?(def_val) }
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
def validate(type, options, attrs, doc, opts)
|
|
476
|
-
validator_options = {
|
|
477
|
-
attributes: attrs,
|
|
478
|
-
options: options,
|
|
479
|
-
required: doc.required,
|
|
480
|
-
params_scope: self,
|
|
481
|
-
opts: opts,
|
|
482
|
-
validator_class: Validations.require_validator(type)
|
|
483
|
-
}
|
|
484
|
-
@api.namespace_stackable(:validations, validator_options)
|
|
485
|
-
end
|
|
486
|
-
|
|
487
|
-
def validate_value_coercion(coerce_type, *values_list)
|
|
488
|
-
return unless coerce_type
|
|
489
|
-
|
|
490
|
-
coerce_type = coerce_type.first if coerce_type.is_a?(Array)
|
|
491
|
-
values_list.each do |values|
|
|
492
|
-
next if !values || values.is_a?(Proc)
|
|
493
|
-
|
|
494
|
-
value_types = values.is_a?(Range) ? [values.begin, values.end].compact : values
|
|
495
|
-
value_types = value_types.map { |type| Grape::API::Boolean.build(type) } if coerce_type == Grape::API::Boolean
|
|
496
|
-
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values) unless value_types.all?(coerce_type)
|
|
497
|
-
end
|
|
498
|
-
end
|
|
499
|
-
|
|
500
|
-
def extract_message_option(attrs)
|
|
501
|
-
return nil unless attrs.is_a?(Array)
|
|
502
|
-
|
|
503
|
-
opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
|
|
504
|
-
opts.key?(:message) && !opts[:message].nil? ? opts.delete(:message) : nil
|
|
391
|
+
validations[:oneof] = variants.map { |block| OneofCollector.collect(block) }
|
|
505
392
|
end
|
|
506
393
|
|
|
507
|
-
def
|
|
508
|
-
|
|
394
|
+
def validate(type, options, attrs, required, opts)
|
|
395
|
+
validator_class = Validations.require_validator(type)
|
|
396
|
+
validator_instance = validator_class.new(
|
|
397
|
+
attrs,
|
|
398
|
+
options,
|
|
399
|
+
required,
|
|
400
|
+
self,
|
|
401
|
+
opts
|
|
402
|
+
)
|
|
403
|
+
@api.inheritable_setting.namespace_stackable[:validations] = validator_instance
|
|
509
404
|
end
|
|
510
405
|
|
|
511
406
|
def all_element_blank?(scoped_params)
|
|
512
407
|
scoped_params.respond_to?(:all?) && scoped_params.all?(&:blank?)
|
|
513
408
|
end
|
|
514
|
-
|
|
515
|
-
# Validators don't have access to each other and they don't need, however,
|
|
516
|
-
# some validators might influence others, so their options should be shared
|
|
517
|
-
def derive_validator_options(validations)
|
|
518
|
-
allow_blank = validations[:allow_blank]
|
|
519
|
-
|
|
520
|
-
{
|
|
521
|
-
allow_blank: allow_blank.is_a?(Hash) ? allow_blank[:value] : allow_blank,
|
|
522
|
-
fail_fast: validations.delete(:fail_fast) || false
|
|
523
|
-
}
|
|
524
|
-
end
|
|
525
|
-
|
|
526
|
-
def validates_presence(validations, attrs, doc, opts)
|
|
527
|
-
return unless validations.key?(:presence) && validations[:presence]
|
|
528
|
-
|
|
529
|
-
validate(:presence, validations.delete(:presence), attrs, doc, opts)
|
|
530
|
-
validations.delete(:message) if validations.key?(:message)
|
|
531
|
-
end
|
|
532
409
|
end
|
|
533
410
|
end
|
|
534
411
|
end
|