grape 1.1.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +370 -44
- data/CONTRIBUTING.md +32 -1
- data/LICENSE +1 -1
- data/README.md +683 -87
- data/UPGRADING.md +481 -17
- data/grape.gemspec +15 -4
- data/lib/grape/api/helpers.rb +2 -0
- data/lib/grape/api/instance.rb +279 -0
- data/lib/grape/api.rb +144 -176
- data/lib/grape/config.rb +34 -0
- data/lib/grape/content_types.rb +34 -0
- data/lib/grape/cookies.rb +4 -0
- data/lib/grape/dry_types.rb +12 -0
- data/lib/grape/dsl/api.rb +1 -1
- data/lib/grape/dsl/callbacks.rb +21 -1
- data/lib/grape/dsl/configuration.rb +1 -1
- data/lib/grape/dsl/desc.rb +41 -23
- data/lib/grape/dsl/headers.rb +7 -2
- data/lib/grape/dsl/helpers.rb +10 -7
- data/lib/grape/dsl/inside_route.rb +118 -62
- data/lib/grape/dsl/logger.rb +2 -0
- data/lib/grape/dsl/middleware.rb +11 -4
- data/lib/grape/dsl/parameters.rb +33 -19
- data/lib/grape/dsl/request_response.rb +12 -9
- data/lib/grape/dsl/routing.rb +22 -13
- data/lib/grape/dsl/settings.rb +10 -6
- data/lib/grape/dsl/validations.rb +19 -14
- data/lib/grape/eager_load.rb +20 -0
- data/lib/grape/endpoint.rb +67 -58
- data/lib/grape/error_formatter/base.rb +2 -0
- data/lib/grape/error_formatter/json.rb +11 -7
- data/lib/grape/error_formatter/txt.rb +2 -0
- data/lib/grape/error_formatter/xml.rb +4 -6
- data/lib/grape/error_formatter.rb +4 -2
- data/lib/grape/exceptions/base.rb +23 -16
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
- data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
- data/lib/grape/exceptions/invalid_formatter.rb +2 -0
- data/lib/grape/exceptions/invalid_message_body.rb +2 -0
- data/lib/grape/exceptions/invalid_response.rb +11 -0
- data/lib/grape/exceptions/invalid_version_header.rb +2 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
- data/lib/grape/exceptions/method_not_allowed.rb +2 -0
- data/lib/grape/exceptions/missing_group_type.rb +10 -1
- data/lib/grape/exceptions/missing_mime_type.rb +2 -0
- data/lib/grape/exceptions/missing_option.rb +2 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
- data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
- data/lib/grape/exceptions/unknown_options.rb +2 -0
- data/lib/grape/exceptions/unknown_parameter.rb +2 -0
- data/lib/grape/exceptions/unknown_validator.rb +2 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +10 -1
- data/lib/grape/exceptions/validation.rb +5 -8
- data/lib/grape/exceptions/validation_array_errors.rb +2 -0
- data/lib/grape/exceptions/validation_errors.rb +16 -13
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
- data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
- data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
- data/lib/grape/extensions/hash.rb +2 -0
- data/lib/grape/extensions/hashie/mash.rb +2 -0
- data/lib/grape/formatter/json.rb +3 -0
- data/lib/grape/formatter/serializable_hash.rb +4 -1
- data/lib/grape/formatter/txt.rb +2 -0
- data/lib/grape/formatter/xml.rb +3 -0
- data/lib/grape/formatter.rb +5 -3
- data/lib/grape/http/headers.rb +50 -18
- data/lib/grape/locale/en.yml +11 -8
- data/lib/grape/middleware/auth/base.rb +7 -7
- data/lib/grape/middleware/auth/dsl.rb +9 -2
- data/lib/grape/middleware/auth/strategies.rb +2 -0
- data/lib/grape/middleware/auth/strategy_info.rb +2 -0
- data/lib/grape/middleware/base.rb +13 -8
- data/lib/grape/middleware/error.rb +22 -17
- data/lib/grape/middleware/filter.rb +2 -0
- data/lib/grape/middleware/formatter.rb +12 -10
- data/lib/grape/middleware/globals.rb +2 -0
- data/lib/grape/middleware/helpers.rb +12 -0
- data/lib/grape/middleware/stack.rb +16 -6
- data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
- data/lib/grape/middleware/versioner/header.rb +13 -9
- data/lib/grape/middleware/versioner/param.rb +4 -1
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
- data/lib/grape/middleware/versioner/path.rb +5 -1
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/namespace.rb +14 -2
- data/lib/grape/parser/json.rb +3 -1
- data/lib/grape/parser/xml.rb +3 -1
- data/lib/grape/parser.rb +4 -2
- data/lib/grape/path.rb +16 -3
- data/lib/grape/presenters/presenter.rb +2 -0
- data/lib/grape/request.rb +21 -9
- data/lib/grape/router/attribute_translator.rb +41 -8
- data/lib/grape/router/pattern.rb +21 -17
- data/lib/grape/router/route.rb +15 -29
- data/lib/grape/router.rb +36 -29
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
- data/lib/grape/types/invalid_value.rb +8 -0
- data/lib/grape/util/base_inheritable.rb +43 -0
- data/lib/grape/util/cache.rb +20 -0
- data/lib/grape/util/endpoint_configuration.rb +8 -0
- data/lib/grape/util/env.rb +19 -17
- data/lib/grape/util/inheritable_setting.rb +3 -3
- data/lib/grape/util/inheritable_values.rb +7 -25
- data/lib/grape/util/json.rb +4 -0
- data/lib/grape/util/lazy_block.rb +27 -0
- data/lib/grape/util/lazy_object.rb +43 -0
- data/lib/grape/util/lazy_value.rb +99 -0
- data/lib/grape/util/registrable.rb +2 -0
- data/lib/grape/util/reverse_stackable_values.rb +10 -35
- data/lib/grape/util/stackable_values.rb +21 -34
- data/lib/grape/util/strict_hash_configuration.rb +3 -1
- data/lib/grape/util/xml.rb +2 -0
- data/lib/grape/validations/attributes_doc.rb +58 -0
- data/lib/grape/validations/attributes_iterator.rb +16 -6
- data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
- data/lib/grape/validations/params_scope.rb +174 -94
- data/lib/grape/validations/single_attribute_iterator.rb +24 -0
- data/lib/grape/validations/types/array_coercer.rb +63 -0
- data/lib/grape/validations/types/build_coercer.rb +47 -49
- data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
- data/lib/grape/validations/types/dry_type_coercer.rb +72 -0
- data/lib/grape/validations/types/file.rb +22 -18
- data/lib/grape/validations/types/invalid_value.rb +17 -0
- data/lib/grape/validations/types/json.rb +47 -39
- data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
- data/lib/grape/validations/types/primitive_coercer.rb +75 -0
- data/lib/grape/validations/types/set_coercer.rb +38 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
- data/lib/grape/validations/types.rb +106 -63
- data/lib/grape/validations/validator_factory.rb +8 -11
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
- data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
- data/lib/grape/validations/validators/as_validator.rb +14 -0
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
- data/lib/grape/validations/validators/base.rb +84 -68
- data/lib/grape/validations/validators/coerce_validator.rb +75 -0
- data/lib/grape/validations/validators/default_validator.rb +51 -0
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
- data/lib/grape/validations/validators/except_values_validator.rb +24 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +27 -16
- data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
- data/lib/grape/validations/validators/presence_validator.rb +15 -0
- data/lib/grape/validations/validators/regexp_validator.rb +16 -0
- data/lib/grape/validations/validators/same_as_validator.rb +29 -0
- data/lib/grape/validations/validators/values_validator.rb +88 -0
- data/lib/grape/validations.rb +18 -6
- data/lib/grape/version.rb +3 -1
- data/lib/grape.rb +175 -94
- data/spec/grape/api/custom_validations_spec.rb +117 -44
- data/spec/grape/api/deeply_included_options_spec.rb +4 -4
- data/spec/grape/api/defines_boolean_in_params_spec.rb +38 -0
- data/spec/grape/api/documentation_spec.rb +59 -0
- data/spec/grape/api/inherited_helpers_spec.rb +1 -1
- data/spec/grape/api/instance_spec.rb +103 -0
- data/spec/grape/api/invalid_format_spec.rb +3 -1
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +1 -1
- data/spec/grape/api/nested_helpers_spec.rb +1 -1
- data/spec/grape/api/optional_parameters_in_route_spec.rb +1 -1
- data/spec/grape/api/parameters_modification_spec.rb +2 -2
- data/spec/grape/api/patch_method_helpers_spec.rb +1 -1
- data/spec/grape/api/recognize_path_spec.rb +2 -2
- data/spec/grape/api/required_parameters_in_route_spec.rb +1 -1
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +1 -1
- data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +10 -16
- data/spec/grape/api/shared_helpers_spec.rb +1 -1
- data/spec/grape/api_remount_spec.rb +473 -0
- data/spec/grape/api_spec.rb +995 -231
- data/spec/grape/config_spec.rb +17 -0
- data/spec/grape/dsl/callbacks_spec.rb +3 -2
- data/spec/grape/dsl/desc_spec.rb +43 -17
- data/spec/grape/dsl/headers_spec.rb +40 -10
- data/spec/grape/dsl/helpers_spec.rb +6 -5
- data/spec/grape/dsl/inside_route_spec.rb +189 -38
- data/spec/grape/dsl/logger_spec.rb +17 -19
- data/spec/grape/dsl/middleware_spec.rb +11 -2
- data/spec/grape/dsl/parameters_spec.rb +3 -1
- data/spec/grape/dsl/request_response_spec.rb +8 -7
- data/spec/grape/dsl/routing_spec.rb +22 -9
- data/spec/grape/dsl/settings_spec.rb +1 -1
- data/spec/grape/dsl/validations_spec.rb +1 -16
- data/spec/grape/endpoint/declared_spec.rb +846 -0
- data/spec/grape/endpoint_spec.rb +136 -577
- data/spec/grape/entity_spec.rb +31 -24
- data/spec/grape/exceptions/base_spec.rb +81 -0
- data/spec/grape/exceptions/body_parse_errors_spec.rb +4 -1
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +65 -23
- data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
- data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -2
- data/spec/grape/exceptions/missing_group_type_spec.rb +21 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +1 -1
- data/spec/grape/exceptions/missing_option_spec.rb +2 -2
- data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
- data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +23 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +21 -15
- data/spec/grape/exceptions/validation_spec.rb +6 -4
- data/spec/grape/extensions/param_builders/hash_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +9 -9
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +9 -9
- data/spec/grape/integration/global_namespace_function_spec.rb +2 -2
- data/spec/grape/integration/rack_sendfile_spec.rb +14 -10
- data/spec/grape/integration/rack_spec.rb +25 -8
- data/spec/grape/loading_spec.rb +9 -9
- data/spec/grape/middleware/auth/base_spec.rb +2 -1
- data/spec/grape/middleware/auth/dsl_spec.rb +19 -10
- data/spec/grape/middleware/auth/strategies_spec.rb +62 -22
- data/spec/grape/middleware/base_spec.rb +36 -17
- data/spec/grape/middleware/error_spec.rb +11 -4
- data/spec/grape/middleware/exception_spec.rb +112 -162
- data/spec/grape/middleware/formatter_spec.rb +65 -29
- data/spec/grape/middleware/globals_spec.rb +8 -5
- data/spec/grape/middleware/stack_spec.rb +25 -13
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -2
- data/spec/grape/middleware/versioner/header_spec.rb +37 -14
- data/spec/grape/middleware/versioner/param_spec.rb +8 -2
- data/spec/grape/middleware/versioner/path_spec.rb +6 -2
- data/spec/grape/middleware/versioner_spec.rb +2 -2
- data/spec/grape/named_api_spec.rb +19 -0
- data/spec/grape/parser_spec.rb +10 -6
- data/spec/grape/path_spec.rb +53 -53
- data/spec/grape/presenters/presenter_spec.rb +8 -7
- data/spec/grape/request_spec.rb +29 -3
- data/spec/grape/util/inheritable_setting_spec.rb +9 -8
- data/spec/grape/util/inheritable_values_spec.rb +5 -3
- data/spec/grape/util/reverse_stackable_values_spec.rb +5 -2
- data/spec/grape/util/stackable_values_spec.rb +10 -7
- data/spec/grape/util/strict_hash_configuration_spec.rb +2 -1
- data/spec/grape/validations/attributes_doc_spec.rb +153 -0
- data/spec/grape/validations/instance_behaivour_spec.rb +13 -14
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +40 -0
- data/spec/grape/validations/params_scope_spec.rb +568 -99
- data/spec/grape/validations/single_attribute_iterator_spec.rb +57 -0
- data/spec/grape/validations/types/array_coercer_spec.rb +33 -0
- data/spec/grape/validations/types/primitive_coercer_spec.rb +150 -0
- data/spec/grape/validations/types/set_coercer_spec.rb +32 -0
- data/spec/grape/validations/types_spec.rb +44 -45
- data/spec/grape/validations/validators/all_or_none_spec.rb +134 -32
- data/spec/grape/validations/validators/allow_blank_spec.rb +137 -141
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +169 -31
- data/spec/grape/validations/validators/coerce_spec.rb +491 -151
- data/spec/grape/validations/validators/default_spec.rb +242 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +198 -40
- data/spec/grape/validations/validators/except_values_spec.rb +6 -5
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +181 -30
- data/spec/grape/validations/validators/presence_spec.rb +45 -2
- data/spec/grape/validations/validators/regexp_spec.rb +27 -33
- data/spec/grape/validations/validators/same_as_spec.rb +57 -0
- data/spec/grape/validations/validators/values_spec.rb +227 -180
- data/spec/grape/validations_spec.rb +502 -72
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +2 -2
- data/spec/integration/multi_xml/xml_spec.rb +2 -2
- data/spec/shared/versioning_examples.rb +34 -29
- data/spec/spec_helper.rb +31 -5
- data/spec/support/basic_auth_encode_helpers.rb +3 -1
- data/spec/support/chunks.rb +14 -0
- data/spec/support/content_type_helpers.rb +2 -0
- data/spec/support/endpoint_faker.rb +2 -0
- data/spec/support/file_streamer.rb +2 -0
- data/spec/support/integer_helpers.rb +2 -0
- data/spec/support/versioned_helpers.rb +8 -8
- metadata +111 -61
- data/Appraisals +0 -32
- data/Dangerfile +0 -2
- data/Gemfile +0 -33
- data/Gemfile.lock +0 -231
- data/Guardfile +0 -10
- data/RELEASING.md +0 -111
- data/Rakefile +0 -25
- data/benchmark/simple.rb +0 -27
- data/benchmark/simple_with_type_coercer.rb +0 -22
- data/gemfiles/multi_json.gemfile +0 -35
- data/gemfiles/multi_xml.gemfile +0 -35
- data/gemfiles/rack_1.5.2.gemfile +0 -35
- data/gemfiles/rack_edge.gemfile +0 -35
- data/gemfiles/rails_3.gemfile +0 -36
- data/gemfiles/rails_4.gemfile +0 -35
- data/gemfiles/rails_5.gemfile +0 -35
- data/gemfiles/rails_edge.gemfile +0 -35
- data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
- data/lib/grape/util/content_types.rb +0 -26
- data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
- data/lib/grape/validations/validators/all_or_none.rb +0 -20
- data/lib/grape/validations/validators/allow_blank.rb +0 -16
- data/lib/grape/validations/validators/as.rb +0 -15
- data/lib/grape/validations/validators/at_least_one_of.rb +0 -20
- data/lib/grape/validations/validators/coerce.rb +0 -74
- data/lib/grape/validations/validators/default.rb +0 -48
- data/lib/grape/validations/validators/exactly_one_of.rb +0 -29
- data/lib/grape/validations/validators/except_values.rb +0 -20
- data/lib/grape/validations/validators/mutual_exclusion.rb +0 -25
- data/lib/grape/validations/validators/presence.rb +0 -10
- data/lib/grape/validations/validators/regexp.rb +0 -11
- data/lib/grape/validations/validators/values.rb +0 -71
- data/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
- data/spec/grape/dsl/configuration_spec.rb +0 -14
- data/spec/grape/validations/attributes_iterator_spec.rb +0 -4
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'attributes_doc'
|
4
|
+
|
1
5
|
module Grape
|
2
6
|
module Validations
|
3
7
|
class ParamsScope
|
@@ -6,11 +10,42 @@ module Grape
|
|
6
10
|
|
7
11
|
include Grape::DSL::Parameters
|
8
12
|
|
13
|
+
class Attr
|
14
|
+
attr_accessor :key, :scope
|
15
|
+
|
16
|
+
# Open up a new ParamsScope::Attr
|
17
|
+
# @param key [Hash, Symbol] key of attr
|
18
|
+
# @param scope [Grape::Validations::ParamsScope] scope of attr
|
19
|
+
def initialize(key, scope)
|
20
|
+
@key = key
|
21
|
+
@scope = scope
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return Array[Symbol, Hash[Symbol => Array]] declared_params with symbol instead of Attr
|
25
|
+
def self.attrs_keys(declared_params)
|
26
|
+
declared_params.map do |declared_param_attr|
|
27
|
+
attr_key(declared_param_attr)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.attr_key(declared_param_attr)
|
32
|
+
return attr_key(declared_param_attr.key) if declared_param_attr.is_a?(self)
|
33
|
+
|
34
|
+
if declared_param_attr.is_a?(Hash)
|
35
|
+
declared_param_attr.transform_values { |value| attrs_keys(value) }
|
36
|
+
else
|
37
|
+
declared_param_attr
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
9
42
|
# Open up a new ParamsScope, allowing parameter definitions per
|
10
43
|
# Grape::DSL::Params.
|
11
44
|
# @param opts [Hash] options for this scope
|
12
45
|
# @option opts :element [Symbol] the element that contains this scope; for
|
13
46
|
# this to be relevant, @parent must be set
|
47
|
+
# @option opts :element_renamed [Symbol, nil] whenever this scope should
|
48
|
+
# be renamed and to what, given +nil+ no renaming is done
|
14
49
|
# @option opts :parent [ParamsScope] the scope containing this scope
|
15
50
|
# @option opts :api [API] the API endpoint to modify
|
16
51
|
# @option opts :optional [Boolean] whether or not this scope needs to have
|
@@ -21,38 +56,59 @@ module Grape
|
|
21
56
|
# validate if this param is present in the parent scope
|
22
57
|
# @yield the instance context, open for parameter definitions
|
23
58
|
def initialize(opts, &block)
|
24
|
-
@element
|
25
|
-
@
|
26
|
-
@
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@
|
59
|
+
@element = opts[:element]
|
60
|
+
@element_renamed = opts[:element_renamed]
|
61
|
+
@parent = opts[:parent]
|
62
|
+
@api = opts[:api]
|
63
|
+
@optional = opts[:optional] || false
|
64
|
+
@type = opts[:type]
|
65
|
+
@group = opts[:group]
|
66
|
+
@dependent_on = opts[:dependent_on]
|
31
67
|
@declared_params = []
|
32
68
|
@index = nil
|
33
69
|
|
34
|
-
instance_eval(&block) if
|
70
|
+
instance_eval(&block) if block
|
35
71
|
|
36
72
|
configure_declared_params
|
37
73
|
end
|
38
74
|
|
75
|
+
def configuration
|
76
|
+
@api.configuration.respond_to?(:evaluate) ? @api.configuration.evaluate : @api.configuration
|
77
|
+
end
|
78
|
+
|
39
79
|
# @return [Boolean] whether or not this entire scope needs to be
|
40
80
|
# validated
|
41
81
|
def should_validate?(parameters)
|
42
|
-
|
43
|
-
|
82
|
+
scoped_params = params(parameters)
|
83
|
+
|
84
|
+
return false if @optional && (scoped_params.blank? || all_element_blank?(scoped_params))
|
85
|
+
return false unless meets_dependency?(scoped_params, parameters)
|
44
86
|
return true if parent.nil?
|
87
|
+
|
45
88
|
parent.should_validate?(parameters)
|
46
89
|
end
|
47
90
|
|
48
91
|
def meets_dependency?(params, request_params)
|
49
|
-
if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
|
50
|
-
return false
|
51
|
-
end
|
52
|
-
|
53
92
|
return true unless @dependent_on
|
93
|
+
|
94
|
+
return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
|
95
|
+
|
54
96
|
return params.any? { |param| meets_dependency?(param, request_params) } if params.is_a?(Array)
|
55
|
-
|
97
|
+
|
98
|
+
meets_hash_dependency?(params)
|
99
|
+
end
|
100
|
+
|
101
|
+
def attr_meets_dependency?(params)
|
102
|
+
return true unless @dependent_on
|
103
|
+
|
104
|
+
return false if @parent.present? && !@parent.attr_meets_dependency?(params)
|
105
|
+
|
106
|
+
meets_hash_dependency?(params)
|
107
|
+
end
|
108
|
+
|
109
|
+
def meets_hash_dependency?(params)
|
110
|
+
# params might be anything what looks like a hash, so it must implement a `key?` method
|
111
|
+
return false unless params.respond_to?(:key?)
|
56
112
|
|
57
113
|
@dependent_on.each do |dependency|
|
58
114
|
if dependency.is_a?(Hash)
|
@@ -71,7 +127,7 @@ module Grape
|
|
71
127
|
def full_name(name, index: nil)
|
72
128
|
if nested?
|
73
129
|
# Find our containing element's name, and append ours.
|
74
|
-
|
130
|
+
"#{@parent.full_name(@element)}#{brackets(@index || index)}#{brackets(name)}"
|
75
131
|
elsif lateral?
|
76
132
|
# Find the name of the element as if it was at the same nesting level
|
77
133
|
# as our parent. We need to forward our index upward to achieve this.
|
@@ -115,20 +171,39 @@ module Grape
|
|
115
171
|
# Adds a parameter declaration to our list of validations.
|
116
172
|
# @param attrs [Array] (see Grape::DSL::Parameters#requires)
|
117
173
|
def push_declared_params(attrs, **opts)
|
174
|
+
opts = opts.merge(declared_params_scope: self) unless opts.key?(:declared_params_scope)
|
118
175
|
if lateral?
|
119
|
-
@parent.push_declared_params(attrs, opts)
|
176
|
+
@parent.push_declared_params(attrs, **opts)
|
120
177
|
else
|
121
|
-
|
122
|
-
|
123
|
-
@api.route_setting(:aliased_params) << { attrs.first => opts[:as] }
|
124
|
-
end
|
178
|
+
push_renamed_param(full_path + [attrs.first], opts[:as]) \
|
179
|
+
if opts && opts[:as]
|
125
180
|
|
126
|
-
@declared_params.concat
|
181
|
+
@declared_params.concat(attrs.map { |attr| ::Grape::Validations::ParamsScope::Attr.new(attr, opts[:declared_params_scope]) })
|
127
182
|
end
|
128
183
|
end
|
129
184
|
|
185
|
+
# Get the full path of the parameter scope in the hierarchy.
|
186
|
+
#
|
187
|
+
# @return [Array<Symbol>] the nesting/path of the current parameter scope
|
188
|
+
def full_path
|
189
|
+
nested? ? @parent.full_path + [@element] : []
|
190
|
+
end
|
191
|
+
|
130
192
|
private
|
131
193
|
|
194
|
+
# Add a new parameter which should be renamed when using the +#declared+
|
195
|
+
# method.
|
196
|
+
#
|
197
|
+
# @param path [Array<String, Symbol>] the full path of the parameter
|
198
|
+
# (including the parameter name as last array element)
|
199
|
+
# @param new_name [String, Symbol] the new name of the parameter (the
|
200
|
+
# renamed name, with the +as: ...+ semantic)
|
201
|
+
def push_renamed_param(path, new_name)
|
202
|
+
base = @api.route_setting(:renamed_params) || {}
|
203
|
+
base[Array(path).map(&:to_s)] = new_name.to_s
|
204
|
+
@api.route_setting(:renamed_params, base)
|
205
|
+
end
|
206
|
+
|
132
207
|
def require_required_and_optional_fields(context, opts)
|
133
208
|
if context == :all
|
134
209
|
optional_fields = Array(opts[:except])
|
@@ -140,6 +215,7 @@ module Grape
|
|
140
215
|
required_fields.each do |field|
|
141
216
|
field_opts = opts[:using][field]
|
142
217
|
raise ArgumentError, "required field not exist: #{field}" unless field_opts
|
218
|
+
|
143
219
|
requires(field, field_opts)
|
144
220
|
end
|
145
221
|
optional_fields.each do |field|
|
@@ -174,18 +250,17 @@ module Grape
|
|
174
250
|
# if required params are grouped and no type or unsupported type is provided, raise an error
|
175
251
|
type = attrs[1] ? attrs[1][:type] : nil
|
176
252
|
if attrs.first && !optional
|
177
|
-
raise Grape::Exceptions::
|
178
|
-
raise Grape::Exceptions::
|
253
|
+
raise Grape::Exceptions::MissingGroupType if type.nil?
|
254
|
+
raise Grape::Exceptions::UnsupportedGroupType unless Grape::Validations::Types.group?(type)
|
179
255
|
end
|
180
256
|
|
181
|
-
opts = attrs[1] || { type: Array }
|
182
|
-
|
183
257
|
self.class.new(
|
184
|
-
api:
|
185
|
-
element:
|
186
|
-
|
258
|
+
api: @api,
|
259
|
+
element: attrs.first,
|
260
|
+
element_renamed: attrs[1][:as],
|
261
|
+
parent: self,
|
187
262
|
optional: optional,
|
188
|
-
type:
|
263
|
+
type: type || Array,
|
189
264
|
&block
|
190
265
|
)
|
191
266
|
end
|
@@ -199,11 +274,11 @@ module Grape
|
|
199
274
|
# @yield parameter scope
|
200
275
|
def new_lateral_scope(options, &block)
|
201
276
|
self.class.new(
|
202
|
-
api:
|
203
|
-
element:
|
204
|
-
parent:
|
205
|
-
options:
|
206
|
-
type:
|
277
|
+
api: @api,
|
278
|
+
element: nil,
|
279
|
+
parent: self,
|
280
|
+
options: @optional,
|
281
|
+
type: type == Array ? Array : Hash,
|
207
282
|
dependent_on: options[:dependent_on],
|
208
283
|
&block
|
209
284
|
)
|
@@ -216,37 +291,36 @@ module Grape
|
|
216
291
|
# @yield parameter scope
|
217
292
|
def new_group_scope(attrs, &block)
|
218
293
|
self.class.new(
|
219
|
-
api:
|
220
|
-
parent:
|
221
|
-
group:
|
294
|
+
api: @api,
|
295
|
+
parent: self,
|
296
|
+
group: attrs.first,
|
222
297
|
&block
|
223
298
|
)
|
224
299
|
end
|
225
300
|
|
226
301
|
# Pushes declared params to parent or settings
|
227
302
|
def configure_declared_params
|
303
|
+
push_renamed_param(full_path, @element_renamed) if @element_renamed
|
304
|
+
|
228
305
|
if nested?
|
229
306
|
@parent.push_declared_params [element => @declared_params]
|
230
307
|
else
|
231
308
|
@api.namespace_stackable(:declared_params, @declared_params)
|
232
|
-
|
233
|
-
@api.route_setting(:declared_params, []) unless @api.route_setting(:declared_params)
|
234
|
-
@api.route_setting(:declared_params, @api.namespace_stackable(:declared_params).flatten)
|
235
309
|
end
|
310
|
+
|
311
|
+
# params were stored in settings, it can be cleaned from the params scope
|
312
|
+
@declared_params = nil
|
236
313
|
end
|
237
314
|
|
238
315
|
def validates(attrs, validations)
|
239
|
-
|
316
|
+
doc = AttributesDoc.new @api, self
|
317
|
+
doc.extract_details validations
|
240
318
|
|
241
319
|
coerce_type = infer_coercion(validations)
|
242
320
|
|
243
|
-
|
244
|
-
|
245
|
-
desc = validations.delete(:desc) || validations.delete(:description)
|
246
|
-
doc_attrs[:desc] = desc if desc
|
321
|
+
doc.type = coerce_type
|
247
322
|
|
248
323
|
default = validations[:default]
|
249
|
-
doc_attrs[:default] = default if validations.key?(:default)
|
250
324
|
|
251
325
|
if (values_hash = validations[:values]).is_a? Hash
|
252
326
|
values = values_hash[:value]
|
@@ -255,7 +329,8 @@ module Grape
|
|
255
329
|
else
|
256
330
|
values = validations[:values]
|
257
331
|
end
|
258
|
-
|
332
|
+
|
333
|
+
doc.values = values
|
259
334
|
|
260
335
|
except_values = options_key?(:except_values, :value, validations) ? validations[:except_values][:value] : validations[:except_values]
|
261
336
|
|
@@ -271,29 +346,22 @@ module Grape
|
|
271
346
|
# type should be compatible with values array, if both exist
|
272
347
|
validate_value_coercion(coerce_type, values, except_values, excepts)
|
273
348
|
|
274
|
-
|
275
|
-
|
276
|
-
full_attrs = attrs.collect { |name| { name: name, full_name: full_name(name) } }
|
277
|
-
@api.document_attribute(full_attrs, doc_attrs)
|
349
|
+
doc.document attrs
|
278
350
|
|
279
|
-
|
280
|
-
opts = {}
|
281
|
-
opts[:fail_fast] = validations.delete(:fail_fast) || false
|
351
|
+
opts = derive_validator_options(validations)
|
282
352
|
|
283
353
|
# Validate for presence before any other validators
|
284
|
-
|
285
|
-
validate('presence', validations[:presence], attrs, doc_attrs, opts)
|
286
|
-
validations.delete(:presence)
|
287
|
-
validations.delete(:message) if validations.key?(:message)
|
288
|
-
end
|
354
|
+
validates_presence(validations, attrs, doc, opts)
|
289
355
|
|
290
356
|
# Before we run the rest of the validators, let's handle
|
291
357
|
# whatever coercion so that we are working with correctly
|
292
358
|
# type casted values
|
293
|
-
coerce_type validations, attrs,
|
359
|
+
coerce_type validations, attrs, doc, opts
|
294
360
|
|
295
361
|
validations.each do |type, options|
|
296
|
-
|
362
|
+
next if type == :as
|
363
|
+
|
364
|
+
validate(type, options, attrs, doc, opts)
|
297
365
|
end
|
298
366
|
end
|
299
367
|
|
@@ -311,9 +379,7 @@ module Grape
|
|
311
379
|
# @return [class-like] type to which the parameter will be coerced
|
312
380
|
# @raise [ArgumentError] if the given type options are invalid
|
313
381
|
def infer_coercion(validations)
|
314
|
-
if validations.key?(:type) && validations.key?(:types)
|
315
|
-
raise ArgumentError, ':type may not be supplied with :types'
|
316
|
-
end
|
382
|
+
raise ArgumentError, ':type may not be supplied with :types' if validations.key?(:type) && validations.key?(:types)
|
317
383
|
|
318
384
|
validations[:coerce] = (options_key?(:type, :value, validations) ? validations[:type][:value] : validations[:type]) if validations.key?(:type)
|
319
385
|
validations[:coerce_message] = (options_key?(:type, :message, validations) ? validations[:type][:message] : nil) if validations.key?(:type)
|
@@ -349,6 +415,7 @@ module Grape
|
|
349
415
|
# but not special JSON types, which
|
350
416
|
# already imply coercion method
|
351
417
|
return unless [JSON, Array[JSON]].include? validations[:coerce]
|
418
|
+
|
352
419
|
raise ArgumentError, 'coerce_with disallowed for type: JSON'
|
353
420
|
end
|
354
421
|
|
@@ -358,7 +425,7 @@ module Grape
|
|
358
425
|
# composited from more than one +requires+/+optional+
|
359
426
|
# parameter, and needs to be run before most other
|
360
427
|
# validations.
|
361
|
-
def coerce_type(validations, attrs,
|
428
|
+
def coerce_type(validations, attrs, doc, opts)
|
362
429
|
check_coerce_with(validations)
|
363
430
|
|
364
431
|
return unless validations.key?(:coerce)
|
@@ -368,7 +435,7 @@ module Grape
|
|
368
435
|
method: validations[:coerce_with],
|
369
436
|
message: validations[:coerce_message]
|
370
437
|
}
|
371
|
-
validate('coerce', coerce_options, attrs,
|
438
|
+
validate('coerce', coerce_options, attrs, doc, opts)
|
372
439
|
validations.delete(:coerce_with)
|
373
440
|
validations.delete(:coerce)
|
374
441
|
validations.delete(:coerce_message)
|
@@ -376,6 +443,7 @@ module Grape
|
|
376
443
|
|
377
444
|
def guess_coerce_type(coerce_type, *values_list)
|
378
445
|
return coerce_type unless coerce_type == Array
|
446
|
+
|
379
447
|
values_list.each do |values|
|
380
448
|
next if !values || values.is_a?(Proc)
|
381
449
|
return values.first.class if values.is_a?(Range) || !values.empty?
|
@@ -386,14 +454,11 @@ module Grape
|
|
386
454
|
def check_incompatible_option_values(default, values, except_values, excepts)
|
387
455
|
return unless default && !default.is_a?(Proc)
|
388
456
|
|
389
|
-
if values && !values.is_a?(Proc)
|
390
|
-
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) \
|
391
|
-
unless Array(default).all? { |def_val| values.include?(def_val) }
|
392
|
-
end
|
457
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values) if values && !values.is_a?(Proc) && !Array(default).all? { |def_val| values.include?(def_val) }
|
393
458
|
|
394
|
-
if except_values && !except_values.is_a?(Proc)
|
459
|
+
if except_values && !except_values.is_a?(Proc) && Array(default).any? { |def_val| except_values.include?(def_val) }
|
395
460
|
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :except, except_values) \
|
396
|
-
|
461
|
+
|
397
462
|
end
|
398
463
|
|
399
464
|
return unless excepts && !excepts.is_a?(Proc)
|
@@ -401,37 +466,34 @@ module Grape
|
|
401
466
|
unless Array(default).none? { |def_val| excepts.include?(def_val) }
|
402
467
|
end
|
403
468
|
|
404
|
-
def validate(type, options, attrs,
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
validator_class: validator_class)
|
415
|
-
@api.namespace_stackable(:validations, factory)
|
469
|
+
def validate(type, options, attrs, doc, opts)
|
470
|
+
validator_options = {
|
471
|
+
attributes: attrs,
|
472
|
+
options: options,
|
473
|
+
required: doc.required,
|
474
|
+
params_scope: self,
|
475
|
+
opts: opts,
|
476
|
+
validator_class: Validations.require_validator(type)
|
477
|
+
}
|
478
|
+
@api.namespace_stackable(:validations, validator_options)
|
416
479
|
end
|
417
480
|
|
418
481
|
def validate_value_coercion(coerce_type, *values_list)
|
419
482
|
return unless coerce_type
|
483
|
+
|
420
484
|
coerce_type = coerce_type.first if coerce_type.is_a?(Array)
|
421
485
|
values_list.each do |values|
|
422
486
|
next if !values || values.is_a?(Proc)
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
unless value_types.all? { |v| v.is_a? coerce_type }
|
428
|
-
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
|
429
|
-
end
|
487
|
+
|
488
|
+
value_types = values.is_a?(Range) ? [values.begin, values.end].compact : values
|
489
|
+
value_types = value_types.map { |type| Grape::API::Boolean.build(type) } if coerce_type == Grape::API::Boolean
|
490
|
+
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values) unless value_types.all?(coerce_type)
|
430
491
|
end
|
431
492
|
end
|
432
493
|
|
433
494
|
def extract_message_option(attrs)
|
434
495
|
return nil unless attrs.is_a?(Array)
|
496
|
+
|
435
497
|
opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
|
436
498
|
opts.key?(:message) && !opts[:message].nil? ? opts.delete(:message) : nil
|
437
499
|
end
|
@@ -440,8 +502,26 @@ module Grape
|
|
440
502
|
validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
|
441
503
|
end
|
442
504
|
|
443
|
-
def all_element_blank?(
|
444
|
-
|
505
|
+
def all_element_blank?(scoped_params)
|
506
|
+
scoped_params.respond_to?(:all?) && scoped_params.all?(&:blank?)
|
507
|
+
end
|
508
|
+
|
509
|
+
# Validators don't have access to each other and they don't need, however,
|
510
|
+
# some validators might influence others, so their options should be shared
|
511
|
+
def derive_validator_options(validations)
|
512
|
+
allow_blank = validations[:allow_blank]
|
513
|
+
|
514
|
+
{
|
515
|
+
allow_blank: allow_blank.is_a?(Hash) ? allow_blank[:value] : allow_blank,
|
516
|
+
fail_fast: validations.delete(:fail_fast) || false
|
517
|
+
}
|
518
|
+
end
|
519
|
+
|
520
|
+
def validates_presence(validations, attrs, doc, opts)
|
521
|
+
return unless validations.key?(:presence) && validations[:presence]
|
522
|
+
|
523
|
+
validate(:presence, validations.delete(:presence), attrs, doc, opts)
|
524
|
+
validations.delete(:message) if validations.key?(:message)
|
445
525
|
end
|
446
526
|
end
|
447
527
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Validations
|
5
|
+
class SingleAttributeIterator < AttributesIterator
|
6
|
+
private
|
7
|
+
|
8
|
+
def yield_attributes(val, attrs)
|
9
|
+
attrs.each do |attr_name|
|
10
|
+
yield val, attr_name, empty?(val), skip?(val)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Primitives like Integers and Booleans don't respond to +empty?+.
|
15
|
+
# It could be possible to use +blank?+ instead, but
|
16
|
+
#
|
17
|
+
# false.blank?
|
18
|
+
# => true
|
19
|
+
def empty?(val)
|
20
|
+
val.respond_to?(:empty?) ? val.empty? : val.nil?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'dry_type_coercer'
|
4
|
+
|
5
|
+
module Grape
|
6
|
+
module Validations
|
7
|
+
module Types
|
8
|
+
# Coerces elements in an array. It might be an array of strings or integers or
|
9
|
+
# an array of arrays of integers.
|
10
|
+
#
|
11
|
+
# It could've been possible to use an +of+
|
12
|
+
# method (https://dry-rb.org/gems/dry-types/1.2/array-with-member/)
|
13
|
+
# provided by dry-types. Unfortunately, it doesn't work for Grape because of
|
14
|
+
# behavior of Virtus which was used earlier, a `Grape::Validations::Types::PrimitiveCoercer`
|
15
|
+
# maintains Virtus behavior in coercing.
|
16
|
+
class ArrayCoercer < DryTypeCoercer
|
17
|
+
def initialize(type, strict = false)
|
18
|
+
super
|
19
|
+
|
20
|
+
@coercer = scope::Array
|
21
|
+
@subtype = type.first
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(_val)
|
25
|
+
collection = super
|
26
|
+
return collection if collection.is_a?(InvalidValue)
|
27
|
+
|
28
|
+
coerce_elements collection
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
attr_reader :subtype
|
34
|
+
|
35
|
+
def coerce_elements(collection)
|
36
|
+
return if collection.nil?
|
37
|
+
|
38
|
+
collection.each_with_index do |elem, index|
|
39
|
+
return InvalidValue.new if reject?(elem)
|
40
|
+
|
41
|
+
coerced_elem = elem_coercer.call(elem)
|
42
|
+
|
43
|
+
return coerced_elem if coerced_elem.is_a?(InvalidValue)
|
44
|
+
|
45
|
+
collection[index] = coerced_elem
|
46
|
+
end
|
47
|
+
|
48
|
+
collection
|
49
|
+
end
|
50
|
+
|
51
|
+
# This method maintains logic which was defined by Virtus for arrays.
|
52
|
+
# Virtus doesn't allow nil in arrays.
|
53
|
+
def reject?(val)
|
54
|
+
val.nil?
|
55
|
+
end
|
56
|
+
|
57
|
+
def elem_coercer
|
58
|
+
@elem_coercer ||= DryTypeCoercer.coercer_instance_for(subtype, strict)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|